clrprofiler:CLR 全面透彻解析:国际应用程序

  本专栏基于 Silverlight 预发布版文中所有信息均有可能发生变更

CLR 全面透彻解析:国际应用<img src='/icons/7516chengxu.gif' />目录

  CLR 本地化

  使用文本

  Silverlight 全球化

  大多数都会向用户呈现某种类型数据很明显Windows Presentation Foundation (WPF) 或 Windows 窗体应用等具有 GUI 应用已被设计为可以向用户提供信息当然简单控制台应用也可以显示文本全球化过程力图确保您应用可以处理来自区别文化数据它可能会非常简单如确保您日历标题足够宽以便能够处理夏威夷月份名称或者使用您用户所期望负数格式

  全球化区别于本地化后者实际上是将应用资源翻译为区别语言即使只使用种语言应用也需要考虑全球化问题

  下面举例展示了这些变化是如何产生(甚至是在尚未翻译应用中)某些类型数据(例如日期)可通过多种方式进行格式化图 1 只列出了几种在月份和星期几中使用英文名称格式所有条目都代表相同数据段但是如果用户对日期格式有特定预期他们可以通过控制面板中设置来指明其个人喜好因此如果您 DateTime.Now.则可能会返回许多如果您尚未指定格式 会自动根据用户个人喜好进行选择

CLR 全面透彻解析:国际应用<img src='/icons/7516chengxu.gif' />   图 1 某些日期格式

  您必须知道各个部分都代表什么否则日期串 "1/2/03" 毫无意义您会认为了解格式串会有助于您解释数据含义但即使这样也不能说万无并非所有人都使用相同历法实际上.Globalization 命名空间当前支持 15 种区别历法因此将串 "6/26/1980" 解释为 "M/d/yy" 可能在公历和儒略历中代表区别日期(有很多网站WebSite可以将日期转换为多种历法)

  非常有趣同样解释问题也出现在数字中有些用户可能希望使用句点作为小数分隔符而其他人却习惯于使用逗号因此某个用户希望看到 1.23而另个可能希望看到 1,23尤其是在处理负数时情况会变得更为复杂如果看.Globalization.NumberFormatInfo.NumberNegativePattern您会注意到 Microsoft .NET Framework 对于负号放置位置提供了 5 种模式这只是针对种符号对于负货币值它在 NumberFormatInfo.CurrencyNegativePattern 中存储了 16 种区别模式

  您可能已经猜到在将数据存储为串时始终应明确指出要遵循格式并且要在 和 Parse 思路方法中均采用该格式请记住如果没有上下文有些含义可能会含混不清当然所有这些繁杂格式化问题完全都出于显示目;最好思路方法是对存储数据选择种仅由机器读取固定表示形式Framework 通过 CultureInfo.InvariantCulture 提供了种解决此问题机制

  在如此众多格式化选项中如何才能知道应该选择哪种呢?幸运.NET Framework 提供了种基础结构可用于格式化要显示数据正如您所看到用户个人喜好可以自动被遵循但在用户未指定情况下Framework 该如何决定使用哪种方式呢?

  让我们仔细看.Globalization 命名空间这会帮助我们解答这个问题.Globalization 内容被用于描述有关文化信息我们已经知道它包含 15 种历法和 16 种格式化负货币值思路方法此外它还有其他些特定于文化时间和数字属性以及用于存储语言和区域相关信息它还包含种机制可以组织和分组此信息以供使用该机制是 CultureInfo 类其中存储着有关文化信息包括数字格式化、日期格式化、历法、文化名称和书写系统相关信息

  CultureInfo 对象是使用某种特定文化标识符创建例如为了创建个在芬兰区域设置中使用时包含有关瑞典区域设置信息对象您会在构造中指定名称 "sv-FI"Framework 包括许多有关文化信息Windows 还包含用于各种区域设置数据除此的外用户还可以创建他们自己自定义文化

  CultureInfo 可实现 IformatProvider这是 可以视为日期和数字参数类型让我们回溯到原来 DateTime.Now. 举例上如果希望使用和 sv-FI 文化相关联默认格式来格式化日期您可以编写下列代码:

