周银辉
有朋友询问如何让WPF应用能够提供多语言支持(中文,英文,日文.....),我的建议是:(1)抛弃WinForm时代的资源存储方式 (2)利用WPF的动态资源特性.
下面简单介绍一下并打造一个简单的DEMO:
1,语言资源文件怎样存储
我们知道以前(WPF以前)我们将界面上的各国语言分别存储在.resx类型的文件中,然后利用ResourceManager来得到相应资源并根据当地的CultureInfo来给界面文本赋值.这是可行的.但在WPF中存在的一个问题是:我们必须为每个控件在后台代码中来Code这些逻辑,因为XAML是找不到.resx类型中的资源的.但WPF中绝大多数界面元素都是在XAML中定义的这是一件很麻烦的事情.所以我们应该抛弃这样的做法.
WPF中的资源继承了以前的多种存储形式,但可以在XAML和C#(或其他)中通行的有两中,一个是Content,一个是Resource,前者是"内容",既是松散的资源链接,后者是被编译并嵌入的资源,注意,你在设置资源文件属性的时候会发现还有一个很让人混淆的Embedded Resource,这也是内嵌的资源,但其被压缩为二进制形式,既和以前WinForm的内嵌资源相同,Resource和Embedded Resource在编码上的区别是前者可以通过Uri找到,这是XAML需要的方式,后者可以通过Stream找到,这可以通过编写c#代码来查找资源.
所以,在这里看来Content是我们保存语言资源的良好方式,由于其是松散的资源链接,所以为应用程序添加新的语言包时就可以不重新编译了,只需将对象的语言文件拷贝到指定的文件夹即可.
2,界面元素如何链接到语言文本
这是StaticResource 和 DynamicResource要做的事情,比如:
<Button Content="{DynamicResource OK}"/>
至于是使用StaticResource 还是DynamicResource,这取决于你是否要在运行时动态切换,如果仅仅是软件启动的时候才切换语言StaticResource就可以了.3,OK 实战一下:
3.1 新建项目,并在项目中新建一个Lang文件夹用于保存我们的语言文件
3.2 在Lang文件夹中,新建"ResourceDictionary(WPF)",命名为"DefaultLanguage.xaml",并将其BuildAction设置为Page,这是一个默认语言资源文件,其将被编译(而不是松散链接,这样可以确保在软件语言包丢失或没有某国家或地区的对应语言包时可以有一种默认的界面语言):我们这里采用英文作为默认语言:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="OK">
OK
</sys:String>
<sys:String x:Key="Cancel">
Cancel
</sys:String>
</ResourceDictionary>
然后,我们添加另一国语言,比如中文,在Lang文件夹中,新建"ResourceDictionary(WPF)",命名为"zh-CN.xaml",并将其BuildAction设置为Content,将CopyToOutputDirctory设置为"if new",这样,我们的中文语言文件为将被拷贝到应用程序目录下的Lang目录下,其他的非默认语言的语言文件都应该采取这种方式.xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="OK">
OK
</sys:String>
<sys:String x:Key="Cancel">
Cancel
</sys:String>
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="OK">
确定
</sys:String>
<sys:String x:Key="Cancel">
取消
</sys:String>
</ResourceDictionary>
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="OK">
确定
</sys:String>
<sys:String x:Key="Cancel">
取消
</sys:String>
</ResourceDictionary>
3.3 为了让编码人员在设计器(比如VS,Blend)中所见即所得地看到界面文本,我们应该将默认语言资源加入到应用程序的资源列表中:
<Application x:Class="LangDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="lang\DefaultLanguage.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="lang\DefaultLanguage.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
这样我们就可以在设计器中使用这些资源了:
我们可以看到由于我们加载的默认语言是英文,设计器中我们的窗口界面上显示的是对应的英文文本.
3.4 软件启动时加载对应的本地化的语言:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LoadLanguage();
}
private void LoadLanguage()
{
CultureInfo currentCultureInfo = CultureInfo.CurrentCulture;
ResourceDictionary langRd = null;
try
{
langRd =
Application.LoadComponent(
new Uri(@"Lang\" + currentCultureInfo.Name + ".xaml", UriKind.Relative))
as ResourceDictionary;
}
catch
{
}
if (langRd != null)
{
if (this.Resources.MergedDictionaries.Count > 0)
{
this.Resources.MergedDictionaries.Clear();
}
this.Resources.MergedDictionaries.Add(langRd);
}
}
}
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LoadLanguage();
}
private void LoadLanguage()
{
CultureInfo currentCultureInfo = CultureInfo.CurrentCulture;
ResourceDictionary langRd = null;
try
{
langRd =
Application.LoadComponent(
new Uri(@"Lang\" + currentCultureInfo.Name + ".xaml", UriKind.Relative))
as ResourceDictionary;
}
catch
{
}
if (langRd != null)
{
if (this.Resources.MergedDictionaries.Count > 0)
{
this.Resources.MergedDictionaries.Clear();
}
this.Resources.MergedDictionaries.Add(langRd);
}
}
}
当软件启动时,我们根据当地的CultureInfo来加载对应的语言文件(如果该语言文件存在的话),由于界面上的文本不是硬编码进去的,而是采用DynamicResource,当后台Resource改变时,前台的引用也会动态改变,OK,当软件启动后,我们的界面就自动的切换了,我这台电脑上是中文:
当然,本地化还有很多内容,比如货币数字,阅读顺序等,这里仅仅提供了一个思想,你还可以在这里下载DEMO