使用 C# 和 .NET 2.0 进行全球化、国际化 (I18N) 和本地化






4.27/5 (15投票s)
.NET 2.0 平台下 Windows 应用程序的全球化、国际化 (I18N) 和本地化。
引言
全球化是指使应用程序能够处理不同文化和地区的过程。对于 Windows 应用程序而言,全球化意味着它面向全球分发。全球化有两个方面:
- 国际化:使应用程序无需语言或文化障碍即可使用;即,语言和文化信息来自资源文件,而不是硬编码在应用程序中。
- 本地化:为特定区域设置翻译和启用产品。基于资源文件,应用程序被翻译成特定的语言和文化。
在本文中,我们将讨论使用 .NET 2.0 平台进行 Windows 应用程序的国际化和本地化。
背景
阅读本文将使您对此处讨论的基本思想有一个大致的了解,但要完全理解每个概念,您必须对 .NET API 调用及其用法、`CultureInfo` 类、.resx 文件创建、向资源文件添加文本和图像等有所了解……
本地化应用程序
资源文件 (.resx) 用于本地化应用程序中的资源。我们使用 `System.Globalization` 命名空间中的类使我们的应用程序具有文化感知性。
全球化应用程序的第一步是设置 Windows 窗体的 `Localizable` 属性。要做到这一点,我们需要在设计器模式下打开窗体。
通过设置上述属性,窗体及其控件的所有设置/值都将保存在该语言的 .resx 文件中。最初,语言将设置为“默认”。在我们的演示应用程序中,我们可以看到 Windows 窗体 `MainForm` 的这些 .resx 文件。
一旦我们将此语言更改为其他语言,将为该窗体生成相应的 .resx 文件。例如,对于德语,是“MainForm.de-DE.resx”。
现在,如果我们更改控件的位置、大小、文本等,所有这些修改后的值都将存储在相应的语言 .resx 文件中。在启动应用程序时,`System.Resources.ResourceManager` 会检测语言并加载正确的资源文件。我们将为 Windows 窗体(我们支持的每种语言)拥有不同的 .resx 文件。如果我们采用这种方法进行 i18n,我们永远不应该触碰我们的资源文件,让设计器来更改事物。
自定义 .resx 文件创建
在深入代码细节之前,首先学习如何创建自定义资源文件并将其添加到我们的项目中。我们可以通过在“解决方案资源管理器”中右键单击我们的**项目文件**,然后单击“**添加 --> 新建项...**”菜单来完成此操作。在“添加新项”窗口中,选择“资源文件”模板并为其命名(如下图所示)。
.resx 文件中添加资源
我们可以将资源作为链接资源(外部文件)或嵌入资源(直接嵌入到 .resx 文件中)添加到我们的项目中。字符串只能作为嵌入资源存储。
当我们添加链接资源时,存储项目资源信息的 .resx 文件仅包含磁盘上资源文件的相对路径。图像将作为链接资源添加。
.resx 资源文件格式由 XML 条目组成,这些条目在 XML 标签内指定对象和字符串。
当我们将字符串添加到 .resx 文件时,一个名称/值对以 .resx 格式被包装在 XML 代码中,该代码描述字符串或对象值;字符串的名称嵌入在 `<data>` 标签中,值包含在 `<value>` 标签中,如下例所示:
<data name="string1">
<value>hello</value>
</data>
在我们的演示应用程序中,我们有一个名为 `CommonResource` 的自定义 .resx 文件,我们可以使用以下语法从代码中访问 .resx 文件的字符串:
// To get the strings from .resx file we need to write the code syntax
// as - ResourceName.DataName
// string returnString = Resource.string1; // returns hello.
myAppCultureName.Text = CommonResource.ApplicationCultureName;
myAppCultureNativeName.Text = CommonResource.ApplicationCultureNativeName;
为了全球化应用程序,我们必须为每种语言创建一个自定义 .resx 文件。新创建的 .resx 文件的名称格式应与系统创建的 .resx 文件类似。例如,在演示应用程序中,我们有一个自定义 .resx 文件 `CommonResource.resx`,那么德语的 .resx 应该是 `CommonResource.de-DE.resx`,法语的 .resx 应该是 `CommonResource.fr-FR.resx`。
ResourceManager 会首先在特定文化 .resx 文件中查找给定的字符串数据。如果该文件中没有字符串数据,它将从非特定文化(默认语言).resx 文件中读取。
.resx 文件中添加图像
将图像添加到 .resx 文件与将字符串添加到 .resx 文件一样简单,命名格式也相似,但实际上有点残酷 ;) 。
由于字符串是嵌入资源,因此为不同语言创建 .resx 文件没有问题。但是,要将图像添加到 .resx 文件,我们需要为每个 .resx 文件提供不同的图像路径。
完成此任务的简单方法是为项目中每种语言创建一个“资源”文件夹,并在这些文件夹中**添加**相应的图像文件(如下所示)。
现在,在 Visual Studio 中打开 .resx 文件,并将资源类型选择为“图像”。然后,单击“**添加资源 --> 添加现有文件...**”将每个语言的图像添加到其相应的 .resx 文件中。
完成!现在我们可以以与引用字符串相同的方式引用图像。在我们的演示应用程序中,我们有一个图像 resx 文件 `Images`,其中有一个资源 `Image1`。
// Display the image on PictureBox a/c to the culture
myLanguagePictureBox.BackgroundImage = Images.Image1;
使用代码
`System.Globalization` 命名空间中的主要类是 `CultureInfo` 类。它包含许多方法和属性来识别不同的文化并配置我们的应用程序以使用特定的文化。
文化类型
我们的应用程序可以使用三种类型的文化:`InvariantCulture`、`NeutralCulture` 和 `SpecificCulture`。
`InvariantCulture` 实际上意味着**独立于文化**。当我们希望以**独立于文化**的方式比较字符串、显示或比较日期时,我们使用 `InvariantCulture`。默认情况下,它使用 en-US 文化。我们在演示应用程序中使用了这种方法来读取日期的独立于文化的格式。
// Invariant Culture can read the format specified
// in TextBox (DD/MM/YYYY), otherwise it
// will expect the date in respective culture format.
// Change the CultureInfo type and see the difference.
DateTime.TryParse(myAgeTextBox.Text, CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal, out dateOfBirth);
`NeutralCulture` 指的是与语言相关但与特定国家或地区无关的文化。我们可以通过两个小写字母来指定 `NeutralCulture`,以识别语言。例如,“en”代表英语,“fr”代表法语等。
`SpecificCulture` 是语言和国家/地区特定的文化,它决定了要与我们的应用程序一起使用的语言以及要与我们的应用程序一起使用的日期/时间格式和货币格式。我们使用两个小写字母标识语言,两个大写字母标识国家/地区,并用连字符分隔来指定特定文化。例如,“en-US”代表英语(美国),“en-GB”代表英语(英国),“fr-FR”代表法语(法国)等。
设置特定文化
我们使用 `System.Threading.Thread.CurrentCulture` 和 `System.Threading.Thread.CurrentUICulture` 来配置我们的应用程序以使用特定的文化设置,例如:
public void SetCultureInfo(string culture)
{
// culture could be 'en-US', 'de-DE' etc...
CultureInfo myCultureInfo = new CultureInfo(culture);
Thread.CurrentThread.CurrentCulture = myCultureInfo;
Thread.CurrentThread.CurrentUICulture = myCultureInfo;
.
.
InitializeComponent();
.
}
`CurrentCulture` 表示当前线程使用的文化,而 `CurrentUICulture` 表示资源管理器在运行时用于查找特定文化资源所使用的文化。
运行时读取 .resx 文件(特定文化)
有时我们可能需要在运行时从不同文化的 .resx 文件读取资源。在我们的演示应用程序中,我们有一个 `ResourceManager` 类来实现这一点:
/// <summary>
/// Resource Manager Class
/// </summary>
public class ResourceManager
{
/// <summary>
/// Read the resource file and return the string of specified culture
/// </summary>
/// <param name="data">String Data to be read</param>
/// <param name="cultureInfo">Type of Culture</param>
/// <returns></returns>
public static string GetString(string data, CultureInfo cultureInfo)
{
return CommonResource.ResourceManager.GetString(data, cultureInfo);
}
}
我们可以修改此类或方法来从特定的 .resx 文件读取资源。现在在运行时,我们可以传递参数来获取**数据**的特定文化**值**。
if (!myShowCultureCheckBox.Checked)
{
myDisplayLabel.Text = string.Format("{0}",
ResourceManager.GetString(FIXED_TEXT, CultureInfo.InvariantCulture));
}
else
{
myDisplayLabel.Text = string.Format("{0}",
ResourceManager.GetString(FIXED_TEXT, CultureInfo.CurrentUICulture));
}
UI 设计
如果我们的 Windows 窗体已本地化,我们可以为每种文化设置控件的大小、位置等。我们可以在演示应用程序中看到这一点,其中控件的位置和大小对于每种文化都不同。
希望这里讨论的想法能帮助您使您的应用程序全球化。 ;)
关注点
- 如果您想手动完成所有工作,只需添加一个资源文件,将所有常用/共享的字符串放在其中,然后将文件嵌入到您的项目中。维护过多的 .resx 文件会很困难。假设您有一个资源字符串,它对应用程序或项目中的所有窗体都是通用的,并且您为每个窗体单独保留它,那么更新字符串值需要在所有 .resx 文件中进行更新,这有点棘手。在我们的演示应用程序中,我们有一个名为 `CommonResource.resx` 的资源文件,其中包含应用程序的所有共享字符串。
- 如果您使用自动国际化,您应该永远不要触碰您的资源文件;使用设计器来更改事物。更改此文件可能会导致应用程序行为异常。
- 尽量不要使用默认语言文件(Form1.resx)。取而代之的是,为英语创建一个,为您的其他语言创建一个(Form1.en-EN.resx 和 Form1.xy-XY.resx)。
- 始终在处理 `DateTime`、`Calendar` 或将对象转换为字符串 - `ToString()` 时使用 `CultureInfo`。例如,在验证“`Date`”时,代码可能期望一种语言的 dd/mm/yyyy 格式,而另一种语言的 mm/dd/yyyy 格式。如果用户按照其文化输入日期,我们应该使用文化信息进行验证;否则,使用**不变**文化。在我们的演示应用程序中,我们有一个计算年龄的函数,其中使用了 `InvariantCulture`,因为用户始终需要以 dd/mm/yyyy 格式输入日期。
历史
- 2009 年 10 月 31 日 - 更新了演示文件。
- 2009 年 10 月 30 日 - 更新了快照和描述。
- 2009 年 10 月 29 日 - 发布了文章。