本篇博客主要描述在 OEA 框架中的多国语言框架的原理及应用。
多国语言常见实现及原理分析
管理软件平台,一般来说,都应该支持多国语言,以支持应用程序走向国际化。OEA 最近也提供了多国语言框架,它可以在修改少量甚至不修改代码的情况下,快速、灵活地,使得整个应用软件支持各个国家的语言。在 .NET 平台上,要实现多国语言切换,一般可以使用资源文件实现;在WPF中,也可以使用动态引用实现。这些可以参考以下几篇文章中讲到的方法:《WP7多国语言支持》、《一种灵活的WPF程序多国语言支持》、《.NET多国语言支持》。
而这些方法背后的本质,其实都是在开发期,通过开发人员定义的“键(Key)”,找到在运行期加载的不同语言包中对应的语言项,而再让界面呈现出来。即:
- 开发期定义开发人员使用的 Key。
- 在不同的语言包中,为 Key 定义不同的语言项(Value)。
- 运行时,通过 Key,寻找并显示不同语言的词句。
这些开发模式中,开发人员都不得不去定义 Key、创建语言包,并修改所有显示的程序。例如,以下代码:lblUserName.Text = Resources.UserNameDisplayLabel; 我们不得不定义一个 UserNameDisplayLabel 的 Key,并在语言包中去填写对应的译文。那么,能不能让开发人员不去定义 Key,而直接使用开发人员本国语言编写呢?例如,让中国的开发人员这样写:lblUserName.Text = “用户名:",最终则在英文版本中显示“UserName:”。OEA 中已经实现了这一方法:
OEA 中的多国语言框架
在 OEA 中,多国语言的实现原理,其实是一样的。但是,多国语言的 Key,就是开发人员的本国语言,而不需要为其定义单独的英文键。例如,lblUserName.Text = “用户名:"; 中,“用户名:" 就是这个 Key,OEA 中称其为开发语言。而某个国家语言的语言包中的每一项,也是一个 Key/Value 对,Key 是开发语言,而 Value 则是这个开发语言对应的第三国语言。
其主要思想就是把开发人员所在国的语言,作为语言包中每一项的键。
在使用时,开发人员在框架基础上随意在代码中使用本国语言,并将最终由框架在界面层翻译为目标国语言。在此设计基础上,OEA 提供了相应的多国语言编辑环境,它是集成在运行时系统中的。由一个 OEA.MultiLanguages 的插件提供:
也就是说,多国语言中的语言包等数据载体,也使用了 OEA 的实体类系统。加载此插件后,并运行系统,会发现在开发工具中添加了两个相应的模块:
分别是开发语言模块和多国语言模块。
在开发语言模块中,我们可以看到整个程序代码中使用的所有开发语言,也就是每一个 Key。
这些 Key 由 OEA 框架自动收集起来,这个过程发生在第一次运行该代码时。例如,开发人员编写了:App.MessageBox.Show("请选择一行数据。"); 这行代码,在这行代码被第一次运行后,OEA 会把"请选择一行数据。"作为开发语言收集起来:
在多国语言模块中,显示的则是所有已经添加的语言及该语言下所有翻译的项:
其中的翻译引擎是直接使用 baidu、bing 两个网站的“服务”,可以为把开发语言直接翻译为目标语言。而“打开所有模块”功能则是为了方便一次性收集整个应用程序所有模块界面的开发语言。这两个模块中,都有一个“自动更新”的按钮。它的作用是把当前框架收集到的所有开发语言写入各语言包中。
所以,要支持一个新的语言,只需要简单、快速的几步:
- 在多国语言中添加一个语言,如日文。
- 点击“自动更新”,把所有开发项添加到日文中。
- 点击“翻译引擎”,把日文包中的所有开发项翻译好。
- 如果哪些项翻译得不好,则可以让专人进行人工翻译。可以一边翻译,一边查看效果,不需要重启应用程序。
- 把整个应用程序配置为使用日文。这一步,只需要在 app.config 配置文件中使用以下配置:<add key="OEA_CurrentLanguageCode" value="JP"/>
这样,再次打开应用程序,会发现所有界面都已经是日文的了,除去人工翻译的整个过程不到3分钟:
编程接口
其实,看到这里,可能很多朋友会问一些问题:
- OEA 框架是怎么做到收集开发语言的?
- OEA 框架是怎么让整个界面都自动翻译的?
- 如果我没有调用框架的层面层 API,还想翻译怎么办?
- 翻译过程,是在什么时候发生?
整个多国语言框架,说白了就是实现字符串的翻译。所以 OEA 为 string 类型添加了扩展方法 Translate:
这样,就可以随时对于任何一个字符串调用,例如:当配置目标语言为英文时,调用 “中国”.Translate(),返回 “China”。这样,不论是否使用 OEA 的界面层框架,都使用这个方法来翻译。(其实换汤不换药,这跟使用资源文件中定义的 Resources.China 发生的过程一致,通过 Key 找到目标语言包的 Value,并返回。)
从这个方法的注释中可以看到,只要是被调用了此方法的字符串,框架都认为它是一个开发语言,会被自动收集起来。而 OEA 强大的界面生成框架,只是在适当的地方调用了 Translate 方法,所以整个界面都会被翻译、收集。OEA 中,这个方法的调用,是尽量靠近最终的界面显示,并尽量远离开发人员代码,这样可以保证开发人员在代码中的各类判断,还是使用开发语言。
同时,除了这个字符串的编程接口,由于 OEA 的客户端使用了 WPF 框架,所以还为 WPF 提供了专门的控件翻译接口,它是一个文本显示控件,及一个附加属性:
这样可以在 xaml 中自动翻译 WPF 中常见的属性。否则也可以直接调用 Translate 方法进行翻译。
待改进点
目前,该多国语言框架已经使用了两个月了。它的不足也慢慢暴露了出来:
- 开发语言变更造成无用的翻译项。
如果一个开发语言已经被收录进各语言包,此时开发人员再在程序中对其修改,则会造成旧的开发语言成为无用项保留在开发语言包中。所以这时需要人工去删除这些无用的项。虽然多国语言的数据是存储在服务端的数据库中(使用了客户端缓存,性能不是问题),再多的项也没关系,但是这毕竟是一个潜在的问题,后面需要设计一个好的方法来自动找到这些无用项。
- 动态拼接字符串的情况,也会造成无用的翻译项。
另外,如果在动态拼接字符串时,不作任何处理,直接交给界面层显示,则也会导致收集了过多无用的项。例如,要显示:”用户键”+ id,则可以收录”用户键1”、”用户键2”、”用户键3”……这时,我们提供了 IgnoreTranslation 方法用于忽略最终的动态字符串。开发人员需要为 ”用户键”单独调用 Translate,并对结果调用 IgnoreTranslation。如:App.MessageBox.Show((“用户键”.Translate() + id).IgnoreTransation())。
就目前的体验上讲,该框架的使用是非常舒服的。开发过程中,除了动态拼接时,基本不需要考虑多国语言的存在。开发效率较高。运行时在界面中可随意修改译文,即时见到效果,灵活性也不错。:)
希望对多国语言有研究的朋友,对我的待改进点提出建议。谢谢。:)