促使程序 赢得更多客户的最好、最经济的方法是使之支持多国语言,而不是 将潜在的客户群限制为全球近70亿人口中的一小部分。本文介绍四种实现WPF应用程序支持多国语言的解决方案。

效果如下图:

Language - en-US (英文)

Language - zh-CN (中文)

一、使用LocBaml工具

二、使用资源字典文件

三、使用.resx资源文件

四、实现动态切换程序显示语言

附:实现MessageBox支持多语言

一、使用LocBaml工具

这是微软MSDN给出的一种解决方案,请参见 如何:对应用程序进行本地化 。这种方式操作相对繁琐,不能方便的实现在程序运行过程中动态切换语言,但是新增支持语言无需对项目重新编译,这是这种实现方式的一个亮点。

1. 设置默认语言环境

项目文件LocalizationDemo.csproj添加 <UICulture>en-US</UICulture> ,程序集文件AssemblyInfo.cs中把[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]注释去掉。

2. 在xaml文件中添加Uid

打开Visual Studio 命令提示(2010)如下图,进入项目文件.csproj所在目录,运行命令: msbuild /t:updateuid LocalizationDemo.csproj 若要验证是否缺少或重复Uid,运行命令: msbuild /t:checkuid LocalizationDemo .csproj。

3. 通过Localization.Attributes,设置本地化的特性(属性)

如:<DataGridTextColumn x:Uid="DataGridTextColumn_1" Header="No." Width="auto" Binding="{Binding No}" Localization.Attributes="$Content(Ignore) Width(Ignore) Header(None Readable Modifiable)"/>,这里 Width(Ignore) 设置Width属性不实现本地化, Header(None Readable Modifiable)设置 Header属性可读可写需要实现本地化,更多本地化特性细节请参见 本地化特性和注释

4. 编译项目,生成语言资源文件

生成的LocalizationDemo.resources.dll会在\bin\Debug\en-US\目录下。

5. 将语言资源文件输出存储为 .csv 文件,以作进一步分析

将 LocBaml.exe( LocBaml 工具是一种尚未投产使用的应用程序。 它显示为一种示例,该示例使用某些本地化 API 并演示如何编写本地化工具。 点击下载LocBaml )复制到应用程序的 bin\debug 文件夹,即创建主应用程序集的位置。 打开Visual Studio 命令提示(2010), 进入 bin\debug目录, 运行命令: LocBaml.exe /parse en-US/LocalizationDemo.resources.dll /out:en-US.csv

6. 翻译可本地化内容

注意此步应当 使用 兼容 unicode的文本编辑器进行编辑翻译 。或者中文系统下 将 .csv 文件在 Microsoft Excel 中进行查看,对最后一列(值)进行翻译更改, 另存为zh-CN.csv。

此时会弹出如下消息提示框,选择"是( Y )",

然后用记事本打开zh-CN.csv文件,选择"另存为",如下图(初始编码为"ANSI")选择"UTF-8"保存。

7. 使用 LocBaml 生成新增语言的.resources.dll资源文件

运行下面命令根据翻译的zh-CN.csv生成中文资源文件LocalizationDemo.resources.dll,可以在E:\目录下找到,将生成的 LocalizationDemo.resources.dll 复制到项目 \bin\Debug\zh-CN\目录下即可。

LocBaml.exe /generate en-US/LocalizationDemo.resources.dll /trans:zh-CN.csv /out:E:\ /cul:zh-CN

