Xamarin.iOS 中的本地化

本文档介绍 iOS SDK 的本地化功能,以及如何使用 Xamarin 访问这些功能

有关在必须处理非 Unicode 数据的应用程序中包含字符集/代码页的说明,请参阅国际化编码

iOS 平台功能

本部分介绍 iOS 中的一些本地化功能。 请跳到下一部分,查看特定代码和示例。

语言

用户在“设置”应用中选择使用的语言。 此设置会影响操作系统和应用中显示的语言字符串和映像。

若要确定应用中使用的语言,请获取 NSBundle.MainBundle.PreferredLocalizations 的第一个元素:

var lang = NSBundle.MainBundle.PreferredLocalizations[0];

此值将作为语言代码,例如英语为 en,西班牙语为 es,日语为 ja 等。返回的值仅限于应用程序支持的本地化之一(使用回退规则来确定最佳匹配)。

应用程序代码并不总是需要检查此值 - Xamarin 和 iOS 都提供有助于自动为用户语言提供正确字符串或资源的功能。 本文档的其余部分将介绍这些功能。

注意

使用 NSLocale.PreferredLanguages 来确定用户的语言首选项,无论应用支持哪种本地化。 此方法返回的值在 iOS 9 中已更改。有关详细信息,请参阅技术说明 TN2418

区域设置

用户在“设置”应用中选择其区域设置。 此设置会影响日期、时间、数字和货币的格式设置方式。

这样用户可以选择查看 12 小时制还是 24 小时制、小数点分隔符是逗号还是点,以及日期显示中的日、月和年的顺序。

借助 Xamarin,可以访问 Apple 的 iOS 类 (NSNumberFormatter) 以及 System.Globalization 中的 .NET 类。 开发人员应该评估哪种类更适合他们的需求,因为每个类都有不同的功能。 具体而言,如果要使用 StoreKit 检索和显示应用内购买价格,则应使用 Apple 的格式设置类来返回价格信息。

可以通过以下两种方式之一查询当前区域设置:

  • NSLocale.CurrentLocale.LocaleIdentifier
  • NSLocale.AutoUpdatingCurrentLocale.LocaleIdentifier

操作系统缓存第一个值,因此可能并不总是反映用户当前选择的区域设置。 使用第二个值获取当前选择的区域设置。

注意

Mono(Xamarin.iOS 所基于的 .NET 运行时)和 Apple 的 iOS API 不支持相同的语言/区域组合集。 因此,可以在 iOS“设置”应用中选择不映射到 Mono 中有效值的语言/区域组合。 例如,将 iPhone 语言设置为英语,将区域设置为西班牙将导致以下 API 产生不同的值:

  • CurrentThead.CurrentCulture:en-US (Mono API)
  • CurrentThread.CurrentUICulture:en-US (Mono API)
  • NSLocale.CurrentLocale.LocaleIdentifier:en_ES (Apple API)

由于 Mono 使用 CurrentThread.CurrentUICulture 选择资源,使用 CurrentThread.CurrentCulture 设置日期和货币格式,因此基于 Mono 的本地化(例如,使用 .resx 文件)可能无法为这些语言/区域组合产生预期结果。 在这些情况下,必要时请依靠 Apple 的 API 进行本地化。

NSCurrentLocaleDidChangeNotification

当用户更新其区域设置时,iOS 会生成一个 NSCurrentLocaleDidChangeNotification。 应用程序可以在运行时侦听此通知,并对 UI 进行适当的更改。

iOS 中的本地化基础知识

在 Xamarin 中可以轻松利用 iOS 的以下功能,以提供要向用户显示的本地化资源。 请参阅 TaskyL10n 示例,了解如何实现这些想法。

在 Info.plist 中指定默认语言和支持的语言

技术问答 QA1828:iOS 如何确定应用的语言中,Apple 描述了 iOS 如何选择在应用中使用的语言。 以下因素会影响显示的语言:

  • 用户的首选语言(在“设置”应用中找到)
  • 与应用捆绑的本地化(.lproj 文件夹)
  • CFBundleDevelopmentRegion(指定应用的默认语言的 Info.plist 值)
  • CFBundleLocalizations(指定所有受支持的本地化的 Info.plist 数组)

如技术问答中所示,CFBundleDevelopmentRegion 代表应用的默认区域和语言。 如果应用未显式支持用户的首选语言,将使用此字段指定的语言。

重要

与以前版本的操作系统相比,iOS 11 更严格地应用这种语言选择机制。 因此,任何未显式声明其支持的本地化的 iOS 11 应用(通过包含 .lproj 文件夹或设置 CFBundleLocalizations 的值)可能会在 iOS 11 中显示与 iOS 10 中不同的语言。

如果 Info.plist 文件中未指定 CFBundleDevelopmentRegion,则 Xamarin.iOS 生成工具当前使用 en_US 的默认值。 虽然在未来的版本中此默认值可能会更改,但这意味着默认语言为英语。

