Our company needs a localization/translation behavior which allows incomplete (ResX) resources. If a String
- isn't available in italian
- fall back to the next roman language, like french
- fall back to our invariant (in this case: german)
The easiest approach was a custom CultureInfo.
/// <summary>
/// A <see cref="CultureInfo" /> which switches to another language instead of <see cref="CultureInfo.InvariantCulture" />.
/// </summary>
public class FallbackCultureInfo : CultureInfo
{
private static readonly List<FallbackCultureInfo> CultureInfos = new List<FallbackCultureInfo>();
private readonly CultureInfo fallback;
private CultureInfo determinedParent;
public override CultureInfo Parent
{
get
{
if (this.determinedParent != null)
{
return this.determinedParent;
}
var originalParent = base.Parent;
if (Object.Equals(originalParent, CultureInfo.InvariantCulture) && (this.fallback != null))
{
return this.determinedParent = this.fallback;
}
if (this.fallback == null)
{
return this.determinedParent = originalParent;
}
this.determinedParent = FallbackCultureInfo.Build(originalParent.Name, this.fallback.Name);
return this.determinedParent;
}
}
private FallbackCultureInfo(String name, CultureInfo fallback = null) : base(name)
{
this.fallback = fallback;
}
/// <summary>
/// Builds a <see cref="CultureInfo"/> with a custom fallback behavior, which switsches to
/// another language before it gets <see cref="CultureInfo.InvariantCulture"/>.
/// </summary>
/// <example>
/// CultureInfo.CurrentUICulture = FallbackCultureInfo.Build("it-CH", "fr-CH", "de-CH");
/// </example>
/// <remarks>
/// Due to a missing <see cref="CultureInfo" />.operator== we have to ensure a unique instance per name on our own.
/// </remarks>
/// <param name="name">Name of our culture, like "en-US"</param>
/// <param name="fallbacks">Fallback stack, like "en-GB", "fr-FR"</param>
/// <returns>The build <see cref="CultureInfo"/>.</returns>
public static FallbackCultureInfo Build(String name, params String[] fallbacks)
{
lock (FallbackCultureInfo.CultureInfos)
{
return FallbackCultureInfo.QueuedBuild(new[] { name }.Concat(fallbacks).Reverse());
}
}
private static FallbackCultureInfo QueuedBuild(IEnumerable<String> names)
{
FallbackCultureInfo result = null;
FallbackCultureInfo lastFallback = null;
foreach (var name in names)
{
result = FallbackCultureInfo.CultureInfos.FirstOrDefault(ci => String.Equals(ci.Name, name));
if (result != null)
{
lastFallback = result;
continue;
}
result = lastFallback = new FallbackCultureInfo(name, lastFallback);
FallbackCultureInfo.CultureInfos.Add(result);
}
return result;
}
}
CultureInfo.CurrentUICulture = FallbackCultureInfo.Build("it-CH", "fr-CH", "de-CH");
Works like a charm at a first glance.
But because it is located at a general .Net position, I would like to ask for other opinions about this way to solve the problem. Has anyone done a similiar approach which failed somehow - or even if you havent - do so see any issues with it?
it-CH
and have half the UI actually rendered infr-CH
with a bit ofde-CH
sprinkled in. \$\endgroup\$