首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》9.10 本地化

关灯直达底部

用户可以在设备上将某种语言作为其主语言。你可能希望应用界面的文本能够对用户的选择做出响应,从而以用户所选的语言来显示。这是通过应用语言的本地化来实现的。你可能会在应用生命周期相对较晚的时间(应用已经开发完毕,准备发布)实现本地化。

图9-15:钻取到时间表

图9-16:对我的代码进行时间分析

本地化是通过项目目录与构建好的应用包中的本地化目录来实现的。假设一个本地化目录中的资源在另外一个本地化目录中也有对应的一份。在应用加载该资源时,它会自动加载适合用户首选语言的那个。

任何类型的资源都可以放在这些本地化目录中;比如,一种语言会加载图片的一个版本,而另一种语言会加载这张图片的另一个版本。不过,你最应该关心的还是显示在界面上的文本。这些文本需要以特殊的格式化.strings文件的形式进行维护,并带有特殊的名字。比如:

·使用InfoPlist.strings来本地化Info.plist文件。

·使用Main.strings来本地化Main.storyboard。

·使用Localizable.strings来本地化代码字符串。

无须再手工创建或维护这些文件了。相反,可以通过标准的.xliff格式使用导出的XML文件。Xcode会根据项目的结构与内容自动生成这些文件;还会读取它们,并将其自动转换为各种本地化的.strings文件。

为了帮助你理解.xliff导出与导入过程的工作方式,首先介绍如何手工创建和维护.strings文件;接下来介绍如何通过.xliff文件完成同样的事情。我将使用之前的Empty Window项目作为该示例的基础。

9.10.1 本地化Info.plist

首先本地化应用图标下Springboard中的字符串,这也是应用的名字。该字符串是Info.plist文件中CFBundleDisplayName键的值。如果Info.plist文件中没有CFBundleDisplayName键,那就先要创建一个:

1.编辑Info.plist文件。

2.选中“Bundle name”,然后单击右侧的“+”按钮。

3.这时会出现一个新的条目。在弹出菜单中选择“Bundle display name”。

4.输入“Empty Window”作为新值并保存。

现在本地化该字符串:如果设备的语言是法语,那么我们需要在Springboard中显示出不同的字符串。Info.plist该如何本地化呢?它依赖于另外一个文件,默认情况下应用模板并不会创建这个文件:InfoPlist.strings。因此,需要创建这个文件:

1.选择File→New→File。

2.选择iOS→Resource→Strings File,单击Next按钮。

3.确保该文件属于应用目标,将其命名为InfoPlist,注意名字与大小写,单击Create按钮。

4.这时,一个名为InfoPlist.strings的文件会出现在项目导航器中。将其选中,在文件查看器中单击Localize按钮。

5.这时会弹出一个对话框,让我们选择初始语言。默认是Base,这就可以,单击Localize按钮。

现在准备添加语言!下面是具体步骤:

1.编辑项目。在Info下,Localizations表格列出了应用的本地化信息。我们一开始只对开发语言做了本地化(我选择的是英语)。

2.单击Localizations表格下方的“+”按钮。从弹出的菜单中选择French。

3.这时会弹出一个对话框,列出了当前已经针对英语进行了本地化的文件(因为它们是应用模板的一部分)。我们这里只操作InfoPlist.strings,因此勾选上它,同时不要勾选其他的文件,单击Finish按钮。

我们现在已经创建了InfoPlist.strings供英语与法语本地化使用。在项目导航器中,InfoPlist.strings的清单已经有了一个三角箭头。展开这个三角箭头,我们会看到项目现在包含了InfoPlist.strings的两个副本,一个用于Base(即英语),一个用于法语。现在就可以分别编辑这两个文件了。

下面编辑InfoPlist.strings文件。.strings文件是个键值对的集合,其格式如下所示:


/* Optional comments are C-style comments *//"key/" = /"value/";  

对于InfoPlist.strings,键是Info.plist中的键名,即原始的键名而不是类似于英语的那个名字。这样,英语的InfoPlist.strings文件应该如下所示:


/"CFBundleDisplayName/" = /"Empty Window/";  

法语的InfoPlist.strings应该如下所示:


/"CFBundleDisplayName/" = /"Fenêtre Vide/";  

就是这些!下面来试一下:

1.在模拟器中构建并运行Empty Window。

2.在Xcode中,停止运行着的项目。在模拟器中会显示出主界面。

