促使程序赢得更多客户的最好、最经济的方法是使之支持多国语言,而不是将潜在的客户群限制为全球近70亿人口中的一小部分。本文介绍四种实现WPF应用程序支持多国语言的解决方案。
效果如下图:
Language - en-US (英文)
Language - zh-CN (中文)
这是微软MSDN给出的一种解决方案,请参见如何:对应用程序进行本地化。这种方式操作相对繁琐,不能方便的实现在程序运行过程中动态切换语言,但是新增支持语言无需对项目重新编译,这是这种实现方式的一个亮点。
项目文件LocalizationDemo.csproj添加<UICulture>en-US</UICulture>,程序集文件AssemblyInfo.cs中把[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]注释去掉。
打开Visual Studio 命令提示(2010)如下图,进入项目文件.csproj所在目录,运行命令:msbuild /t:updateuid LocalizationDemo.csproj;若要验证是否缺少或重复Uid,运行命令:msbuild /t:checkuid LocalizationDemo.csproj。
如:<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属性可读可写需要实现本地化,更多本地化特性细节请参见本地化特性和注释。
生成的LocalizationDemo.resources.dll会在\bin\Debug\en-US\目录下。
将 LocBaml.exe(LocBaml 工具是一种尚未投产使用的应用程序。它显示为一种示例,该示例使用某些本地化 API 并演示如何编写本地化工具。点击下载LocBaml)复制到应用程序的 bin\debug 文件夹,即创建主应用程序集的位置。打开Visual Studio 命令提示(2010),进入bin\debug目录,运行命令:LocBaml.exe /parse en-US/LocalizationDemo.resources.dll /out:en-US.csv
注意此步应当使用兼容unicode的文本编辑器进行编辑翻译。或者中文系统下将 .csv 文件在 Microsoft Excel 中进行查看,对最后一列(值)进行翻译更改,另存为zh-CN.csv。
此时会弹出如下消息提示框,选择"是(Y)",
然后用记事本打开zh-CN.csv文件,选择"另存为",如下图(初始编码为"ANSI")选择"UTF-8"保存。
运行下面命令根据翻译的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
1 //中文为: "zh-CN" 2 CultureInfo ci = new CultureInfo("en-US"); 3 Thread.CurrentThread.CurrentCulture = ci; 4 Thread.CurrentThread.CurrentUICulture = ci;
这种方式的实现相对较为丰富,即可实现新增支持语言无需重新编译,也可实现程序运行中切换语言(参见本文实现动态切换程序显示语言),以下是常用实现方式。
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>
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>
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>
1 List<ResourceDictionary> dictionaryList = new List<ResourceDictionary>(); 2 foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries) 3 { 4 dictionaryList.Add(dictionary); 5 } 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) 9 { 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); 17 }
这种方式和Winform支持多国语言保持一致,相对较容易实现,新增支持语言需要重新编译程序,所有的.resx文件必须放在同一个主程序集中。
在资源文件Resources.resx中添加字符串资源,并将访问修饰符设置为Public。
.xaml文件,引入名称空间:xmlns:props="clr-namespace:LocalizationDemo.Properties";使用方式:Text="{x:Static props:Resources.TblText}"
.cs文件,使用方式:string s = Properties.Resources.MsgShowTime;
以新增简体中文为例,复制资源文件Resources.resx,重命名为Resources.zh-CN.resx,将值翻译为中文保存。
1 LocalizationDemo.Properties.Resources.Culture = new CultureInfo("zh-CN");
以上三种支持多国语言的解决方案都是在程序运行过程中不能变更语言的,要实现程序运行中动态切换语言就需要在UI设计使用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}" />
需要注意的是,在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>
1 <DataGridTextColumn HeaderStyle="{StaticResource HeaderNoStyle}" Width="auto" Binding="{Binding No}"/>
另外,和其他元素不同的是,MessageBox的确定(OK),是(Yes),否(NO)等按钮是直接调用系统Win32 API,MessageBox按钮当前显示哪种语言文本是由Windows操作系统安装的默认语言决定,而与其他无关。为了解决这一问题需要实现设置自定义系统
MessageBox按钮文本,
MessageBoxManager能够实现这一需求,更多内容
请参见
Localizing SystemMessageBox一文。另外一种解决办法就是
自定义替代MessageBox功能的Windows窗体。
//设置OK按钮显示文本 MessageBoxManager.OK = "确定啊,亲"; //注册MessageBoxManager MessageBoxManager.Register(); //正常使用MessageBox MessageBox.Show("你懂的..."); //取消注册MessageBoxManager MessageBoxManager.Unregister();
效果如下图: