LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

C# 实现多语言支持的本地化(System.Globalization名称空间)

admin
2025年3月22日 7:44 本文热度 167

本章内容:
● 使用表示区域性和区域的类
● 应用程序的全球化
● 应用程序的本地化
本章将介绍.NET 应用程序的全球化和本地化。全球化(Globalization)用于国际化的应用程序:使 应用程序可以在国际市场上销售。采用全球化策略,应用程序应根据区域性、不同的日历等支持不 同的数字和日期格式。本地化(Localization)用于为特定的区域性翻译应用程序。而字符串的翻译可 以使用资源,如.NET 资源或WPF 资源字典。

System.Globalization名称空间

System.Globalization 名称空间包含了所有的区域性类和区域类,以支持不同的日期格式、不同 的数字格式,甚至如GregorianCalendar 类、HebrewCalendar 类和JapaneseCalendar 类等表示的不同 日历。使用这些类可以根据不同的地区显示不同的表示法。

Unicode问题

因为一个Unicode 字符有16 位,所以共有65536 个Unicode 字符。这对于当前在信息技术中使 用的所有语言够用吗?例如,汉语就需要80 000 多个字符。但是,Unicode 可以解决这个问题。使 用Unicode 必须区分基本字符和组合字符。可以给一个基本字符添加若干个组合字符,组成一个可 显示的字符或一个文本元素。

例如,冰岛的字符Ogonek,就可以使用基本字符0x006F(拉丁小字母o)、组合字符0x0328(组 合Ogonek)和0x0304(组合Macron)组合而成,组合字符在0x0300~0x0345 之间定 义,对于美国和欧洲市场,预定义字符有助于处理特殊的字符。字符Ogonek 也可以用预定义字符 0x01ED 来定义。

image

StringInfo 类的静态方法

方 法
说 明
GetNextTextElement()
返回指定字符串的第一个文本元素(基本字符和所有的组合字符)
GetTextElementEnumerator()
返回一个允许迭代字符串中的所有文本元素的TextElementEnumerator 对象
ParseCombiningCharacters()
返回一个引用字符串中的所有基本字符的整型数组

一个显示字符可以包含多个Unicode 字符。要解决这个问题,如果编写的应用程 序要在国际市场销售,就不应使用数据类型char,而应使用string。string 可以包含由 基本字符和组合字符组成的文本元素,但char 不能。

区域性和区域

世界分为多个区域性和区域,应用程序必须知道这些区域性和区域的差异。区域性是基于用户 的语言和区域性习惯的一组偏爱特性。RFC 1766(www.ietf.org/rfc/rfc1766.txt)定义了区域性的名称, 这些名称根据语言和国家或区域的不同在世界各地使用。例如en-AU、en-CA、en-GB 和en-US 分 别用于表示澳大利亚、加拿大、英国和美国的英语。

在 System.Globalization 名称空间中,最重要的类是CultureInfo。这个类表示区域性,定义了日 历、数字和日期的格式,以及和区域性一起使用的排序字符串。

RegionInfo 类表示区域设置(如货币),说明该区域是否使用米制系统。在某些区域中,可以使用 多种语言。例如,西班牙区域就有Basque(eu-ES)、Catalan(ca-ES)、Spanish(es-ES)和Galician(gl-ES) 区域性。类似于一个区域可以有多种语言,一种语言也可以在多个区域使用,例如,墨西哥、西班 牙、危地马拉、阿根廷和秘鲁等都使用西班牙语。

特定、中立和不变的区域性

在.NET Framework 中使用区域性,必须区分3 种类型:特定、中立和不变的区域性。

特定的区域性与真正存在的区域性相关,这种区域性用上一节介绍的RFC 1766 定义。特定的 区域性可以映射到中立的区域性。例如,de 是特定区域性de-AT、de-DE、de-CH 等的中立区域性, de 是德语(German【Deutsch】)的简写,AT、DE 和CH 分别是奥地利(Austria)、德国(Germany)和瑞 士(Switzerland)等国家的简写。

在翻译应用程序时,通常不需要为每个区域翻译,因为奥地利和瑞士等国使用的德语没有太大 的区别。所以可以使用中立的区域性来本地化应用程序,而不需要使用特定的区域性。

不变的区域性独立于真正的区域性。在文件中存储格式化的数字或日期,或通过网络把它们发 送到服务器上时,最好使用独立于任何用户设置的区域性。

下图显示了区域性类型的相互关系。

image

CurrentCulture和CurrentUICulture

设置区域性时,必须区分用户界面的区域性和数字及日期格式的区域性。区域性与线程相关, 并且通过这两种区域性类型,就可以把两种区域性设置应用于线程。Thread 类提供了CurrentCulture 和CurrentUICulture 属性。CurrentCulture 属性用于设置与格式化和排序选项一起使用的区域性,而 CurrentUICulture 属性用于设置用户界面的语言。

使用 Windows 控制面板中的“区域和语言”选项,就可以改变CurrentCulture 的默认设置,如 下图 所示。使用这个配置,还可以改变区域性的默认数字、时间和日期格式。

CurrentUICulture 属性不依赖于这个配置,而依赖于操作系统的语言。这有一个例外:如果 Windows 7、Windows Vista 或Windows XP 安装了多语言用户界面(Multi-language User Interface, MUI),就可以用区域配置改变用户界面的语言,这会影响CurrentUICulture 属性。

这些设置都使用默认值,在许多情况下,不需要改变默认值。如果需要改变区域性,只需把线 程的两个区域性改为Spanish 区域性,如下面的代码段所示:

System.Globalization.CultureInfo ci = new
System.Globalization.CultureInfo("es-ES");
System.Threading.Thread.CurrentThread.CurrentCulture = ci;
System.Threading.Thread.CurrentThread.CurrentUICulture = ci;

前面已学习了区域性的设置,下面讨论CurrentCulture 设置对数字和日期格式的影响。

数字格式

System 名称空间中的数字结构Int16、Int32 和Int64 等都有一个重载的ToString()方法。这个方 法可以根据地域创建不同的数字表示法。对于Int32 结构,ToString()方法有下述4 个重载版本:

public string ToString();
public string ToString(IFormatProvider);
public string ToString(string);
public string ToString(string,
IFormatProvider);

不带参数的ToString()方法返回一个没有格式化选项的字符串,也可以给ToString()方法传递一 个字符串和一个实现IFormatProvider 接口的类。

该字符串指定表示法的格式。而这个格式可以是标准数字格式化字符串或者图形数字格式化字 符串。对于标准数字格式化,字符串是预定义的,其中C 表示货币符号,D 表示输出为小数,E 表 示输出用科学计数法表示,F 表示定点输出,G 表示一般输出,N 表示输出为数字,X 表示输出为 十六进制数。对于图形数字格式化字符串,可以指定位数、节和组分隔符、百分号等。图形数字格 式字符串###, ###表示两个3 位数块被一个组分隔符分开。

IFormatProvider 接口由NumberFormatInfo、DateTimeFormatInfo 和CultureInfo 类实现。这个接 口定义了GetFormat()方法,它返回一个格式对象。

NumberFormatInfo 类可以为数字定义自定义格式。使用NumberFormatInfo 类的默认构造函数, 可以创建独立于区域性的对象或不变的对象。使用这个类的属性,可以改变所有格式选项,如正号、 百分号、数字组分隔符和货币符号等。从静态属性InvariantInfo 返回一个与区域性无关的只读 NumberFormatInfo 对象。NumberFormatInfo 对象的格式化值取决于当前线程的CultureInfo 类,该线 程从静态属性CurrentInfo 返回。

下一个示例使用一个简单的控制台项目。在这段代码中,第一个示例显示了在当前线程的区域 性格式中所显示的数字(这里是English-US,是操作系统的设置)。第二个示例使用了带有 IFormatProvider 参数的ToString()方法。CultureInfo 类实现IFormatProvider 接口,所以创建一个使用 法国区域性的CultureInfo 对象。第3 个示例改变了当前线程的区域性。使用Thread 实例的 CurrentCulture 属性,把区域性改为德国区域性:

using System;
using System.Globalization;
using System.Threading;

namespace NumberAndDateFormatting
{
class Program
{
    static void Main(string[] args)
    {
        NumberFormatDemo();
    }
    private static void NumberFormatDemo()
    {
        int val = 1234567890;
        // culture of the current thread
        Console.WriteLine(val.ToString("N"));
        // use IFormatProvider
        Console.WriteLine(val.ToString("N", new CultureInfo("fr-FR")));
        // change the culture of the thread
        Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
        Console.WriteLine(val.ToString("N"));
    }
  }
}