3.查看应用的名字,它显示在模拟器主界面中(Springboard),名字是Empty Window(也许会有截断)。

4.在模拟器中,打开设置应用,将语言修改为French(General→Language & Region→iPhone Language→Fran鏰is),单击Done按钮。系统会提示我们是否要修改为French。确定。

5.短暂的停顿之后,语言就会改变。关掉设置应用,再次在Springboard中查看应用。其名字现在已经显示为了Fenêtre Vide!

有意思吧?操作之后,请将模拟器的语言改回到English。

9.10.2 本地化nib文件

现在介绍一下如何本地化nib文件。曾经,我们需要本地化整个nib副本。比如,如果需要法语版本的nib文件,你就需要维护两个单独的nib文件。如果在一个nib文件中创建了一个按钮,那就需要在另一个nib文件中创建一个相同的按钮——只不过一个按钮上的文字是英语,另一个是法语。诸如此类,每个界面对象与每个本地化语言都如此。看起来太枯燥了吧?

时至今日,我们已经有了更好的方式。如果项目使用了基础国际化,那么Base.lproj目录中创建的nib文件与本地化目录中创建的.strings文件之间就可以形成一种对应关系。这样,开发者只需维护一个nib文件副本即可。如果应用所运行的设备的本地化语言有对应的.strings文件,那么.strings文件中的字符串就会替换掉nib文件中的字符串。

在默认情况下,Empty Window项目会使用基础国际化,其Main.storyboard文件位于Base.lproj目录中。我们准备将故事板文件本地化为法语。你还需要对故事板文件做些操作才能进行本地化:

1.编辑Main.storyboard,确保初始主视图包含一个按钮,按钮上的文字为/"Hello/"。如果没有就添加一个。将按钮宽度设为100像素,保存(这很重要)。

2.继续编辑Main.storyboard,打开文件查看器。在Localization下,Base应该已经勾选了。此外,勾选French。

3.在项目导航器中,查看Main.storyboard列表。它现在应该有一个小三角,展开这个小三角。当然,现在应该会有一个基于Base的本地化Main.storyboard与一个基于French的本地化Main.strings。

4.编辑French Main.strings。它会自动创建出来,其键对应于Main.storyboard中每个有文本的界面元素。你需要从注释与键名中推断出这种对应关系。对于这个示例来说,Main.storyboard中只有一个界面元素,因此很容易就能猜出来键代表的是哪个界面元素。它应该如下所示:


/* Class = /"UIButton/"; normalTitle = /"Hello/"; ObjectID = /"PYn-zN-WlH/"; *//"PYn-zN-WlH.normalTitle/" = /"Hello/";  

5.将第2行(包含键值对这一行)的值修改为“Bonjour”。不要修改键!它是自动生成的,也是正确无误的,用于指定值与按钮文本之间的对应关系。

运行项目并查看界面。由于现在是在查看自己的应用,有一个更快的方式可以在各种本地化语言中查看:相对于切换设备语言,可以切换应用语言。要做到这一点,请编辑方案,在运行动作的Options页签中修改应用语言弹出菜单。当然,当应用使用法语时,按钮上的文本显示为/"Bonjour/"!

如果修改nib会出现什么结果呢?假设在Main.storyboard中向视图再添加一个按钮。这时与nib对应的.strings文件不会发生任何变化;我们需要手工重新生成这些文件(这也是在实际情况下,为何要在界面开发工作基本完成时才开始本地化nib文件的原因所在)。不过内容并未丢失:

1.选中Main.storyboard,然后选择File→Show in Finder。

2.运行Terminal。输入命令xcrun ibtool--export-strings-file output.strings,后跟一个空格,然后将Main.storyboard从Finder拖曳到Terminal窗口,按回车键。

结果就是基于Main.storyboard的,名为output.strings的新文件会在主目录(也就是当前目录)下生成。可以根据Main.storyboard将这部分信息与现有的本地化.strings文件合并到一起。

在该示例中,我让你提前增加/"Hello/"按钮的宽度,从而为更长的本地化文本/"Bonjour/"留出足够的空间。在实际情况下,你可能会使用自动布局;这样按钮与标签就会自动伸缩了,同时界面的其他部分会相应地进行补偿。

