概览

随着我们的应用程序越来越受欢迎,我们的下一步将要开发多语言功能。方便越来越多的国家使用我们中国的应用程序,

基于 WPF 本地化,我们很多时候使用的是系统资源文件,可是动态切换本地化,就比较麻烦了。

有没有一种方法既可以适用系统的资源文件,又能方便快捷的切换本地化呢?

实现思路

现在我们将要实现的是基于 DotNetCore 3.0 以上版本 and WPF 桌面应用程序模块化的多语言功能。

动态切换多语言思路:

  • 把所有模块的资源文件添加到字典集合。
  • 将资源文件里的key,绑定到前台。
  • 通过通知更改 CurrentCulture 多语言来使用改变的语言文件里的key。
  • 通过绑定 Binding 拼接Path 在输出。

动态切换

我们先来看实现结果

  • 第一行是我们的主程序的数据展示,用于业务中的本地化
  • 第二行是我们业务模块A的数据展示
  • 第三行是我们业务模块B的数据展示

来看一下xaml展示

通过ComboBox选择来切换语言

搭建模拟业务项目

创建一个WPF App(.NET Core)应用程序

创建完成后,我们需要引入业务A模块及业务B模块和业务帮助模块



PS:根据自己的业务需要来完成项目的搭建。本教程完全适配多语言功能。

使用ResX资源文件

在各个模块里添加Strings 文件夹用来包含 各个国家和地区的语言文件。

多语言可以参考:https://github.com/UnRunDeaD/WPF---Localization/blob/master/ComboListLanguages.txt

资源文件可以放在任意模块内,比如业务模块A ,主程序,底层业务,控件工具集等

创建各个业务模块资源文件

Strings文件夹可以任意命名

SR资源文件可以任意命名

帮助类

封装到底层供各个模块调用

    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;