结果如下所示。可以把这个结果与前面列举的美国、英国、法国和德国区域性的结果进行比较。

1,234,567,890.00
1 234 567 890,00
1.234.567.890,00


对于日期,也提供了与数字相同的支持。DateTime 结构有一些把日期转换为字符串的方法。公 共实例的ToLongDateString()、ToLongTimeString()、ToShortDateString()和ToShortTimeString()方法都使用当前区域性来创建字符串表示法。使用ToString()方法,可以指定另一种区域性:

public string ToString();
public string ToString(IFormatProvider);
public string ToString(string);
public string ToString(string, IFormatProvider);

使用ToString()方法的字符串参数,可以指定预定义格式字符或自定义格式字符串,把日期转换 为字符串。DateTimeFormatInfo 类指定了可能的值。DateTimeFormatInfo 类指定的格式字符串有不同 的含义。例如,D 表示长日期格式,d 表示短日期格式, ddd 表示星期的缩写,dddd 表示星期的全 称,yyyy 表示年份,T 表示长时间格式,t 表示短时间格式。使用IFormatProvider 参数可以指定区 域性。使用不带IFormatProvider 参数的重载方法,表示所使用的是当前线程的区域性:

DateTime d = new DateTime(2009, 06, 02);
// current culture
Console.WriteLine(d.ToLongDateString());
// use IFormatProvider
Console.WriteLine(d.ToString("D", new CultureInfo("fr-FR")));
// use culture of thread
CultureInfo ci = Thread.CurrentThread.CurrentCulture;
Console.WriteLine("{0}: {1}", ci.ToString(), d.ToString("D"));
ci = new CultureInfo("es-ES");
Thread.CurrentThread.CurrentCulture = ci;
Console.WriteLine("{0}: {1}", ci.ToString(), d.ToString("D"));

这个示例程序的结果说明了使用线程的当前区域性的ToLongDateString()方法,其中给ToString() 方法传递一个CultureInfo 实例,则显示其法国版本,把线程的CurrentCulture 属性改为es-ES,则显 示其西班牙版本,如下所示。

Tuesday, June 02, 2009
mardi 2 juin 2009
en-US: Tuesday, June 02, 2009
es-ES: martes, 02 de junio de 2009

使用区域性

为了全面介绍区域性,下面使用一个WPF 应用程序示例,该应用程序列出所有的区域性,描 述区域性属性的不同特征。下图 显示了该应用程序在Visual Studio 2010 WPF 设计器中的用户界 面。

在应用程序的初始化阶段,所有可用的区域性都添加到应用程序左边的树形视图控件中。这个初 始化在AddCulturesToTree()方法中进行,该方法在Window类CultureDemoWindow的构造函数中调用:

public CultureDemoWindow()
{
    InitializeComponent();
    AddCulturesToTree();
}

在AddCulturesToTree()方法中,从通过静态方法CultureInfo.GetCultures()中获取所有区域性。给 这个方法传递CultureTypes.AllCultures,就会返回所有可用区域性的未排序数组。该数组用一个 Lambda 表达式排序,这个Lambda 表达式要传递给Array.Sort()方法的第二个参数的Comparison 委 托。在foreach 循环中,把每个区域性添加到树形视图中。为每种区域性创建一个TreeViewItem 对 象,因为WPF 的TreeView 类使用TreeViewItem 对象来显示。将TreeViewItem 对象的Tag 属性设置 为CultureInfo 对象,以便以后访问这个树型视图中的CultureInfo 对象。

TreeViewItem 对象添加到树中的什么地方取决于区域性类型。如果区域性没有父区域性,它就 会添加到树的根节点上。要查找父区域性,必须把所有区域性保存到一个字典中。相关内容参见前 面章节,其中第10 章介绍了字典,第8 章介绍了Lambda 表达式。