要在不同本地化的情况下测试界面,还可以在Xcode中预览本地化nib文件,而无须运行应用。编辑.storyboard或.xib文件,打开辅助窗格,将追踪菜单切换至Preview。右下角的菜单会列出本地化信息;可以在菜单中进行切换。“两倍长度的伪语言”会通过非常长的替换文本来测试界面在这种情况下的反应。

在iOS 9中,当应用运行在自右向左的语言中时,运行时会自动颠倒(镜像)整个界面及其行为。比如,推动变换会沿着老视图向右滑动,然后从左侧加载新视图。如果使用了自动布局和两端约束,那么界面就会颠倒过来,但如果代码依赖于从左向右的方向性,那就需要使用一些新的UIView API。

9.10.3 本地化代码字符串

如何本地化其值是通过代码生成的字符串呢?在Empty Window应用中,轻拍按钮所弹出的警告就是个很好的示例。它会显示文本—警告的标题与消息,以及用于关闭警告的按钮文本:


@IBAction func buttonPressed(sender:AnyObject) {    let alert = UIAlertController(        title: /"Howdy!/", message: /"You tapped me!/", preferredStyle: .Alert)    alert.addAction(        UIAlertAction(title: /"OK/", style: .Cancel, handler: nil))    self.presentViewController(alert, animated: true, completion: nil)}  

该文本该如何本地化呢?方式是一样的(需要一个.strings文件),不过需要修改代码才能显式使用它。代码会调用全局的NSLocalizedString函数;函数的第1个参数是.strings文件中的键,注释参数给出了非常好的说明,比如,待翻译的原始文本。NSLocalizedString还接收几个可选参数;如果省略,那么默认会使用一个名为Localizable.strings的文件。

比如,我们将buttonPressed:方法修改为下面这个样子:


@IBAction func buttonPressed(sender:AnyObject) {    let alert = UIAlertController(        title: NSLocalizedString(/"ATitle/", comment:/"Howdy!/"),        message: NSLocalizedString(/"AMessage/", comment:/"You tapped me!/"),        preferredStyle: .Alert)        alert.addAction(            UIAlertAction(title: NSLocalizedString(/"Accept/", comment:/"OK/"),                style: .Cancel, handler: nil))        self.presentViewController(alert, animated: true, completion: nil)}  

当然,上述代码是有问题的,因为没有Localizable.strings文件。下面创建一个,过程与之前一样:

1.选择File→New→File。

2.选择iOS→Resource→Strings File,单击Next按钮。

3.确保该文件属于应用目标,将其命名为Localizable,注意名字与大小写,单击Create按钮。

4.这时,一个名为Localizable.strings的文件会出现在项目导航器中。将其选中,在文件查看器中单击Localize按钮。

5.这时会弹出一个对话框,让我们选择初始语言。默认是Base,这就可以,单击Localize按钮。

6.在文件导航器中勾选French。

现在,Localizable.strings文件对应于两个本地化,Base(即English)与French。我们需要在文件中添加内容。就像之前使用ibtool那样,可以通过genstrings工具自动生成初始内容。比如,我会在计算机上打开Terminal,然后输入xcrun genstrings,后跟空格。接下来将ViewController.swift从Finder拖曳到Terminal窗口,按回车键。这会在当前目录下生成一个Localizable.strings文件,其内容如下所示:


/* OK *//"Accept/" = /"Accept/";/* You tapped me! *//"AMessage/" = /"AMessage/";/* Howdy! *//"ATitle/" = /"ATitle/";  

将上述内容复制并粘贴到项目Localizable.strings文件的English与French版本中,然后检查键值对,修改每一个键值对的值,使得值是我们所需要的。比如,在English版的Localizable.strings文件中:


/* Howdy! *//"ATitle/" = /"Howdy!/";  

在French版的Localizable.strings文件中:


/* Howdy! *//"ATitle/" = /"Bonjour!/";  

以此类推。

9.10.4 使用XML文件进行本地化

从Xcode 6开始,我们可以通过另外一种方式完成之前的工作。从表面来看,文本本地化可以看作对.xliff文件导入与导出的解析。这意味着你实际上无须按下任何Localize按钮或编辑任何.strings文件!相反,你可以编辑目标并选择Editor→Export For Localization;在保存时,Xcode会创建一个目录,里面包含了用于各种本地化的.xliff文件。接下来编辑这些文件(或让专门负责编辑的人帮你)并将编辑好的文件导入;编辑目标并选择Editor→Import Localizations。Xcode会读取编辑好的.xliff文件并完成其他操作,根据需要自动创建好本地化,生成或修改.strings文件。