DateTime.Now.( CultureInfo("sv-FI"));

  如果没有用户改写 sv-FI 且讨论日期恰好仍为 "6/26/1980"则日期将输出为 "26.6.1980"后跟时间如果想得到和 sv-FI 相关联长日期格式可编写如下代码:

CultureInfo ci = CultureInfo("sv-FI");
Console.WriteLine(DateTime.Now.(ci.DateTimeFormat.LongDatePattern, ci));


  这将得到串 "den 26 juni 1980"请注意只包括 DateTimeand 日期部分 LongDatePattern 将忽略时间这些举例的所以正常执行是使用了存储在 CultureInfo 对象中格式化信息

  这阐明了传入特定格式化信息时 工作方式但正如您在文章最开头所看到有许多 重载思路方法它们根本未收到任何格式化信息但数据却仍被格式化了那么这些信息是从哪里来呢?

  CultureInfo 类个静态属性是 CurrentCulture这将返回和当前线程相关联文化此属性可通过 Thread.CurrentThread.CurrentCulture 加以设置线程创建完毕后它们会默认使用操作系统当前文化如果没有为 或 Parsing 操作指定格式化信息则使用 CultureInfo.CurrentCulture 中信息

  文化信息通常会发生改变某个国家/地区可能会采用欧元、更改时区或添加其他日期格式所以即使您指定了某种文化数据仍可能随着区别使用场合发生变更请记住全球化信息仅用于显示如果您需要将数据存储为CultureInfo InvariantCulture 属性是个不错选择

  CLR 本地化

  如果您打算将您应用本地化为多种语言具体该如何办?此过程可能涉及 .Globalization 中处理所有数据显示功能以及应用各个部件翻译您可能还打算针对区别文化显示区别图像为此您可以制作应用多个副本每个都对应种产品本地化目标语言

  但这种思路方法效率会很低幸运Framework 提供了种思路方法它可以通过 .Resources 命名空间中某些类来提取这些可本地化资源用于创建、读取和使用这些资源主类分别是 ResourceWriter、ResourceReader 和 ResourceManager

  ResourceWriter 类允许您以名称和值对形式存储资源当您创建 ResourceWriter 例子时它是用于特定文件这些文件命名约定为 [basename].[culturename].resources"Basename" 用于组织您资源它是应用或类名称您可以使用 CultureInfo.Name 属性来查找文化名称

  例如举例应用 MySampleApplication 美国英语资源将被放在文件 "MySampleApplication.en-US.resources" 中此外应用应有个默认资源文件用于名为 [basename].resources 中性文化通常最好将资源文件打包到附属集中以便能够独立于主集来制定本地化数据版本但是最好在主集中始终都包括组资源和中性资源以便在查找资源时至少能找到

  ResourceReader 类允许您枚举资源文件中成对名称/值但是最常见工作通常是针对某特定文化来查找特定资源这是通过 ResourceManager 类实现您可以使用 GetObject 和 GetString 思路方法访问单独资源或者也可以使用 GetResourceSet 思路方法将某特定文化所有资源加载到 Hashtable 中

  如同全球化信息资源也是按文化存储而就像可以使用默认文化来配置全球化ResourceManager 也可以在其默认文化中使用 CurrentCulture.CurrentUICulture同样这是当前线程属性CurrentCulture 和 CurrentUICulture 通常是相但它们管理对象区别CurrentCulture 控制数据格式化而 CurrentUICulture 可确定加载资源

  Framework 包含 .Globalization 中类所需大部分数据而应用控制着它自己资源如果应用未本地化为某种特定文化这些资源在该文化中将不可用般而言您不希望在查询资源时出错这就是首先要在主集中包括中性资源原因 — 应用始终都可以返回组资源

  CultureInfo 个未被提及属性是 Parent 属性文化信息按层次结构排列其中中性文化(即未和特定区域相关联文化)是和某特定区域相关联文化父项例如en-US、en-CA 和 en-UK 父项是中性文化 en如果某种文化资源未找到ResourceManager 会探查父链直到找到种为止这最后将以 InvariantCulture 结束(InvariantCulture 是嵌入在主集中资源)

  利用文化层次结构您可以将公共资源从特定文化中提取出来并嵌入到中性文化中让我们举例来介绍说明假如我打算将我应用本地化为 en-US、en-CA 和 en-UK除了少许差异外这些文化所需许多资源可能都是相同在这种情况下我可以将所有公共资源存储在个文件中用于中性文化 en而仅将差异内容放在资源文件中用于特定文化

  如果您有如图 2 所述资源而您试图查找和名称 "greeting" 相对应资源则 en-US 会返回 "hi"而 en-UK 和 en-CA 都将返回 en 资源 "hello"