// add all cultures to the tree view
private void AddCulturesToTree()
{
    var culturesByName = new Dictionary<string, TreeViewItem>();
    // get all cultures
    var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
    Array.Sort(cultures, (c1, c2) = c1.Name.CompareTo(c2.Name));
    var nodes = new TreeViewItem[cultures.Length];
    int i = 0;
    foreach (var ci in cultures)
    {
        nodes[i] = new TreeViewItem();
        nodes[i].Header = ci.DisplayName;
        nodes[i].Tag = ci;
        culturesByName.Add(ci.Name, nodes[i]);
        TreeViewItem parent;
        if (!String.IsNullOrEmpty(ci.Parent.Name) &&culturesByName.TryGetValue(ci.Parent.Name, out parent))
        {
            parent.Items.Add(nodes[i]);
        }
        else
        {
            treeCultures.Items.Add(nodes[i]);
        }
        i++;
    }
}

在用户选择树中的一个节点时,就会调用TreeView 类的SelectedItemChanged 事件的处理程序。 在这里,这个处理程序在TreeCultures_SelectedItemChanged()方法中实现。在这个方法中,先调用 ClearTextFields()方法清除所有字段,再选择TreeViewItem 对象的Tag 属性,从树中获取CultureInfo 对象。接着使用CultureInfo 对象的属性Name、NativeName 和EnglishName 设置一些文本字段。如果 CultureInfo 对象是一个可以使用IsNeutralCulture 属性进行查询的中立区域性,就设置相应的复选框。

private void treeCultures_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e)
{
    ClearTextFields();
    // get CultureInfo object from tree
    CultureInfo ci = (CultureInfo)((TreeViewItem)e.NewValue).Tag;
    textCultureName.Text = ci.Name;
    textNativeName.Text = ci.NativeName;
    textEnglishName.Text = ci.EnglishName;
    checkIsNeutral.IsChecked = ci.IsNeutralCulture;

然后获取区域性的日历信息。CultureInfo 类的Calendar 属性返回特定区域性的默认Calendar 对 象。因为Calendar 类没有对应的名称属性,所以需要使用基类的ToString()方法获取类的名称,并删 除要在文本字段textCalendar 中显示的这个字符串的名称空间。

因为一种区域性可能支持多种日历,所以OptionalCalendars 属性返回额外支持的Calendar 对象 数组。这些可选的日历显示在列表框listCalendars 中。派生自Calendar 的GregorianCalendar 类还有 一个CalendarType 属性,它列出了Gregorian 日历的类型。这个类型可以是GregorianCalendarTypes 枚举的一个值:Arabic、MiddleEastFrench、TransliteratedFrench、USEnglish 或Localized,这取决于 区域性。使用Gregorian 日历,类型还可以显示在列表框中。

// default calendar
textCalendar.Text = ci.Calendar.ToString().
Remove(0, 21).Replace("Calendar", "");
// fill optional calendars
listCalendars.Items.Clear();
foreach (var optCal in ci.OptionalCalendars)
{
    StringBuilder calName = new StringBuilder(50);
    calName.Append(optCal.ToString());
    calName.Remove(0, 21);
    calName.Replace("Calendar", "");
    // for GregorianCalendar add type information
GregorianCalendar gregCal = optCal as GregorianCalendar;
    if (gregCal != null)
    {
        calName.AppendFormat(" {0}", gregCal.CalendarType.ToString());
    }
    listCalendars.Items.Add(calName.ToString());
}

接着,在if 语句中使用“!ci.IsNeutralCulture”,以检查区域性是否为特定区域性(不是中立区域 性)。使用ShowSamples()方法显示数字和日期示例。这个方法将在下一段代码中实现。使用 ShowRegionInformation()方法显示区域的一些信息。对于不变的区域性,只能显示数字和日期示例, 不能显示区域信息。因为不变的文件与实际的语言无关,所以它与区域也无关。

if (!ci.IsNeutralCulture)
{
    groupSamples.IsEnabled = true;
    ShowSamples(ci);
    // invariant culture doesn’t have a region
    if (String.Compare(ci.ThreeLetterISOLanguageName, "IVL", true) == 0)
    {
        groupRegion.IsEnabled = false;
    }
    else
    {
        groupRegion.IsEnabled = true;
        ShowRegionInformation(ci.Name);
    }
}
else // neutral culture: no region, no number/date formatting
  {
    groupSamples.IsEnabled = false;
    groupRegion.IsEnabled = false;
  }
}

为了显示一些本地化的数字和日期,把CultureInfo 类型的选中对象传递给ToString()方法的 IFormatProvider 参数。

