.NET 基于 XML 的本地化
一篇关于 .NET 本地化的文章
简介与背景
在开发我的个人项目时,我必须解决本地化的问题。C# 为此提供了一个几乎完美的解决方案。如果你的窗体 (form) 的 Localization
属性设置为 true,并且你指定了默认语言,那么 studio (或 C# express) 将生成一系列组件的可本地化文本属性,这些属性部署在项目中。你可以通过双击 *.resx 文件在表格中编辑这些值。此外,编辑器会生成一个 Resourcename.Designer.cs 文件,该文件包含本地化属性,这些属性作为名为“resourcename”的类的静态成员。
这允许通过唯一的静态属性引用任何文本值,以基于 CultureInfo
属性查找变量的适当字符串值(有关详细信息,请参阅 msdn)。如果你想为你的项目添加一种额外的语言,你必须创建一个具有相同名称的新 .resx 文件,但名称必须包含文化 ID。 例如:LocalisationSample.resx(默认 resx 文件),LocalisationSample.en-EN.resx(英文版本)。
现在,如果你的项目重新编译,并且操作系统语言设置从你的默认语言(在我的例子中是匈牙利语)更改为英语,那么所有翻译的文本将以英语显示。
虽然这是一个强大的本地化问题解决方案,但我有一些额外的要求
- 我不希望重新编译项目来添加新语言。
- 我不希望代码大小和编译时间因新语言而增加。
- 我想为用户提供将显示的文本翻译成任何语言的工具。
结果
为了满足上述要求,数据必须存储在用户可编辑的 XML 文件 (.resxml) 中。这些文件的文件名根据以下规则创建
文件名 + _lang + .resxml,例如
MyResources.resxml -> 英语
MyResources_hu.resxml -> 匈牙利语
resxml 文件的结构非常简单;以下示例 (Fruits.resxml) 包含一些水果的(ID、值、描述)三元组
<?xml version="1.0" encoding="utf-8"?>
<root>
<it id="APPLE_ID" value="apple" description="my favorite fruit" />
<it id="PEAR_ID" value="pear" description="a sweet fruit" />
</root>
定义了一个静态类 RManager
,它能够在应用程序执行之初根据语言设置加载适当的 resxml 文件。这个类必须包含在你的项目中。为了将字符串变量(静态属性)用作全局变量,应该在 filename.cs(示例中的 Fruits.cs)文件中定义一个额外的静态类,名为“filename”。
/*RMTool 1.0 generated file, do not edit!*/
using RMTool;
using System.ComponentModel;
namespace FruitsNm{
public static class Fruits
{
///<summary>
// my favorite fruit
///</summary>
public static string APPLE_ID { get { return RManager.GetValue("Fruits",
"APPLE_ID"); } }
///<summary>
// a sweet fruit
///</summary>
public static string PEAR_ID { get { return RManager.GetValue("Fruits",
"PEAR_ID"); } }
}
} /* end of file */
为了简化 resxml 文件和静态类的生成,我开发了一个免费工具,名为 RMTool。此工具在所选 resxml 文件的表格中显示数据三元组(ID (Name),值 (Text),描述)。
该界面有助于管理、生成和翻译你的资源文件。
Using the Code
我创建了一个新项目,并在 Form1 中放置了一个 Label。
在将 RManager.cs、Fruit.cs 文件添加到项目,并在 Form1 文件中引用 RMTool 和 FruitsNm 命名空间后,我能够将依赖于 APPLE_ID 语言的字符串值分配给 label1
的 Text
属性。
源代码,Form1 的构造函数
public Form1()
{
InitializeComponent();
// the path of the application exe,
// the Fruits.resxml resource file
// is stored in this directory
string AppPath =
Path.GetDirectoryName(Application.ExecutablePath);
// get Fruits.resxml
RManager.GetFiles(AppPath, "");
label1.Text = Fruits.APPLE_ID
}
以及结果
摘要
我提出了一种解决本地化问题的替代方法。通过这种方式,你的项目大小和编译时间不会随着新的语言资源而增加,因为它们存储在不同的 XML 文件 (resxml) 中,这些文件在应用程序执行期间动态加载。我还提供了一个工具来方便资源文件的开发。