NSLocale

Mattt Thompson撰写、 Henry Lee翻译、 发布于

产品的国际化就像牙线:所有人都知道他们应该使用,却可能都不去用。

像其他习惯一样,通过不断实践才能使应用国际化这件事变成你开发过程中的天性,以至于习惯之后你都没有办法容许自己不做国际化。而你现在所需的只是一个人教你如何去做。

就让NSHipster成为帮你通过这一片异土的维吉尔牙齿健康专家,保证没有蛀牙哟!

国际化相对于本地化

每当我们讨论国际化(i18n)或者本地化(l10n)的时候,我们有必要弄清楚两者之间的区别:

  • 本地化是将你的应用适应某一个特定市场的操作。
  • 国际化是将你的应用本地化的准备操作。

国际化是本地化的必要不充分条件,它也是这篇文章主要讨论的东西。本地化,包括将一些文本和资源翻译成指定语言的操作,将会在NSHipster之后的内容中涉及到。

让国际化变得困难的是,你需要摆脱你的文化环境去思考。像排序或者分类之类的工作,之前你觉得很微不足道而不想去管,但现在你必须得与这种情绪做斗争了,而且还得重视一些极小差别可能带来的痛苦或者困惑。

不过幸运的是,你不是一个人在战斗,现在来介绍NSLocale:

NSLocale

NSLocale是一个包含这所有地区的语言与文化习俗的基础类。一个NSLocale的实例包含了针对这个地区内特定一群人的所有语言文化基准,其中包括:

  • 语言
  • 键盘
  • 数字、日期和时间格式
  • 货币
  • 排序和分类
  • 符号、颜色与头像的使用

每一个NSLocale实例对应着一个地区标识符,例如en_USfr_FRja_JPen_GB,这些标识符包含一个语言码(例如en代表英语)和一个地区码(例如US代表美国)。

地区标识符还能标识更多更详尽的货币、日历系统或者数字表示的一些使用偏好。例如de_DE@collation=phonebook,currency=DDM就表示了说德语的德国人,文字使用电话本排序,使用的货币在加入欧盟之前是德国马克。

用户可以在“语言和地区”(在早期版本的OS X里是“国际化”)选项里改变他们的系统设置,在iOS里是在“通用>多语言环境”里设置。

Language & Text System Preferences

日期格式和数字

尽管NSLocale包含了大量的不同区域的信息,但它的一般用法还是很保守的。

如果你要从NSLocale里只学一样东西,那就是你需要把[NSLocale currentLocale]传进NSDateFormatterNSNumberFormatter的实例,这样做能确保日期、数字和货币能根据用户设置的地点信息显示正确的格式。

实际上,当你需要展示任何有关日期或者数字的时候,你最好永远用NSDateFormatter或者NSNumberFormatter,这须成为你的一个最基本的知识。

不过还是让我们来看看NSLocale一些更棒的内容吧。

-objectForKey:

NSLocale也有Foundation中对领域专有的迂腐特征,而没有比-objectForKey:这个方法更明显地体现这个迂腐了。这是这个方法里key的常量的列表:

  • NSLocaleIdentifier
  • NSLocaleLanguageCode
  • NSLocaleCountryCode
  • NSLocaleScriptCode
  • NSLocaleVariantCode
  • NSLocaleExemplarCharacterSet
  • NSLocaleCalendar
  • NSLocaleCollationIdentifier
  • NSLocaleUsesMetricSystem
  • NSLocaleMeasurementSystem
  • NSLocaleDecimalSeparator
  • NSLocaleGroupingSeparator
  • NSLocaleCurrencySymbol
  • NSLocaleCurrencyCode
  • NSLocaleCollatorIdentifier
  • NSLocaleQuotationBeginDelimiterKey
  • NSLocaleQuotationEndDelimiterKey
  • NSLocaleAlternateQuotationBeginDelimiterKey
  • NSLocaleAlternateQuotationEndDelimiterKey

尽管这些东西看起来都有些难懂,但如果为了让自己的应用有更好的体验,你会惊讶于你使用这些常数的频率的。

与这些常量相关的配置全都是些小东西,例如你必须知道引号在不同地区其实有不同用法:

英语: “I can eat glass, it doesn't harm me.” 德语: „Ich kann Glas essen, das tut mir nicht weh.“ 日语:「私はガラスを食べられます。それは私を傷つけません。」

所以如果你在实现一个为任意文本加上引号的组件,那么你需要用到NSLocaleQuotationBeginDelimiterKeyNSLocaleAlternateQuotationEndDelimiterKey,而不是仅仅用英文的这个引号@"\""

-displayNameForKey:value:

另一个需要注意的、却也基本没什么用的方法是-displayNameForKey:value:,它返回一个地区标识符用来展示的名字。

NSLocale *frLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"];
NSLog(@"fr_FR: %@", [frLocale displayNameForKey:NSLocaleIdentifier value:@"fr_FR"]);
NSLog(@"en_US: %@", [frLocale displayNameForKey:NSLocaleIdentifier value:@"en_US"]);

fr_FR: français (France) en_US: anglais (États-Unis)

你应该在需要展示用户当前地区或者有其他地区可以选择的情况下使用这个方法,就想iOS设置里展示的一样:

Languages Settings

+preferredLanguages

最后一个值得一提的方法是NSLocale +preferredLanguages,它会根据用户的偏好返回一个IETF BCP 47 语言标识符的字符串数组。

与服务器交互的应用可以用这些值来定义HTTP头里面的Accept-Language字段,而后服务器就能选择相应的本地化资源:

NSMutableURLRequest *request = ...;
[request setValue:[NSString stringWithFormat:@"%@", [[NSLocale preferredLanguages] componentsJoinedByString:@", "]], forHTTPHeaderField:@"Accept-Language"];

即使你的服务器并没有本地化的资源,把这个属性放进去会让你在需要的时候用到,而不必更新客户端。着实干净利落!


国际化通常是一个在编程的时候不怎么性感的话题,也是可能大多数项目都不必考虑的一些杂活。而实际上,为另一个地区来设计软件其实是一种珍贵的经历(而且并不是为了增加你在另一个市场的经济收入)。

编程中有一个最大的乐趣和挑战就是设计出的系统能共经受得住变化,而唯一能让系统设计在某一层次的改变中幸存的方法就是定位并重构系统可能不能总承受住改变的部分。在这个说法下,国际化也代表着一个巨大的挑战,让我们时时对我们的文化身份提出疑问,而通过这样,我们不仅能成为更好的程序员,我们更能成为一个更好的人。

所以,做一个更好的人吧,让NSLocale成为你每天仪式的一部分。