DotNetCore 3.0 助力 WPF本地化
概览
随着我们的应用程序越来越受欢迎,我们的下一步将要开发多语言功能。方便越来越多的国家使用我们中国的应用程序,基于 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
DotNetCore 3.0 助力 WPF本地化相关推荐
- DotNetCore 3.0 助力 WPF 开发
前言 Visual Studio 2019 已经正式发布了,DotNetCore 3.0 的正式版也指日可待.在之前的版本中,作为一名基于微软生态的传统 WPF 程序员看着隔壁同学在开发 DotNet ...
- 将传统 WPF 程序迁移到 DotNetCore 3.0
介绍 由于历史原因,基于 Windows 平台存在着大量的基于 .NetFramework 开发的 WPF 和 WinForm 相关程序,如果将这些程序全部基于 DotNetCore 3.0 重写一遍 ...
- 使用 MSIX 打包 DotNetCore 3.0 客户端程序
如何你希望你的 WPF 程序能够以 Windows 的保护机制保护起来,不被轻易反编译的话,那么这篇文章应该能帮到你. 介绍 MSIX 是微软于去年的 Windows 开发者日峰会 上推出的全新应用打 ...
- 创建或打开解决方案时提示“DotNetCore.1.0.1-SDK.1.0.0.Preview2-003131-x86“错误的解决方案
创建或打开解决方案时提示"DotNetCore.1.0.1-SDK.1.0.0.Preview2-003131-x86"错误的解决方案 参考文章: (1)创建或打开解决方案时提示& ...
- 关于asp.net2.0资源文件本地化多语言版本的一些小技巧
花了几个小时,把我的每天记帐的程序改成多语言版本的了~~~ Asp.net 2.0提供了本地化多语言版本的一些方便的特性,网上关于这个的文章已经很多了,我在此就不用多说拉,我只是说说,我在做的时候碰到 ...
- 安装DotNetCore.1.0.0-VS2015Tools.Preview2.exe 错误Error 0x81f40001 解决方法
安装DotNetCore.1.0.0-VS2015Tools.Preview2.exe 错误Error 0x81f40001 解决方法 参考文章: (1)安装DotNetCore.1.0.0-VS20 ...
- Scratch3.0——助力新进程序员理解程序(案例一、画画的蝴蝶)
Scratch3.0--助力新进程序员理解程序(案例一.画画的蝴蝶) 前言 一般来说,针对6-18岁的少年儿童开展的编程教育,现在,最常见的形式是线上和线下模式相结合的课外培训. 这里我用作让大朋友们 ...
- Scratch3.0——助力新进程序员理解程序(案例八、等差数列计算1)
Scratch3.0--助力新进程序员理解程序(案例八.等差数列计算1) 前言 一般来说,针对6-18岁的少年儿童开展的编程教育,现在,最常见的形式是线上和线下模式相结合的课外培训. 这里我用作让大朋 ...
- Scratch3.0——助力新进程序员理解程序(案例九、等差数列2)
Scratch3.0--助力新进程序员理解程序(案例九.等差数列2) 前言 一般来说,针对6-18岁的少年儿童开展的编程教育,现在,最常见的形式是线上和线下模式相结合的课外培训. 这里我用作让大朋友们 ...
最新文章
- Handle In-Day Changes
- Python常用模块之sys模块
- mysql 常用权限_MySQL的权限有哪些
- C++pair对组的创建
- A. And Then There Were K
- 多路复用IO: select、sys_select、do_select源码分析
- 机器学习笔记【一】- 线性回归(末):统计学推导以及局部加权线性回归算法实例
- 编写带界面的ActiveX控件(CAB网页控件)全过程
- NASM汇编教程翻译01 第一讲 Hello, World!
- DBVisualizer导入excel数据
- ​以数据科学家的眼光投资,你可能会一夜暴富
- 防DNS劫持教程,手动修复本地DNS教程
- 文科生的悲哀-斐波拉契数列
- (新)B站视频播放源地址获取及B站视频下载
- ValueError: cannot reshape array of size xxx into shape (xxx,xxx,xxx)解决方法
- 康耐视VisionPro
- HTTP请求以及接收的方式
- 爬虫需谨慎!那些你不知道的爬虫反爬虫套路,学起来!
- Spark性能调优案例-优化spark估计表大小失败 和 小表关联 走 broadcast join
- 如何构建一个成功的期权交易系统?