CLR 全面透彻解析:国际应用<img src='/icons/7516chengxu.gif' />   图 2 资源层次结构

  使用文本

  文本引入了组全新问题例如如何实际呈现在将文本翻译为其他语言时可能需要用到所有串在 .NET Framework 中是通过 .String 类来表示幸运CLR 中串使用是 Unicode(更具体点说是 UTF-16 编码)因此它们可以表示标准

  .Text 命名空间包括些类它们可用于将 Unicode 编码为字节将字节解码为这将允许您根据需要在区别 Unicode 编码的间进行转换

  虽然 Unicode 是目前标准但也存在某些先前编码它们仅表示在特定语言或区域中使用这些编码被称为“代码页”如果需要处理使用其中种旧代码页编码文本可以使用 .Text 类进行处理般而言只有在处理些旧应用时才需要这样做新应用使用应该是 Unicode 标准

  有些串可能只在控制台上或在标签中输出但通常您都会希望对串执行某些操作或许采用串列表形式会更易于读取(如果它已存储在显示装置中)有时您会希望将串中转换为区别大小写格式这些操作或许看上去非常简单但实情却可能并非如此对单词进行排序时就存在个根本问题它并不会眼就被发现例如在英语中有个由 26 个组成字母表各字母具有固定顺序却有多种大小写形式应该如何排列具有区别大小写形式相同单词区别例子呢?

  Framework 提供 3种串比较模式序数、单词以及序数比较依据是待比较每个数值这意味着在基于 String.CompareOrdinal 排序中"alphabet" 会大于 "Alphabet"默认比较方式是单词比较这种方式是区分文化可能会将某些非字母数字(如连)视为特例它可能会赋予这些很小权重或重要性因此举例而言"a-lphabet" 和 "alphabet" 在排序时彼此靠近串比较也区分文化但是它没有特例非字母数字均排在字母数字的前

  如果要使用 String.Compare 对 en-US 进行区分文化比较您可能会发现 "Alphabet" 大于 "alphabet"如果对其进行忽略大小写序数比较它们会相等.Globalization.CompareOptions 包括 9种区别选项它们指出了在排序时哪些参数会被纳入考虑范畴

  每种文化都有其自己集和排序顺序有些甚至有不止例如es-ES即西班牙语(西班牙)在默认情况下使用国际化排序但也可以选择使用传统排序顺序zh-CN即中文(中国)在默认情况下按拼音排序但也可以按笔画数排序Framework 中每个 CultureInfo 对象都有个 CompareInfo 属性可用来对串进行比较Framework 中存储着此数据

  般而言对于排序和显示更适合使用区分文化排序但是在测试串是否相等时尤其是在某些可能会涉及安全隐患情况下序数比较无疑是最佳选择序数比较只关注待比较值;因此它始终是而区分文化比较则可能会由于使用文化区别而得到区别结果

  使用区分文化串比较思路方法时最常被引用个涉及安全原因问题就是“土耳其语 I 问题”在大多数拉丁字母集中 i (u0069) 只是 I (u0049) 小写形式多数人在使用这些字母集时根本没有意识到其中可能会有任何变体因此都会将其视为默认行为但是在土耳其语中 (tr-TR)该映射却是 i 大写形式是 İ (u0130)也就是带个点大写字母 I类似地 I 小写形式是 ı (u0131)也就是没有点小写字母 i

  假设您想检查下 URI(统资源标识符)是否以串 "FILE:" 开头您希望在检查时区分大小写以确保 "file:" 等词语不会通过过滤器过滤如果使用 en-US 文化对 "file:" 和 "FILE:" 进行比较并忽略大小写则它们将会是相等但如果使用 tr-TR 文化则它们是不等String.Compare 默认使用 CurrentCulture

  因此对于这些情况您应使用 StringComparison.OrdinalIgnoreCase如果只是想测试是否相等则更好选择是使用 String.Equals默认情况下它按序数进行比较图 3 给出了字母 I 在执行 ToUpper 和 ToLower 操作后所得到结果有关比较详细信息请参阅“在 Microsoft .NET 2.0 中使用新建议”