若要确保应用选择预期语言,请执行以下步骤:

  • 指定默认语言。 打开 Info.plist 并使用“源”视图设置 CFBundleDevelopmentRegion 键的值;在 XML 中应如下所示
<key>CFBundleDevelopmentRegion</key>
<string>es</string>

此示例使用“es”指定在不支持用户的首选语言时,默认为西班牙语。

  • 声明所有受支持的本地化。 在 Info.plist 中,使用“源”视图设置 CFBundleLocalizations 键的数组;在 XML 中应如下所示
<key>CFBundleLocalizations</key>
<array>
    <string>en</string>
    <string>es</string>
    ...
</array>

已使用 .NET 机制(例如 .resx 文件)本地化的 Xamarin.iOS 应用也必须提供这些 Info.plist 值

有关这些 Info.plist 键的详细信息,请查看 Apple 信息属性列表键参考

GetLocalizedString 方法

NSBundle.MainBundle.GetLocalizedString 方法查找项目的 .strings 文件中已存储的本地化文本。 这些文件按语言组织在带有 .lproj 后缀的专门命名的目录中(注意扩展名的第一个字母是小写“L”)

.strings 文件位置

  • Base.lproj 是包含默认语言资源的目录。 它通常位于项目根目录中(但也可以放置在 Resources 文件夹中)
  • <language> .lproj 目录是为每种支持的语言创建的,通常位于 Resources 文件夹中

每个语言目录中可以有多个不同的 .strings 文件

  • Localizable.strings – 本地化文本的主列表
  • InfoPlist.strings – 该文件中允许使用某些特定键来翻译应用程序名称等内容
  • <storyboard-name>.strings – 可选文件,包含情节提要中用户界面元素的翻译

这些文件的“生成操作”应为“捆绑资源”

.strings 文件格式

本地化字符串值的语法为:

/* comment */
"key"="localized-value";

应转义字符串中的以下字符:

  • \" 报价
  • \\ 反斜杠
  • \n 换行符

以下是示例中的 es/Localized.strings(即西班牙语)文件示例

"<new task>" = "<new task>";
"Task Details" = "Detalles de la tarea";
"Name" = "Nombre";
"task name" = "nombre de la tarea";
"Notes" = "Notas";
"other task info"= "otra información de tarea";
"Done" = "Completo";
"Save" = "Guardar";
"Delete" = "Eliminar";

映像

在 iOS 中本地化映像:

  1. 参考代码中的映像,例如:

    UIImage.FromBundle("flag");
    
  2. 将默认映像文件 flag.png 放置在 Base.lproj(本机开发语言目录)中

  3. (可选)将映像的本地化版本放置在每种语言的 .lproj 文件夹中(例如 es.lproj、ja.lproj)。 在每个语言目录中使用相同的文件名 flag.png

如果特定语言没有映像,iOS 将回退到默认的本机语言文件夹,并在此处加载映像。

启动映像

将启动映像(以及适用于 iPhone 6 型号的 XIB 或情节提要)放置在每种语言的 .lproj 目录中时,请使用启动映像的标准命名约定

Default.png
Default@2x.png
Default-568h@2x.png
LaunchScreen.xib

应用名称

将 InfoPlist.strings 文件放置在 .lproj 目录中可以替代应用的 Info.plist 中的某些值,包括应用程序名称

"CFBundleDisplayName" = "LeónTodo";

可用于本地化特定于应用程序的字符串的其他键包括:

  • CFBundleName
  • CFBundleShortVersionString
  • NSHumanReadableCopyright

日期和时间

尽管可以使用内置的 .NET 日期和时间函数(以及当前的 CultureInfo)设置区域设置的日期和时间格式,但这会忽略特定于区域设置的用户设置(可以与语言分开设置)。

使用 iOS NSDateFormatter 生成与用户的区域设置首选项匹配的输出。 以下代码示例演示了基本日期和时间格式设置选项:

var date = NSDate.Now;
var df = new NSDateFormatter ();
df.DateStyle = NSDateFormatterStyle.Full;
df.TimeStyle = NSDateFormatterStyle.Long;
Debug.WriteLine ("Full,Long: " + df.StringFor(date));
df.DateStyle = NSDateFormatterStyle.Short;
df.TimeStyle = NSDateFormatterStyle.Short;
Debug.WriteLine ("Short,Short: " + df.StringFor(date));
df.DateStyle = NSDateFormatterStyle.Medium;
df.TimeStyle = NSDateFormatterStyle.None;
Debug.WriteLine ("Medium,None: " + df.StringFor(date));

英语(美国)的结果:

Full,Long: Friday, August 7, 2015 at 10:29:32 AM PDT
Short,Short: 8/7/15, 10:29 AM
Medium,None: Aug 7, 2015

西班牙语(西班牙)的结果:

Full,Long: viernes, 7 de agosto de 2015, 10:26:58 GMT-7
Short,Short: 7/8/15 10:26
Medium,None: 7/8/2015

有关详细信息,请参阅 Apple 日期格式化程序文档。 测试区分区域设置的日期和时间格式设置时,请检查“iPhone 语言”和“区域”设置