为了演示,我们再向本地化添加一种语言——Spanish。

1.编辑目标并选择Editor→Export For Localization。

2.我们可以在现有的本地化与基础语言中导入字符串。如果要编辑French本地化,那就需要将其导出,不过我不打算在该示例中这么做。相反,只需要将Include弹出菜单切换至Development Language Only即可。

3.指定好保存的位置(如桌面)。这时要创建一个目录,因此请不要让目录名与保存位置处的现有目录重名。比如,如果保存到包含了项目目录的相同目录下,那么可以将其命名为Empty Window Localizations,单击Save按钮。

4.在Finder中,打开刚才创建的目录。它包含了项目基础语言的.xliff文件。比如,我的文件叫作en.xliff,因为开发语言是English。

查看这个.xliff文件,你会看到Xcode已经帮我们做好了之前需要手工完成的一切。不再需要.strings文件了!Xcode完成了所有工作:

·对于项目中的每个Info.plist文件,Xcode都会创建一个相应的<file>元素。在导入时,这些文件会转换为本地化的InfoPlist.strings文件。

·对于每个.storyboard与.xib文件,Xcode都会运行ibtool来提取出文本,并且创建相应的<file>元素。在导入时,这些元素会转换为齐名的本地化.strings文件。

·对于包含了对NSLocalizedString调用的每个代码文件,Xcode都会调用genstrings,并且创建相应的<file>元素。在导入时,这些元素会转换为本地化Localizable.strings文件。

我们现在继续将该文件中的字符串翻译为其他语言,保存编辑好的.xliff文件,将其导入:

1.在合适的文本编辑器(或XML编辑器)中打开.xliff文件。

2.对于该示例来说,我只对故事板中的/"Hello/"按钮进行本地化。因此,删除(请小心,不要搞乱了XML)除original属性为/"Empty Window/Base.lproj/Main.storyboard/"的其他所有<file>...</file>元素组。删除除<source>为/"Hello/"的其他所有<trans-unit>...</trans-unit>元素。

3.将Spanish作为目标语言,向<file>元素添加一个属性:target-language=/"es/"。

4.提供一个翻译,在<source>元素后添加一个<target>元素,加上一些文本,如/"Hola/"。文件的内容现在应该如下所示:


<?xml version=/"1.0/" encoding=/"UTF-8/" standalone=/"no/"?><xliff xmlns=/"urn:oasis:names:tc:xliff:document:1.2/"  xmlns:xsi=/"http://www.w3.org/2001/XMLSchema-instance/" version=/"1.2/"  xsi:schemaLocation=/"urn:oasis:names:tc:xliff:document:1.2  http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd/">  <file original=/"Empty Window/Base.lproj/Main.storyboard/"  source-language=/"en/" target-language=/"es/" datatype=/"plaintext/">    <header>      <tool tool-id=/"com.apple.dt.xcode/" tool-name=/"Xcode/"      tool-version=/"6.2/" build-num=/"6C107a/"/>    </header>    <body>      <trans-unit>        <source>Hello</source>        <target>Hola</target>        <note>Class = /"UIButton/"; normalTitle = /"Hello/";          ObjectID = /"PYn-zN-WlH/";</note>      </trans-unit>    </body>  </file></xliff>  

5.回到Xcode,编辑目标并选择Editor→Import Localizations。在Open对话框中,选择编辑好的en.xliff并单击Open。

6.Xcode会提示我们没有翻译全部内容。忽略该提示并单击Import。

下面就是见证奇迹的时刻!在没有任何提示的情况下,Xcode为本地化添加了Spanish,并且又创建了一个InfoPlist.strings文件、一个Main.strings文件和一个Localizable.strings文件,所有这些文件都本地化为Spanish。查看Main.strings,你会发现其内容与我们手工编辑的一模一样:


/* Class = /"UIButton/"; normalTitle = /"Hello/"; ObjectID = /"PYn-zN-WlH/"; *//"PYn-zN-WlH.normalTitle/" = /"Hola/";  

显然,.xliff文件是创建并维护本地化的一种非常便捷的手段。项目中本地化的结构与本节之前介绍的完全一样,不过.xliff文件将相同的信息具化为可通过单个文件编辑的格式。.xliff导出过程会使用ibtool与genstrings,这样在添加界面和代码时就可以轻松维护本地化内容了。