如何用最简单的方法让WPF程序支持多国语言,并且语言文字用户可以随意编辑?
支持多语言的核心无非是,切换Applicaion级别的ResourceDictionary
语言文件随意编辑,无非是在程序启动时,加载一下某个资源文件而已。
为了支持界面使用多语言,我们自己定义了一个Resource扩展标记。
同时,为了防止,用户误将语言资源删除,而引起界面无任何文字的情况,我们内部定义了一个默认的资源文件,这样就不会没有显示了。
资源文件的目录结构:
Languages目录下有Default.xaml, en-US.xaml, zh-CN.xaml等
先上核心读取类LanguageReader:
#region Header /** * <pre> * * Work Force Management * File: LanguageReader.cs * * Grandsys, Inc. * Copyright (C): 2009 * * Description: * Init LanguageReader * * Note * Created By: Prime Li at 7/15/2009 2:10:04 PM * * </pre> */ #endregion Header namespace Luna.Globalization { using System; using System.Globalization; using System.IO; using System.Windows; using System.Windows.Markup; /// <summary> /// Read the Language /// </summary> public static class LanguageReader { #region Fields private const string LANGUAGE_PATH = @".\Languages\"; private const string RESOURCE_FILE = @".\Resources\ApplicationResource.xaml"; private static ResourceDictionary _currentLanguage; private static ResourceDictionary _defaultLanguage; #endregion Fields static LanguageReader() { Uri uri = new Uri("Luna.Globalization;component/Languages/default.xaml", UriKind.RelativeOrAbsolute); _defaultLanguage = Application.LoadComponent(uri) as ResourceDictionary; _currentLanguage = _defaultLanguage; } #region Methods /// <summary> /// Loads the specified culture. /// </summary> /// <param name="culture">The culture.</param> public static void Load(string culture) { bool isExistLanguage = CheckLanguageName(culture); if (isExistLanguage) { string path = LANGUAGE_PATH + culture + ".xaml"; using (FileStream fs = File.OpenRead(path)) { _currentLanguage = XamlReader.Load(fs) as ResourceDictionary; if (Application.Current.Resources.MergedDictionaries.Contains(_currentLanguage)) { Application.Current.Resources.MergedDictionaries.Remove(_currentLanguage); } Application.Current.Resources.MergedDictionaries.Add(_currentLanguage); } Application.Current.Dispatcher.Thread.CurrentCulture = CultureInfo.GetCultureInfo(culture); Application.Current.Dispatcher.Thread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); } else { //Use default Language Application.Current.Dispatcher.Thread.CurrentCulture = CultureInfo.GetCultureInfo("zh-CN"); Application.Current.Dispatcher.Thread.CurrentUICulture = CultureInfo.GetCultureInfo("zh-CN"); } } public static void LoadResource(string fileName) { if (File.Exists(fileName)) { using (FileStream fs = File.OpenRead(fileName)) { ResourceDictionary dic = XamlReader.Load(fs) as ResourceDictionary; Application.Current.Resources.MergedDictionaries.Add(dic); } } } public static void LoadDefaultResource() { LoadResource(RESOURCE_FILE); } /// <summary> /// Gets the value. /// </summary> /// <param name="key">The key.</param> /// <returns></returns> public static string GetValue(string key) { object value = GetValue((object)key); if (value == null) return string.Empty; return value.ToString(); } /// <summary> /// Gets the value. /// </summary> /// <param name="key">The key.</param> /// <returns></returns> public static object GetValue(object key) { object value = _currentLanguage[key]; if (value == null) value = _defaultLanguage[key]; return value; } /// <summary> /// Gets the value. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key">The key.</param> /// <returns></returns> public static T GetValue<T>(object key) { object value = GetValue(key); if (value == null) return default(T); return (T)value; } public static bool ContainsKey(string key) { return ContainsKey((object)key); } public static bool ContainsKey(object key) { return _currentLanguage.Contains(key) || _defaultLanguage.Contains(key); } /// <summary> /// Checks the name of the language. /// </summary> /// <param name="languageName">Name of the language.</param> /// <returns></returns> private static bool CheckLanguageName(string languageName) { var allLanguageNames = Directory.GetFiles(LANGUAGE_PATH); foreach (var name in allLanguageNames) { if (name.Contains(languageName)) { return true; } } return false; } #endregion Methods } }
然后就是自己的Resource扩展标记
using System; using System.Windows; using System.Windows.Markup; namespace Luna.Globalization { [MarkupExtensionReturnType(typeof(object)), Localizability(LocalizationCategory.NeverLocalize)] public class ResourceExtension : MarkupExtension { public ResourceExtension() { } public ResourceExtension(object key) { Key = key; } public override object ProvideValue(IServiceProvider serviceProvider) { return LanguageReader.GetValue(Key); } [ConstructorArgument("Key")] public object Key { get; set; } } }
如何使用?
在App.config里面配置:
<add key="Language" value="zh-TW" />
在App.xaml.cs的启动时加载:
LanguageReader.Load(ConfigurationManager.AppSettings["Language"]);
在XAML文件里面使用:
<TextBlock Text="{inf:Resource OK}"/>