Language Conversion with WPF
In WPF, there is no way to load Label, button and other
controls’ content based on your UI culture or allow you to override UI contents based on your selected language. Using
custom MarkupExtension can achieve the goal.
Example of use of MarkupExtension with Label
<Label Grid.Column="0" Grid.Row="1" Content="{Extensions:LanguageString Value=Teststring}" />
Implementation of MarkupExtension
Note : This is a xml file based example, file name “language-” + culture
name.Xml e.g. “language-en-gb.xml”
[MarkupExtensionReturnType(typeof(string))]
public class LanguageStringExtension : MarkupExtension
{
#region Properties
/// <summary>
/// Gets or sets the string that is to be translated.
/// </summary>
public string Value { get; set; }
/// <summary>
/// Gets or sets the format string to be used with the translated string.
/// e.g. "({0})" to place the translated string in brackets.
/// </summary>
public string Format { get; set; }
#endregion
#region Overrides of MarkupExtension
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (string.IsNullOrEmpty(Format))
{
return LanguageDictionary.Translate(Value);
}
return LanguageDictionary.Translate(Value, Format);
}
#endregion
}
public sealed class LanguageDictionary
{
//for thread safe access
private readonly ConcurrentDictionary<string,string> _phrases;
static LanguageDictionary()
{
FilesDirectory = Directory.GetCurrentDirectory();
Instance = new LanguageDictionary();
//load current culture file
Instance.LoadDictionary(CultureInfo.CurrentUICulture.Name);
}
//private constructor
LanguageDictionary()
{
_phrases = new ConcurrentDictionary<string, string>();
}
public static LanguageDictionary Instance { get; private set; }
public static string FilesDirectory { get; set; }
internal static string Translate(string phrase)
{
return Instance.TranslatePhrase(phrase, null);
}
internal static string Translate(string phrase, string formatString, params object[] prms)
{
return Instance.TranslatePhrase(phrase, formatString, prms);
}
internal static void Load(string culture)
{
Instance.LoadDictionary(culture);
}
internal static void Save()
{
Instance.SaveDictionary();
}
private static string _overrideCulture;
internal static string OverrideCulture
{
get { return _overrideCulture; }
set
{
Instance.LoadDictionary(value);
}
}
private void SaveDictionary()
{
var fileName = GetLanguageFileName();
var xmlDocument = new XmlDocument();
foreach (var kpPhrase in _phrases)
{
var node = xmlDocument.CreateNode(XmlNodeType.Element, "Translate", string.Empty);
node.InnerText = kpPhrase.Value;
var nameAttr = xmlDocument.CreateAttribute("From");
nameAttr.Value = kpPhrase.Key;
node.Attributes.Append(nameAttr);
xmlDocument.AppendChild(node);
}
xmlDocument.Save(fileName);
}
private string GetLanguageFileName()
{
return Path.Combine(FilesDirectory,
string.Format("language-{0}.xml", _overrideCulture));
}
private void LoadDictionary(string culture)
{
_overrideCulture = culture;
//clear old values
_phrases.Clear();
var fileName = GetLanguageFileName();
if (File.Exists(fileName))
{
var xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
foreach (XmlNode childNode in xmlDocument.ChildNodes)
{
_phrases.TryAdd(childNode.Attributes["From"].Value, childNode.InnerText);
}
}
}
private string TranslatePhrase(string phrase, string formatString, params object[] prms)
{
//get value or add for save
var lanValue = _phrases.GetOrAdd(phrase, phrase);
if (prms == null || prms.Length == 0)
{
return lanValue;
}
return string.Format(lanValue, prms);
}
}