// string.Empty/null indicates that all properties have changed
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
}
}
} // WPF bindings register PropertyChanged event if the object supports it and update themselves when it is raised
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); // does this control have a "Translation.ResourceManager" attached property with a set value?
if (localValue != DependencyProperty.UnsetValue)
{
if (localValue is ResourceManager resourceManager)
{
TranslationSource.Instance.AddResourceManager(resourceManager); return resourceManager;
}
}
} return null;
} public override object ProvideValue(IServiceProvider serviceProvider)
{
// targetObject is the control that is using the LocExtension
object targetObject = (serviceProvider as IProvideValueTarget)?.TargetObject; if (targetObject?.GetType().Name == "SharedDp") // is extension used in a control template?
return targetObject; // required for template re-binding string baseName = GetResourceManager(targetObject)?.BaseName ?? string.Empty; if (string.IsNullOrEmpty(baseName))
{
// rootObject is the root control of the visual tree (the top parent of targetObject)
object rootObject = (serviceProvider as IRootObjectProvider)?.RootObject;
baseName = GetResourceManager(rootObject)?.BaseName ?? string.Empty;
} if (string.IsNullOrEmpty(baseName)) // template re-binding
{
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"
// 每个模块通过帮助类,将当前模块的资源类,
// 加载到资源管理集合里面用于分配每个键值
// 引用刚才你命名的资源文件名字 -> SR
ext:Translation.ResourceManager="{x:Static resx:SR.ResourceManager}"

显示文字

//读取资源文件里的键值
<Label Content="{ext:Loc Test}" FontSize="21" />

后台实现

根据业务的需要,我们在界面上无法适用静态文字显示的,一般通过后台代码来完成,对于 code-behind 的变量使用,同样可以应用于资源字典。

比如在业余模块代码段里的模拟实现

// SR 是当前业务模块的资源文件类,管理当前模块的资源字符串。
// 根据不同的 `CurrentCulture` 选择相对应的本地化
Message = string.Format(SR.ResourceManager.GetString("Message",Thread.CurrentThread.CurrentUICulture),System.DateTime.Now);

PS: 欢迎各位大佬慷慨指点,有不足之处,请指出!有疑问,请指出,喜欢它,请支持!

下载地址

https://github.com/androllen/WpfNetCoreLocalization

相关链接

https://github.com/Jinjinov/wpf-localization-multiple-resource-resx-one-language/blob/master/README.md

https://codinginfinity.me/post/2015-05-10/localization_of_a_wpf_app_the_simple_approach

DotNetCore 3.0 助力 WPF本地化的更多相关文章

  1. DotNetCore 3.0 助力 WPF 开发

    DotNetCore Is AnyWhere. 前言 Visual Studio 2019 已经正式发布了,DotNetCore 3.0 的正式版也指日可待.在之前的版本中,作为一名基于微软生态的传统 ...

  2. 将传统 WPF 程序迁移到 DotNetCore 3.0

    介绍 由于历史原因,基于 Windows 平台存在着大量的基于 .NetFramework 开发的 WPF 和 WinForm 相关程序,如果将这些程序全部基于 DotNetCore 3.0 重写一遍 ...

  3. 使用 MSIX 打包 DotNetCore 3.0 客户端程序

    如何你希望你的 WPF 程序能够以 Windows 的保护机制保护起来,不被轻易反编译的话,那么这篇文章应该能帮到你. 介绍 MSIX 是微软于去年的 Windows 开发者日峰会 上推出的全新应用打 ...

  4. MSIX 打包 DotNetCore 3.0

    使用 MSIX 打包 DotNetCore 3.0 客户端程序 如何你希望你的 WPF 程序能够以 Windows 的保护机制保护起来,不被轻易反编译的话,那么这篇文章应该能帮到你. 介绍# MSIX ...

  5. [WPF]本地化入门

    1. 前言 WPF的本地化是个很常见的功能,我做过的WPF程序大部分都实现了本地化(不管最终有没有用到).通常本地化有以下几点需求: 在程序启动时根据CultureInfo.CurrentUICult ...

  6. 创建或打开解决方案时提示"DotNetCore.1.0.1-SDK.1.0.0.Preview2-003131-x86"错误的解决方案

    提示"DotNetCore.1.0.1-SDK.1.0.0.Preview2-003131-x86"错误的解决方案: 1.检查是否有C:\Program Files (x86)\d ...

  7. 解决 DotNetCore.1.0.1-VS2015Tools.Preview2.0.3.exe 在VS2015 Update3 安装失败的问题

    今天抽空升级VS2015 Update3. 在安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.3.exe 时报错了,看了错误日志 显示: 看到我标红的两个地方,那么 ...

  8. 无法安装 DotNetCore.1.0.0-VS2015Tools.Preview2解决方法

    安装 DotNetCore.1.0.0-VS2015Tools.Preview2,已经安装vs2015update3,还是提示检测到 Visual Studio 2015 Update 3没有完全安装 ...

  9. [转] 安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.2出现0x80072f8a未指定的错误

    原文地址:安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.2出现0x80072f8a未指定的错误 最近DotNetCore更新到了1.0.1,Azure tools ...

随机推荐

  1. CAD2014学习笔记-图纸布局和打印输出

    基于 虎课网huke88.com CAD教程 图纸设计规范:施工图 封面设计:地点.名称.设计人 目录设计:施工图编号.名称.意义.对应页数.注释.图号序号:包括平面.立面.大样图.施工图 设计说明/ ...

  2. C# ExcelAddIn 开发笔记

    好久都没有写博客了,最近真的是太忙了,接手公司要做的一个小的新的项目,从接触认识到一个新东西,再到自己琢磨研究,最终结合公司业务把整个excel插件项目完成,还是有一点点成就感.以下是项目中基本上大致 ...

  3. Excel催化剂开源第28波-调用Google规划求解库

    在Excel催化剂的自定义函数中,有规划求解的函数,用于在一些凑数的场景,某财务工作网友向我提出的需求,例如用于凑发票额使用. 一般开发票的场景是多次采购合在一起开具,即多个订单产生后开,同时发票一般 ...

  4. 手把手教你破解文件密码、wifi密码、网页密码

    手把手教你破解文件密码.wifi密码.网页密码 1.破解文件密码: 有时候我们在网上下载一个压缩包后,必须要关注或者支付一定费用才给你解压密码,实属比较恶心.在这里手把手叫你实现破解文件解压密码. 1 ...

  5. 基于Bitnami gitlab OVA包的gitlab 环境搭建

    前言 最近在折腾gitlab,本篇记录搭建的过程方便以后查找 环境 Windows server + VMware 安装 为方便本次我们直接采用Bitnami的VOA安装包(VOA格式可同时兼容Vir ...

  6. join,列表和字典用for循环的删除,集合,深浅拷贝

    1.join() 将列表转换成字符串,并且每个字符之间用另一个字符连接起来,join后面必须是可迭代的对象(字符串,列表,元组,字典,集合),数字不能迭代 例如: s = ['a','b','c'] ...

  7. 手写C语言字符库

    鉴于以前碰到过很多这样的题目,甚至上次月考核也考了,马上就要考试了,就再重新写一遍,加深印象,但是肯定和库函数有区别,丢失许多细节 1.strlen函数(求字符串长度) int strlen(char ...

  8. 剖析std::function接口与实现

    目录 前言 一.std::function的原理与接口 1.1 std::function是函数包装器 1.2 C++注重运行时效率 1.3 用函数指针实现多态 1.4 std::function的接 ...

  9. 如何实现Excel多人共享与协作

    1.写在前面的话 本人从事信息化工作多年,对Excel等电子表格的多人共享与协作接触较早,帮助客户实施的方案也较多,因此有些体会和认识.正好看到网上这方面的讨论较多,但都不完整,我就进一步做了专题调研 ...

  10. TestNG独立运行的几种方法.md

    目录 通过main函数调用testng.xml文件,然后打成可执行jar包执行 1 假设我们已经写好了testng.xml,现在我们需要写一个主类和main函数用来调用testng.xml 2 把项目 ...