private void ShowSamples(CultureInfo ci)
{
    double number = 9876543.21;
    textSampleNumber.Text = number.ToString("N", ci);
    DateTime today = DateTime.Today;
    textSampleDate.Text = today.ToString("D", ci);
    DateTime now = DateTime.Now;
    textSampleTime.Text = now.ToString("T", ci);
}

为了显示与RegionInfo 对象相关的信息,通过在ShowRegionInformation()方法中传递选中的区域性标识符,构造一个RegionInfo 对象,然后访问DisplayName、CurrencySymbol、ISOCurrencySymbol 和IsMetric 属性,以显示这些信息。

private void ShowRegionInformation(string culture)
{
    var ri = new RegionInfo(culture);
    textRegion.Text = ri.DisplayName;
    textCurrency.Text = ri.CurrencySymbol;
    textCurrencyISO.Text = ri.ISOCurrencySymbol;
    checkIsMetric.IsChecked = ri.IsMetric;
}

启动应用程序,在树形视图中就会看到所有的区域性,选择一个区域性后,就会列出该区域性 的特征。

排序

排序字符串取决于区域性。一些区域性有不同的排列顺序。例如在芬兰,字符V 和W 就是相 同的。在默认情况下,为排序而比较字符串的算法要使用与区分区域性的排序方式,其中排序依赖 于区域性。

为了说明芬兰的排序方式,下面的代码创建一个控制台应用程序小示例,其中对数组中尚未排 序的美国州名进行排序。因为我们将使用System.Collections.Generic 、System.Threading 和 System.Globalization 名称空间中的类,所以必须声明这些名称空间。下面的DisplayNames()方法用 于在控制台上显示数组或集合中的所有元素:

static void DisplayNames(string title, IEnumerable<string> e)
{
    Console.WriteLine(title);
    foreach (string s in e)
      Console.Write(s + "—");
    Console.WriteLine();
    Console.WriteLine();
}

在Main()方法中,在创建了包含一些美国州名的数组后,就把线程的CurrentCulture 属性设置 为Finnish 区域性,这样,下面的Array.Sort()方法就使用芬兰的排列顺序。调用DisplayNames()方法 在控制台上显示所有的州名:

static void Main()
{
    string[] names = {"Alabama", "Texas", "Washington",
"Virginia", "Wisconsin", "Wyoming",
"Kentucky", "Missouri", "Utah", "Hawaii",
"Kansas", "Louisiana", "Alaska", "Arizona"};
    Thread.CurrentThread.CurrentCulture =
new CultureInfo("fi-FI");
    Array.Sort(names);
    DisplayNames("Sorted using the Finnish culture", names);

在以芬兰排列顺序第一次显示美国州名后,数组将再次排序。如果希望排序独立于用户的区域 性,就可以使用不变的区域性。在已排序的数组要发送到服务器上,或存储到某个地方时,就可以 采用这种方式。
为此,给Array.Sort()方法传递第二个参数。Sort()方法希望第二个参数是实现IComparer 接口的 一个对象。System.Collections 名称空间中的Comparer 类实现IComparer 接口。 Comparer.DefaultInvariant 返回一个Comparer 对象,该对象使用不变的区域性比较数组值,以进行独 立于区域性的排序。

// sort using the invariant culture
Array.Sort(names, System.Collections.Comparer.DefaultInvariant);
DisplayNames("Sorted using the invariant culture", names);
}

这个程序的输出显示了用Finnish 区域性进行排岸的结果和独立于区域性的排序结果。在使用独 立于文件的排序方式时,Virginia 排在Washington 的前面。用Finnish 区域性进行排序时,Virginia 排在Washington 的后面。

Sorted using the Finnish culture
Alabama-Alaska-Arizona-Hawaii-Kansas-Kentucky-Louisiana-Missouri-Texas-Utah-
Washington-Virginia-Wisconsin-Wyoming -
Sorted using the invariant culture
Alabama-Alaska-Arizona-Hawaii-Kansas-Kentucky-Louisiana-Missouri-Texas-Utah-
Virginia-Washington-Wisconsin-Wyoming -

如果对集合进行的排序应独立于区域性,该集合就必须用不变的区域性进行排 序。在把排序结果发送给服务器或存储在文件中时,这种方法尤其有效。

除了依赖地域的格式化和测量系统之外,文本和图片也可能因区域性的不同而不同。此时就需要使用资源。


阅读原文:原文链接


该文章在 2025/3/24 13:20:49 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved