多国语言开发最佳实践 (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

posted @ 2010-07-13 18:17  rob_2010  阅读(173)  评论(0)    收藏  举报