概览
随着我们的应用程序越来越受欢迎,我们的下一步将要开发多语言功能。方便越来越多的国家使用我们中国的应用程序,基于 WPF 本地化,我们很多时候使用的是系统资源文件,可是动态切换本地化,就比较麻烦了。
实现思路
现在我们将要实现的是基于 DotNetCore 3.0
以上版本 and WPF
桌面应用程序模块化的多语言功能。动态切换多语言思路:
把所有模块的资源文件添加到字典集合。
将资源文件里的key,绑定到前台。
通过通知更改
CurrentCulture
多语言来使用改变的语言文件里的key。通过绑定
Binding
拼接Path 在输出。
动态切换
我们先来看实现结果
第一行是我们的主程序的数据展示,用于业务中的本地化
第二行是我们业务模块A的数据展示
第三行是我们业务模块B的数据展示
来看一下xaml展示
通过ComboBox选择来切换语言
搭建模拟业务项目
创建一个WPF App(.NET Core)应用程序
创建完成后,我们需要引入业务A模块及业务B模块和业务帮助模块
使用ResX资源文件
在各个模块里添加Strings
文件夹用来包含 各个国家和地区的语言文件。
多语言可以参考:https://github.com/UnRunDeaD/WPF---Localization/blob/master/ComboListLanguages.txt
资源文件可以放在任意模块内,比如业务模块A ,主程序,底层业务,控件工具集等
创建各个业务模块资源文件
Strings文件夹可以任意命名
帮助类
封装到底层供各个模块调用
public class TranslationSource : INotifyPropertyChanged{public static TranslationSource Instance { get; } = new TranslationSource();private readonly Dictionary<string, ResourceManager> resourceManagerDictionary = new Dictionary<string, ResourceManager>();public string this[string key]{get{Tuple<string, string> tuple = SplitName(key);string translation = null;if (resourceManagerDictionary.ContainsKey(tuple.Item1))translation = resourceManagerDictionary[tuple.Item1].GetString(tuple.Item2, currentCulture);return translation ?? key;}}private CultureInfo currentCulture = CultureInfo.InstalledUICulture;public CultureInfo CurrentCulture{get { return currentCulture; }set{if (currentCulture != value){currentCulture = value;PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));}}}public event PropertyChangedEventHandler PropertyChanged;public void AddResourceManager(ResourceManager resourceManager){if (!resourceManagerDictionary.ContainsKey(resourceManager.BaseName)){resourceManagerDictionary.Add(resourceManager.BaseName, resourceManager);}}public static Tuple<string, string> SplitName(string local){int idx = local.ToString().LastIndexOf(".");var tuple = new Tuple<string, string>(local.Substring(0, idx), local.Substring(idx + 1));return tuple;}}public class Translation : DependencyObject{public static readonly DependencyProperty ResourceManagerProperty =DependencyProperty.RegisterAttached("ResourceManager", typeof(ResourceManager), typeof(Translation));public static ResourceManager GetResourceManager(DependencyObject dependencyObject){return (ResourceManager)dependencyObject.GetValue(ResourceManagerProperty);}public static void SetResourceManager(DependencyObject dependencyObject, ResourceManager value){dependencyObject.SetValue(ResourceManagerProperty, value);}}public class LocExtension : MarkupExtension{public string StringName { get; }public LocExtension(string stringName){StringName = stringName;}private ResourceManager GetResourceManager(object control){if (control is DependencyObject dependencyObject){object localValue = dependencyObject.ReadLocalValue(Translation.ResourceManagerProperty);if (localValue != DependencyProperty.UnsetValue){if (localValue is ResourceManager resourceManager){TranslationSource.Instance.AddResourceManager(resourceManager);return resourceManager;}}}return null;}public override object ProvideValue(IServiceProvider serviceProvider){object targetObject = (serviceProvider as IProvideValueTarget)?.TargetObject;if (targetObject?.GetType().Name == "SharedDp")return targetObject;string baseName = GetResourceManager(targetObject)?.BaseName ?? string.Empty;if (string.IsNullOrEmpty(baseName)){object rootObject = (serviceProvider as IRootObjectProvider)?.RootObject;baseName = GetResourceManager(rootObject)?.BaseName ?? string.Empty;}if (string.IsNullOrEmpty(baseName)){if (targetObject is FrameworkElement frameworkElement){baseName = GetResourceManager(frameworkElement.TemplatedParent)?.BaseName ?? string.Empty;}}Binding binding = new Binding{Mode = BindingMode.OneWay,Path = new PropertyPath($"[{baseName}.{StringName}]"),Source = TranslationSource.Instance,FallbackValue = StringName};return binding.ProvideValue(serviceProvider);}}
前台绑定
xmlns:ext="clr-namespace:WpfUtil.Extension;assembly=WpfUtil"xmlns:resx="clr-namespace:ModuleA.Strings"ext:Translation.ResourceManager="{x:Static resx:SR.ResourceManager}"
显示文字
<Label Content="{ext:Loc Test}" FontSize="21" />
后台实现
根据业务的需要,我们在界面上无法适用静态文字显示的,一般通过后台代码来完成,对于 code-behind 的变量使用,同样可以应用于资源字典。
PS: 欢迎各位大佬慷慨指点,有不足之处,请指出!有疑问,请指出,喜欢它,请支持!
下载地址
https://github.com/androllen/WpfNetCoreLocalization
相关链接
https://github.com/Jinjinov/wpf-localization-multiple-resource-resx-one-language/blob/master/README.md