多国语言开发最佳实践 (2)
要实现界面文本的多国语言支持,通常是将文本资源独立的保存在外部文件中,如XML,INI,在 .NET 环境下,有时会使用 .NET 资源文件。
先看一个简单的使用XML来实现多国语言开发的例子。
XML定义可能类似下面这样:
英文资源:
<Language name=”en”>
         <String1>Hello world.</String1>
         <String2>Knowledge is power</String2>
</Language> 
简体中文资源:
<Language name=”zh-CHS”>
         <String1>你好世界.</String1>
         <String2>知识就是力量</String2>
</Language> 
在代码中编写操作资源的类库时,会通过获取XML中的指定节点来获取文本资源,如:
string str = Language.Get(“String1”);
使用INI文件或其它文件形式存储,原理大体相同,并且在获取资源时,都会使用 String 做为方法参数来取得资源。
这种实现方式最大的问题和隐患在于可控性比较薄弱,对资源的使用无法进行强制性的约束。
考虑以下问题:
1. 开发人员获取资源时,拼写错误。
2. 修订资源文件时,对原有资源文件标识进行了修改,而未能同步到程序的各处。
3. 修订资源文件时,误删原有资源条目,或删除误认为已不在使用中但确仍在某处需要的条目。
4. 无法得知某条资源文本的使用情况:是否在使用,是否在多处使用。
5. 增加语言资源时,难以保证各语种资源完全同步,尤其在是资源数量较大,修订次数较多时。
这些问题都实际上都是不可避免的常犯错误,由于是类似弱引用方式,资源的调用方在使用资源时,对资源提供者(如资源文件处理类库)能否提供正确的资源完全未知。使得这些问题非常隐蔽,难以发现,无法在编译程序时发现问题,在团队开发中,以上问题可能会随着项目的深入越来越明显。
有没有办法,为不同语种的语言资源建立起统一的约束机制,并强制开发人员通过强类型的方式来调用资源呢?
我们想到了面向接口的方式,看下面这张类图。
首先定义接口 ILanguage,在接口中定义属性(Property)成员,然后让所有的资源文件类都实现ILanguage接口。
除此之外,还需要一些辅助操作的类和方法,假设我们定义一个 Current 类作为资源控制的主类:
该类包括一个表示当前正在使用的资源的Language属性,它的类型是ILanguage接口类型。还包含一个用于获取当前所有可用语言资源的GetLanguages方法,它返回一个ILanguage泛型List。
在这种方式下,我们对资源文件的使用就可以演变为:
string str = Current.Language.String1;
完全面向接口的编程,此时,资源文件对于调用者而言,不再是一个完全未知的黑箱,而是基于接口的强类型引用,即使出现拼写错误、资源文件反复修订,也可以保证程序对资源文件的使用完全可控。
如果要动态切换当前语言,只需将要切换至的资源类赋予 Current.Language 即可,因为调用者只管通过 ILanguage接口调用,不依赖具体的资源类。
增加新的语言资源时,只需使该资源类实现ILanguage接口,就可以整合进资源类库使用,因为有接口的约束,任何语言资源文件都会并且一定会提供统一的资源接口,不会因为反复修订,同时维护多种语言资源而造成混乱。
接下来通过一个简单实例,来讲解这种方式的具体应用。
该实例需要一个多语言资源处理工具,SE String Resource(下载:http://sailingease.com/ssr/download.htm)
首先启动SE String Resource。
数据列表中已经有了“Chinese(Simplified)”,那么我们再增加一个英文资源,点击上方的“添加语言”按钮。
选择英语,点击“确定”按钮后,看到资源列表中多出了 “English”这一列。
在资源列表中填写如下内容:
资源字符串编辑好之后,点击上方的“生成”按钮,显示如下生成界面:
对几个字段进行简要的说明:
默认语言:程序运行时,如果没有明确指定具体要使用的语言,使用该默认语言。
命名空间:资源类,主要类,接口的所属命名空间。
接口:接口的名子。
主要类:主要类的名子。
点击“浏览”按钮指定输出路径,点击“生成按钮”。
生成完毕后,指定的输出路径下会产生如下文件:
AssemblyInfo..cs是程序集文件。
Chinese (Simplified).resx、Chinese (Simplified).cs分别中简体中文的资源文件和对应类文件。
English.resx、English.cs是英语的资源文件和对应类文件。
ILanguage.cs是语言资源的接口。
Current.cs 是主要类。
类之间的关系请参见上文的类图和相关说明。
我们在解决方案中通过“添加现有项目”把这个资源类库添加到解决方案中,然后在主程序中添加引用,引用此项目:
解决方案搭建起来后,以后再在SE String Resouce中修订资源,只需把输出路径指定到解决方案下的资源文件所在文件夹生成即可,如果手工修改了自动生成的主要类、程序集信息,要记得在生成界面上取消“主要类”、“程序集信息”复选框的选择。
我在这个DEMO程序中设计了一个仅用于演示功能的简单界面:
界面左边的列表通过
Languages.Current.GetLanguages()
方法,获取当前所有可用的语言资源。
更改列表中选定的项后,把选定的资源指定到 Current.Language中:
if (this.listBoxLanguages.SelectedItem != null)
{
  Languages.Current.Language = (ILanguage)this.listBoxLanguages.SelectedItem;
  ApplyLanguage();
} 
完整代码如下:
代码 public Form1()
{
InitializeComponent();
ApplyLanguage();
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (ILanguage language in Languages.Current.GetLanguages())
{
this.listBoxLanguages.Items.Add(language);
}
}
private void listBoxLanguages_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.listBoxLanguages.SelectedItem != null)
{
Languages.Current.Language = (ILanguage)this.listBoxLanguages.SelectedItem;
ApplyLanguage();
}
}
private void ApplyLanguage()
{
this.label1.Text = Languages.Current.Language.Msg_HelloWorld;
this.label2.Text = Languages.Current.Language.Msg_KnowledgeIsPower;
}
至此,我们就实现了面向接口的多语言开发。
DEMO演示程序下载: /Files/sheng_chao/LanguagesDEMO.zip
SE String Resource下载:http://sailingease.com/ssr/download.htm
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号