应用程序具备多语言支持,是实现全球化(Globalization)和本地化(Localization)最重要的功能。

关于全球化和本地化的概念、设计原则、基本流程可参考微软文档 https://docs.microsoft.com/zh-cn/dotnet/framework/wpf/advanced/wpf-globalization-and-localization-overview

目前有多种方案可供选择:

  • 使用resx资源文件
  • 使用xaml资源文件
  • 使用markup扩展

本文介绍学习使用resx文件的一些经验和心得。

resx资源文件通常是嵌入应用的数据,随应用程序一起发布,如果修改资源文件中的数据,需要重新编译。

.NET对多种语言使用“中心–卫星”模式管理多个区域的资源,处于“中心”位置的称为默认资源,处于“卫星”位置的称为特定区域资源。

例如,项目中有个资源文件Resources.resx,这个就是默认资源文件,Resources.en-US.resx、Resources.fr-FR.resx、Resources.zh-CN.resx等就是特定区域的资源文件。

在VS中编译生成应用程序时,编译器会在可执行程序的目录中生成对应特定区域的子目录,子目录中存放区域对应资源的dll文件。

.NET使用ResourcesManager对象管理这些资源文件, ResourceManager从Thread.CurrentUICulture属性中获得当前的区域,然后根据该属性从特定区域中进行查找,如果找到了对应的区域,则使用该区域的资源内容,如果未在特定区域中找到内容,则使用默认的资源。

下面举个简单的例子说明。

代码与数据

新建一个WPF(.NET Framework)桌面应用TestApp,

创建的项目中,VS已经预先建好了一些文件,打开Properties目录下的Resources.resx文件,在其中添加一些字符串资源,并且将访问修饰符改为Public。

然后在Properties目录下新建一个文件名为Resources.en-US.resx的资源文件,在其中添加字符串资源。

修改Properties目录下的Settings.settings文件,增加当前App语言的设置。

编辑MainWindow.xaml前端文件。

首先添加名称空间,并在Grid中添加两个按钮控件。

xmlns:prop="clr-namespace:TestApp.Properties"
<Button Content="{x:Static prop:Resources.btnChnText}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_chn_Click"/>
<Button Content="{x:Static prop:Resources.btnEngText}" HorizontalAlignment="Left" Margin="100,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_eng_Click"/>

编辑MainWindow.xaml.cs后台文件

首先添加引用

using System.Globalization;

然后在MainWindow中添加两个内部成员变量

string _currentCulture = Properties.Settings.Default.Culture;
ResourceManager _rm = new ResourceManager("TestApp.Properties.Resources",
    typeof(MainWindow).Assembly);

修改MainWindow的构造函数,在InitializeComponent之前修改当前的Culture。

Thread.CurrentThread.CurrentUICulture
    = CultureInfo.CreateSpecificCulture(_currentCulture); 

增加两个按钮的响应函数

private void Button_chn_Click(object sender, RoutedEventArgs e)
    if (MessageBox.Show(_rm.GetString("strChnQuestion"), "", 
        MessageBoxButton.YesNo, 
        MessageBoxImage.Question)
        == MessageBoxResult.Yes)
        Properties.Settings.Default.Culture = "zh-CN";
        Properties.Settings.Default.Save();
private void Button_eng_Click(object sender, RoutedEventArgs e)
    if (MessageBox.Show(_rm.GetString("strEngQuestion"), "",
        MessageBoxButton.YesNo,
        MessageBoxImage.Question)
        == MessageBoxResult.Yes)
        Properties.Settings.Default.Culture = "en-US";
        Properties.Settings.Default.Save();

程序运行后缺省显示中文,点击按钮显示的也是中文的提示。

选择某个语言后,重新启动程序,所有的界面和提示都切换成了目标语言。

这种方法的缺陷在于界面的文字采用静态渲染,运行过程中不会因为语言的切换而改变现实。

如果不考虑程序的执行效率,可以利用Markup的扩展,将界面语言的切换改为动态方式,这样可以在运行中进行语言的切换,而不必重新启动程序。

使用一种语言的多个Resource.resx文件(资源管理器)进行WPF本地化。 如果只想对文本进行本地化,而发现太大而不能满足您的需要,则可以将其用作“一个文件”替代方案,它可以对字符串(而不是图像)进行本地化。 它支持文化的动态变化,这意味着:它允许在运行时更改语言,而无需重新启动应用程序。 用于本地化多个棱镜模块: ModuleA\Properties\Resources.resx ModuleA\Properties\Resources.de.resx ModuleA\Properties\Resources.es.resx ModuleB\Properties\Resourc
资源在编译期间添加到程序集。如果要将资源嵌入到程序集,则必须将文件添加到项目中,文件会自动拷贝到项目文件夹的Resources文件夹中。如果要嵌入到程序集,还需选中文件,修改其属性“生成操作”(Build Action)为“嵌入的资源”,默认为“内容”。 一旦设置为嵌入的资源,则它就会成为资源清单中程序集的一部分。每一程序集,无论是静态的还是动态的,均包含描述该程序集中各元素彼此如何关联的数据...
xmlns:res=“clr-namespace:双语言测试.Language” 然后,调用资源: Content="{x:Static res:StringResource.String1}" 3 设置资源的区域性 双语言测试.Langu...
WPF中的二进制资源,就是类似于MFC中在对话框程序中添加的图片、字符串等资源,程序在运行时将其转换成二进制,以供程序使用。 下面以将字符串转换成二进制为例来说明,二进制资源的使用方法: 首先在项目的Properties->Resources.resx中添加如下的字符串: 然后在Xmal文件中,添加如下代码: <Window x:Class="_9_5.MainWindow"
WPF 中使用多线程有几种方法: 1. 使用 `BackgroundWorker` 类。这是 WPF 提供的一个内置类,可以在后台线程中执行长时间运行的任务,并提供了方便的事件来监视任务的进度和状态。 2. 使用 `Task` 类。这是 .NET Framework 提供的一个类,可以在后台线程中执行长时间运行的任务,并提供了很多有用的方法来管理和同步多个任务。 3. 使用 `Dispatcher` 类。这是 WPF 提供的一个类,可以用来在不同的线程之间执行操作。例如,如果你有一个后台线程,并且想要在 UI 线程上更新某个 UI 元素,就可以使用 `Dispatcher` 来实现。 示例代码: private void Button_Click(object sender, RoutedEventArgs e) BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += Worker_DoWork; worker.RunWorkerCompleted += Worker_RunWorkerCompleted; worker.RunWorkerAsync(); private void Worker_DoWork(object sender, DoWorkEventArgs e) // 在后台线程上执行长时间运行的任务 private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) // 在 UI 线程上更新 UI 元素