从右到左 (RTL) 布局

iOS 提供了许多功能来帮助生成 RTL 感知应用:

  • 使用自动布局的 leadingtrailing 属性进行控件对齐(对应于英语的左和右,但对于 RTL 语言则相反)。 UIStackView 控件特别适用于将控件布局为 RTL 感知。
  • 使用 TextAlignment = UITextAlignment.Natural 进行文本对齐(对于大多数语言为左对齐,但对于 RTL 语言为右对齐)。
  • UINavigationController 自动翻转后退按钮并反转滑动方向。

以下屏幕截图显示了阿拉伯语和希伯来语的本地化 Tasky 示例(尽管已在字段中输入英语):

Localization in Arabic

Localization in Hebrew

iOS 会自动反转 UINavigationController,而其他控件则放置在 UIStackView 内部或与自动布局对齐。 RTL 文本使用与 LTR 文本相同的方式通过 .strings 文件进行本地化

在代码中本地化 UI

Tasky(在代码中本地化)示例演示如何本地化用户界面在代码(而不是 XIB 或情节提要)中内置的应用程序

项目结构

Screenshot shows the resources tree for a sample including the location of localizable strings.

Localizable.strings 文件

如上所述,Localizable.strings 文件格式由键值对组成。 键描述字符串的意向,值是应用中使用的已翻译文本。

西班牙语 (es) 本地化示例如下所示

"<new task>" = "<new task>";
"Task Details" = "Detalles de la tarea";
"Name" = "Nombre";
"task name" = "nombre de la tarea";
"Notes" = "Notas";
"other task info"= "otra información de tarea";
"Done" = "Completo";
"Save" = "Guardar";
"Delete" = "Eliminar";

执行本地化

在应用程序代码中,无论在何处设置用户界面的显示文本(无论是标签的文本,还是输入的占位符等),代码都会使用 iOS GetLocalizedString 函数检索要显示的正确翻译:

var localizedString = NSBundle.MainBundle.GetLocalizedString ("key", "optional");
someControl.Text = localizedString;

本地化情节提要 UI

Tasky(本地化情节提要)示例演示如何本地化情节提要中的控件上的文本。

项目结构

Base.lproj 目录包含情节提要,还应包含应用程序中使用的任何映像

其他语言目录包含代码中引用的任何字符串资源的 Localizable.strings 文件,以及包含情节提要文本翻译的 MainStoryboard.strings 文件

Screenshot shows the resources tree for a sample including the location of MainStoryboard strings.

语言目录应包含已本地化的任何映像的副本,以替代 Base.lproj 中存在的映像

对象 ID/本地化 ID

在情节提要中创建和编辑控件时,选择每个控件并检查要用于本地化的 ID:

  • 在 Visual Studio for Mac 中,它位于 Properties Pad 中,称为“本地化 ID”
  • 在 Xcode 中,它称为“对象 ID”

此字符串值通常具有“NF3-h8-xmR”等格式,如以下屏幕截图所示:

Xcode view of Storyboard localization

此值在 .strings 文件中用于自动将翻译的文本分配给每个控件

MainStoryboard.strings

情节提要翻译文件的格式类似于 Localizable.strings 文件,不同之处在于键(左侧的值)不能是用户定义的,而必须具有非常特定的格式:ObjectID.property

在下面的示例 Mainstoryboard.strings 中,可以看到 UITextField 具有可以本地化的 placeholder 文本属性;UILabel 具有 text 属性;UIButton 使用 normalTitle 设置默认文本

"SXg-TT-IwM.placeholder" = "nombre de la tarea";
"Pqa-aa-ury.placeholder"= "otra información de tarea";
"zwR-D9-hM1.text" = "Detalles de la tarea";
"bAM-2j-Rzw.text" = "Notas";           /* Notes */
"NF3-h8-xmR.text" = "Completo";        /* Done */
"MWt-Ya-pMf.normalTitle" = "Guardar";  /* Save */
"IGr-pR-05L.normalTitle" = "Eliminar"; /* Delete */

重要

将情节提要与大小类一起使用可能会导致应用程序中不显示翻译。 Apple 的 Xcode 发行说明表明,如果满足以下三个条件,情节提要或 XIB 将无法正确本地化:使用大小类、基本本地化和生成目标设置为通用、生成目标为 iOS 7.0。 解决方法是将情节提要字符串文件复制到两个相同的文件中:MainStoryboard~iphone.strings 和 MainStoryboard~ipad.strings,如以下屏幕截图所示

Strings files

App Store 一览

按照 Apple 关于 App Store 本地化的常见问题解答,为应用在售的每个国家/地区输入翻译。 请注意,仅当应用还包含语言的本地化 .lproj 目录时,才会显示翻译。

总结

本文介绍使用内置资源处理和情节提要功能本地化 iOS 应用程序的基础知识。

可在此跨平台指南中了解有关 iOS、Android 和跨平台应用(包括 Xamarin.Forms)的 i18n 和 L10n 的详细信息。