CLR 全面透彻解析:国际应用<img src='/icons/7516chengxu.gif' />   图 3 ToUpper 和 ToLower 结果

  Silverlight 全球化

  全球化对 Silverlight 应用尤为重要使用桌面应用您可以确切知道您目标受众都包括哪些人但是对于 Web 上 Silverlight 应用任何人都可能看到它Silverlight 有许多和桌面 Framework 相同全球化基础结构但是它大部分数据都从底层操作系统获取这有助于使 Silverlight 应用在 Windows 中运行时看上去更像是基于 Windows 应用而当 Silverlight 应用在 Mac 操作系统中运行时看上去更像是 Mac 操作系统应用

  桌面 CLR 和 Silverlight (CoreCLR) 的间许多差异都基于下载速度和难易度等实际情况Silverlight 需要更小运行时同样Silverlight 许多信息都从底层操作系统获取而并非在 CoreCLR 中时始终附带着它们例如CoreCLR 并不存储排序表因此它只能访问操作系统中已有排序表此外Silverlight 只使用 Unicode它并没有旧代码页它还会从操作系统中获取文化信息这意味着您确需要做好不得不处理各种数据准备

  直以来都不容回避个问题是如果数据依赖于自定义文化或操作系统文化则该数据可能无法使用但是如果所有数据都来自操作系统 Silverlight 应用很可能会遇到区别数据确保您目标操作系统支持您要使用 UI 文化还要确保当所请求文化不存在时应用也能以合理方式运行这是种理想状态

  文化数据已被融入到 .NET Framework 中因此很难在数据发生变更时及时得到更新这将导致数据可能会过期还可能会导致本机应用和托管应用显示数据区别在 Silverlight 中您可以使这 2者保持应用可以充分利用更新操作系统信息

  使用操作系统数据还允许您充分利用更多其他信息例如在桌面版本 .NET Framework 中每个 CultureInfo 都有单父项文化要查询资源所有 ResourceManager 需要做只是遍历遍父链

  假如有个应用其中包含 de-DE 资源和 fr-FR 资源现在我们假设有个用户使用 es-ES 作为主文化但他也懂得德语可以阅读 de-DE 资源ResourceManager 会退回到固定文化即使存在用户可以理解资源如今大多数操作系统都允许用户指定多种语言首选项在 Silverlight 中我们修改了 ResourceManager 以便在退回到固定文化的前遵循这些首选项

  Silverlight 在串比较方面还包含些和安全性而非大小相关差异在桌面 Framework 中大多数涉及串比较操作都使用 CurrentCulture 作为默认设置而在 Silverlight 中涉及部分匹配操作(包括 String.IndexOf 和 String.StartsWith)现在默认使用序数比较Compare 和 CompareTo 默认仍使用 CurrentCulture但是对于所需特定行为您应认真考虑使用是否是正确重载ToUpper 和 ToLower 现在默认使用 InvariantCultureEquality 仍使用序数比较



  请将您想询问问题和提出意见发送至 [email protected]

  Melitta Andersen 是 CLR 基类库团队(Team)项目经理(project manager)她主要负责本专栏中详述有关数字、集合以及某些基础结构工作



Tags:  clrerror80004005 clrcmos clrde clrprofiler

延伸阅读

最新评论

发表评论