8. 测试新增语言资源文件

  • 更改操作系统的地区设置( “开始” | 控制面板 | “区域和语言选项” )。
  • 1       //中文为: "zh-CN"
    2       CultureInfo ci = new CultureInfo("en-US");
    3       Thread.CurrentThread.CurrentCulture = ci;
    4       Thread.CurrentThread.CurrentUICulture = ci;

    二、使用资源字典文件

    这种方式的实现相对较为丰富,即可实现新增支持语言无需重新编译,也可实现程序运行中切换语言(参见本文 实现动态切换程序显示语言 ),以下是常用实现方式。

    1. 创建资源字典StringResource.xaml,包含需要支持多国语言的字符串

     1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     3                     xmlns:sys="clr-namespace:System;assembly=mscorlib">
     4     <sys:String x:Key="WinTitle">MainWindow</sys:String>
     5     <sys:String x:Key="TblText">Support multi language demo.</sys:String>
     6     <sys:String x:Key="BtnOK">OK</sys:String>
     7     <sys:String x:Key="HdNo">No.</sys:String>
     8     <sys:String x:Key="HdName">Name</sys:String>
     9     <sys:String x:Key="HdGender">Gender</sys:String>
    10     <sys:String x:Key="HdDept">Dept</sys:String>
    11     <sys:String x:Key="HdEmail">Email</sys:String>
    12     <sys:String x:Key="HdTel">Tel</sys:String>
    13     <sys:String x:Key="MsgShowTime">Now time is:{0}</sys:String>
    14 </ResourceDictionary>

    2. 在app.xaml中使用MergedDictionary

     1 <Application x:Class="LocalizationDemo.App"
     2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     4              StartupUri="MainWindow.xaml" Exit="Application_Exit">
     5     <Application.Resources>
     6         <ResourceDictionary>
     7             <ResourceDictionary.MergedDictionaries>
     8                 <ResourceDictionary Source="Resources\StringResource.xaml" />
     9                 <ResourceDictionary Source="Resources\StringResource.zh-CN.xaml" />
    10             </ResourceDictionary.MergedDictionaries>
    11         </ResourceDictionary>
    12     </Application.Resources>
    13 </Application>

    3. 在程序中使用资源字典中的字符串

  • .xaml文件,Text="{StaticResource TblText}"
  • .cs文件,string s = Application.Current.FindResource("MsgShowTime").ToString();
  • 4. 复制 StringResource.xaml命名为StringResource.zh-CN.xaml,key不变,value翻译为中文

     1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     3                     xmlns:sys="clr-namespace:System;assembly=mscorlib">
     4     <sys:String x:Key="WinTitle">主窗体</sys:String>
     5     <sys:String x:Key="TblText">支持多国语言示例。</sys:String>
     6     <sys:String x:Key="BtnOK">确定</sys:String>
     7     <sys:String x:Key="HdNo">员工号</sys:String>
     8     <sys:String x:Key="HdName">姓名</sys:String>
     9     <sys:String x:Key="HdGender">性别</sys:String>
    10     <sys:String x:Key="HdDept">部门</sys:String>
    11     <sys:String x:Key="HdEmail">邮箱</sys:String>
    12     <sys:String x:Key="HdTel">电话</sys:String>
    13     <sys:String x:Key="MsgShowTime">现在时间是:{0}</sys:String>
    14 </ResourceDictionary>

    5. 在程序启动时根据当前区域加载对应的资源字典文件

     1             List<ResourceDictionary> dictionaryList = new List<ResourceDictionary>();
     2             foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries)
     4                 dictionaryList.Add(dictionary);
     6             string requestedCulture = string.Format(@"Resources\StringResource.{0}.xaml", Culture);
     7             ResourceDictionary resourceDictionary = dictionaryList.FirstOrDefault(d => d.Source.OriginalString.Equals(requestedCulture));
     8             if (resourceDictionary == null)
    10                 requestedCulture = @"Resources\StringResource.xaml";
    11                 resourceDictionary = dictionaryList.FirstOrDefault(d => d.Source.OriginalString.Equals(requestedCulture));
    12             }
    13             if (resourceDictionary != null)
    14             {
    15                 Application.Current.Resources.MergedDictionaries.Remove(resourceDictionary);
    16                 Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
    

    6. 测试新增语言资源文件同"使用LocBaml工具"中step8

    三、使用.resx资源文件

    这种方式和Winform支持多国语言保持一致,相对较容易实现,新增支持语言需要重新编译程序,所有的.resx文件必须放在同一个主程序集中。

    1. 添加字符串资源

    在资源文件Resources.resx中添加字符串资源,并将访问修饰符设置为Public。

    2. 在程序中使用资源文件

    .xaml文件,引入名称空间:xmlns:props="clr-namespace:LocalizationDemo.Properties";使用方式:Text="{x:Static props:Resources.TblText}"

    .cs文件,使用方式:string s = Properties.Resources.MsgShowTime;

    3. 新增语言资源文件

    以新增简体中文为例,复制资源文件Resources.resx,重命名为Resources.zh-CN.resx,将值翻译为中文保存。

    4. 设置程序运行语言环境

    1     LocalizationDemo.Properties.Resources.Culture = new CultureInfo("zh-CN");
    

    四、实现动态切换程序显示语言

    以上三种支持多国语言的解决方案都是在程序运行过程中不能变更语言的,要实现程序运行中动态切换语言就需要在UI设计使用DynamicResource,其中一种简单的实现是通过DynamicResource引用资源字典文件键值。

    使用DynamicResource

    具体实现方法和本文使用资源字典文件中步骤基本一致,只需将UI相关的.xaml文件中引用资源字典文件键值的StaticicResource改为DynamicResource,如下:

    1     <TextBlock Height="23" TextWrapping="Wrap" Name="txtTips" Text="{StaticicResource TblText}" />
    2     <!--StaticicResource改成DynamicResource,如下-->
    3     <TextBlock Height="23" TextWrapping="Wrap" Name="txtTips" Text="{DynamicResource TblText}" />

    非Visual Controls实现动态切换语言

    需要注意的是,在WPF DataGrid中的DataGridColumn等控件不是Visual Controls,它们的Binding属性可以进行绑定,除此之外必须是Static静态的,也就是我们可以通过创建静态的样式资源,在这些样式资源中包含动态内容,然后在像DataGridColumn这样的控件中引用静态的样式资源就可以了,实现如下:

  • 静态样式资源包含动态内容
  • 1     <Window.Resources>
    2         <Style x:Key="HeaderNoStyle" TargetType="{x:Type DataGridColumnHeader}">
    3             <Setter Property="Content" Value="{DynamicResource HdNo}" />
    4     </Window.Resources>
  • DataGridColumn引用静态的样式资源
  • 1     <DataGridTextColumn HeaderStyle="{StaticResource HeaderNoStyle}" Width="auto" Binding="{Binding No}"/>

    附:实现MessageBox支持多语言

    另外,和其他元素不同的是,MessageBox的确定(OK),是(Yes),否(NO)等按钮是直接调用系统Win32 API,MessageBox按钮当前显示哪种语言文本是由Windows操作系统安装的默认语言决定,而与其他无关。为了解决这一问题需要实现设置自定义系统MessageBox按钮文本,MessageBoxManager能够实现这一需求,更多内容请参见Localizing SystemMessageBox一文。另外一种解决办法就是自定义替代MessageBox功能的Windows窗体。

    如何使用MessageBoxManager

          //设置OK按钮显示文本
          MessageBoxManager.OK = "确定啊,亲";
          //注册MessageBoxManager
          MessageBoxManager.Register();
          //正常使用MessageBox
          MessageBox.Show("你懂的...");
          //取消注册MessageBoxManager
          MessageBoxManager.Unregister();

    效果如下图: