概览

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

基于 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. Edgeboard试用 — 基于CIFAR10分类模型的移植

    前言 在上一次的测试中,我们按照官方给的流程,使用EasyDL快速实现了一个具有性别检测功能的人脸识别系统,那么今天,我们将要试一下通过Paddlepaddle从零开始,训练一个自己的多分类模型,并进 ...

  2. [笨方法学Python]ImportError"No module named bin.app"【笔记】

    运行nosetests时,出现:ImportError"No module named bin.app" 解决方法: 1.检查路径是否是bin/app.py 2.检查是否创建bin ...

  3. java多线程总结-同步容器与并发容器的对比与介绍

    1 容器集简单介绍 java.util包下面的容器集主要有两种,一种是Collection接口下面的List和Set,一种是Map, 大致结构如下: Collection List LinkedLis ...

  4. [分享] 一款极简单的 BaseEntity CRUD 方法

    前言 尝试过 ado.net.dapper.ef,以及Repository仓储,甚至自己还写过生成器工具,以便做常规CRUD操作. 它们日常操作不方便之处: 每次使用前需要声明,再操作: 很多人一个实 ...

  5. [小米OJ] 5. 找出旋转有序数列的中间值

    排序,输出 #include <bits/stdc++.h> using namespace std; int main() { string input; while (cin > ...

  6. 转载——Asp.Net MVC+EF+三层架构的完整搭建过程

    转载http://www.cnblogs.com/zzqvq/p/5816091.html Asp.Net MVC+EF+三层架构的完整搭建过程 架构图: 使用的数据库: 一张公司的员工信息表,测试数 ...

  7. 【Java中级】(五)异常处理

    1.什么是异常 异常定义:导致程序的正常流程被中断的事件,叫做异常. 2.异常处理 try catch finally throws package exception; import java.io ...

  8. .net持续集成sonarqube篇之项目管理与用户管理

    系列目录 删除项目 在学习阶段,我们可能需要经常删除已构建的项目,在sonarqube中想要删除一个项目有两个入口,都在Administration导航栏内. 在项目内部的管理界面删除 如果项目处于打 ...

  9. storm trident 的介绍与使用

    一.trident 的介绍 trident 的英文意思是三叉戟,在这里我的理解是因为之前我们通过之前的学习topology spout bolt 去处理数据是没有问题的,但trident 的对spou ...

  10. 温故而知新,重温 Java 7 的那些“新”特性

    2009 年 4 月 20 日,Java 的亲生父亲 Sun 被养父 Oracle 以 74 亿美元收购,这在当时可是一件天大的事.有不少同学都担心 Java 的前途,我当时傻不啦叽地也很担心:自己刚 ...