iOS的SwiftUI本地化教程入门¶
Dec 6 2021, Swift 5.5, iOS 15, Xcode 13
学习节省时间的技巧,改变你的团队的本地化工作流程! 作者:Andy Pereira。
学习省时的技巧,改变你的团队的本地化工作流程¶
当你构建应用程序时,有一长串需要跟踪的事情。考虑让你的应用程序在其他语言中工作可能不在你的考虑范围内--特别是如果你不会说其他语言。幸运的是,苹果公司已经在SwiftUI中进行了修改和改进,使你的本地化工作流程中的许多猜测变得简单。
在这个SwiftUI本地化教程中,你将通过学习如何利用SwiftUI和iOS 15的国际化和本地化技术。
- 从你的项目中导出和导入字符串。
- 在开发时预览本地化。
- 使用新技术来处理本地化字符串。
- 本地化资产目录项目。
- 使用
Markdown来格式化本地化字符串。 - 使用自动语法协议。
入门¶
点击本教程顶部或底部的下载材料按钮,下载启动项目。
你将使用的应用程序,国家测验时间,是一个简短的游戏,对用户的世界知识进行测验。它还记录了每个问题的正确答案。打开启动项目,看看周围。你会注意到该应用程序已经有了它的字符串,目前都是英文的,在两个文件中。
Localizable.strings:这是你的应用程序将存储大部分字符串的地方。Localizable.stringsdict:这个文件处理复数。稍后你会学到更多关于这个的知识,但现在,它是你处理需要单数或复数的名词的方式。
建立和运行,并给测验一个最好的机会。 :]

导出和导入本地化¶
随着每个新版本的推出,苹果公司使管理字符串文件的本地化变得更加容易。Xcode 13现在可以自动提取它在你的代码中的各种情况下发现的任何字符串。当您在Xcode 13中创建一个新项目时,这个设置是默认开启的。然而,由于你很可能想在一个现有的项目中使用这个功能,你需要把它打开。
在你的项目设置中,进入Build Settings并搜索Use compiler to Extract Swift Strings。确保搜索所有设置,而不仅仅是基本的设置。把这个设置的值改为Yes。

在Xcode中,进入Product ▸ Export Localizations...。按照提示将文件夹保存到你的桌面。

在对导出的xcloc文件进行任何修改后,使用Product ▸ Import Localizations...将这些修改导入你的项目中。这样,翻译人员就可以对显示的文本进行修改,而不需要整个项目。
使用xloc文件¶
在Finder中,导航到你在上一步创建的目录,并打开en.xcloc文件。在左侧,展开该文件夹并选择Localizable。

在这里,你要看的是Xcode能够识别的所有东西,作为你的一个项目文件中使用的字符串键。有四列信息你需要了解:
Key:这些是在你的代码中用于字符串字面的值。English:这第二列代表你的参考或基础本地化语言。这个项目的基础语言是英语,所以无论你查看的是哪个语言文件,你都会在这里看到英语值。English:第三列会有你正在查看的语言文件的所有本地化信息--在这个例子中,也是英语。你可以编辑这一栏中的值。Comment:评论栏将显示开发者留下的任何注释,以帮助翻译者理解每个字符串的意图。
这个文件是你可以对你所有的本地化文件进行修改的地方--不仅仅是字符串文件。在过滤器中,搜索points-count并展开它找到的所有项目的值。你会看到以下内容。

在这里,你看到的是在项目的stringsdict文件中发现的值。因为strings和stringsdict文件都有相同的名字,所以这些值在这个视图中被合并。如果你为不同的本地化创建了不同的文件名,编辑器窗口会相应地将它们分开。
你也可以用这个文件翻译你的应用程序的名称。在编辑器中,选择InfoPlist。你会看到以下内容:

根据需要调整语言的捆绑名称或显示名称。这将取决于你和你的团队,了解什么是最有意义的翻译。也许你的应用程序的名称在几种语言中都有意义,而只需要对其他几种语言进行翻译。
添加另一种语言¶
为了完成本教程,你将向你的项目添加另一种语言。进入你的项目的Project设置,然后选择Info。从那里,在Localizations下选择+。

在出现的菜单中,选择Spanish (es)。

将出现一个确认对话框。让设置保持原样,并选择Finish。

这在Localizable.strings和Localizable.stringsdict下增加了西班牙语文件。

现在你已经准备好将你的应用程序本地化为西班牙语了!
在开发过程中查看本地化情况¶
在大多数情况下,如果一个应用程序使用传统的本地化技术,你只能在你的手机被设置为该语言时看到其他语言。然而,这在开发环境中可能相当难以管理--特别是当你不熟悉该语言时。
打开Localizable.strings (Spanish),添加以下一行。
"Welcome to Country Quiz Time" = "Bienvenido a Country Quiz Time";
这将帮助你看到在开发过程中如何查看本地化。以后,你会对这个翻译进行微调,并在这个文件中添加更多的翻译。
你可以通过调整目标的方案在模拟器内看到翻译。打开Product ▸ Scheme ▸ Edit Scheme… ▸ Run ▸ Options。对于设置App Language,从下拉菜单中选择Spanish。

选择Close,然后建立并运行。

你会看到你的应用程序的标题被翻译成西班牙语。
翻译的预览¶
虽然在开发过程中能够改变模拟器的语言是件好事,但SwiftUI的好处之一是能够在预览中直接看到你的代码变化,而且是实时的。打开ContentView.swift,用以下内容替换ContentView_Previews:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
// 1
ContentView(quiz: Quiz())
.environment(\.locale, .init(identifier: "en"))
// 2
ContentView(quiz: Quiz())
.environment(\.locale, .init(identifier: "es"))
}
}
}
以下是你添加的内容:
- 对于这个视图显示的第一个预览,你已经将环境的区域设置为始终是英语。因为方案的语言会影响到预览的显示语言,否则,根据你在上一步所做的,它将显示为西班牙语。
- 接下来,你将第二个预览的语言设置为西班牙语。
刷新预览,你会看到你的应用程序的翻译标题:

现在,对QuestionView做同样的事情,打开QuestionView.swift并将QuestionView_Previews替换成以下内容:
struct QuestionView_Previews: PreviewProvider {
static var previews: some View {
QuestionView(question: Question.mockQuestion)
.environmentObject(Quiz())
.environment(\.locale, .init(identifier: "en"))
QuestionView(question: Question.mockQuestion)
.environmentObject(Quiz())
.environment(\.locale, .init(identifier: "es"))
}
}
现在,无论你在开发什么视图,预览将始终允许你看到两种语言。这也是帮助多语言团队的开发者以他们可能更习惯的语言看到他们的工作的一个好方法。现在,QuestionView的两个预览看起来是一样的,因为你还没有翻译该视图中的任何内容。
利用新的本地化技术¶
在接下来的章节中,您将了解到Xcode 13和iOS 15中引入的本地化过程的新的省时的改进。
初始化字符串¶
在前面的例子中,iOS可以翻译你的应用程序的标题,因为传递给Text的字符串实际上被视为LocalizedStringKey的实例。在幕后,它为你处理所有的工作,使翻译工作变得更加容易。但是当你没有直接向Text的实例提供一个字符串时会发生什么?
在过去,开发者会通过使用全局函数或宏,NSLocalizedString(_:tableName:bundle:value:comment:)来解决这个问题。在大多数情况下,这是很容易使用的。不过,当你的字符串需要向你的代码提供参数时,它确实变得很复杂。打开ContentView.swift,找到属性scoreMessage。
private var scoreMessage: String {
// 1
let localizedString
= NSLocalizedString("points-count %lld", comment: "The pluralized score")
// 2
return String(format: localizedString, quiz.score)
}
下面是上面的代码中发生的事情:
- 这将创建一个对复数字符串
points-count的引用。 - 用实际分数替换
%lld格式指定符。前面的步骤总是需要的,这样你就可以向字符串格式化器提供本地化的字符串。
虽然这肯定不是你可能遇到的最复杂的过程,但肯定还有改进的余地。
iOS 15和Xcode 13的新功能是String(localized:table:bundle:locale: comment:)。这允许你将之前代码中的两步过程整合为一步。将scoreMessage替换为以下内容:
private var scoreMessage: String {
String(
localized: "points-count \(quiz.score)",
comment: "The pluralized score."
)
}
在这里,你在一个简单的步骤中传递了复数字符串的键和它需要的值。你还有一个好处,就是编译器会自动导出在这个初始化器中发现的字符串,所以你不需要担心在更大的项目中去寻找它们。
应用程序中还有几个地方需要使用这个新的初始化器。打开QuestionView.swift,从optionButton(_:)中替换这一行:
message = isCorrect ? "correctly" : "incorrectly"
为:
message = isCorrect ?
String(localized: "correctly", comment: "Correct message") :
String(localized: "incorrectly", comment: "Incorrect message")
这将使用户回答问题时显示的字符串本地化。
最后,打开QuestionModel.swift。你会发现有12个地方使用了NSLocalizedString(_:tableName:bundle:value:comment:)。利用你在上两步中学到的知识,将每一个地方转换为使用String(localized:table:bundle:locale: comment:)。
包括本地化的Markdown¶
在iOS 15中,一个伟大的新增功能是能够在接受字符串的视图中直接包含Markdown,如Text。打开ContentView.swift,找到设置标题的那行代码:
Text("Welcome to Country Quiz Time")
用以下内容取代它:
Text("Welcome to _Country Quiz Time_", comment: "App welcome title")
这是你刚才添加的内容:
- 该字符串现在在应用程序的标题前后都有一个下划线。这将使应用程序的标题在运行时显示为斜体。
- 你添加了参数
comment,使翻译者更清楚地了解情况。重要的是要记住给你的翻译人员提供所有你能提供的帮助。
你也要回到Localizable.strings (Spanish),更新应用程序的标题行,以包括Markdown。具体做法是将Welcome…开头的一行替换为以下内容:
"Welcome to _Country Quiz Time_" = "Bienvenido a _Country Quiz Time_";
根据需要刷新预览。你会看到应用程序的标题,只有名字是斜体的。

你不一定能在Text内直接设置字符串值。但是,如果你想用Markdown提供一个本地化的字符串,你就需要使用AttributedString。在ContentView.swift中,在scoreMessage属性后添加以下新属性:
private var appTitle: AttributedString {
AttributedString(
localized: "Welcome to _Country Quiz Time_",
comment: "App welcome title"
)
}
在这里,你添加了一个本地化的字符串,用Markdown,几乎和你在教程中几步之前做的一模一样。
还是在ContentView.swift中,替换:
Text("Welcome to _Country Quiz Time_", comment: "App welcome title")
为以下内容:
Text(appTitle)
根据需要恢复自动预览更新。你仍然会看到标题的格式和以前一样。
资产目录的本地化¶
颜色和符号在不同的文化和语言中会有不同的含义。你可以对你项目的资产目录进行颜色和图像的本地化,以帮助为所有用户带来更丰富的文化体验。
打开Assets,并选择颜色资产Region。突出颜色,在Attributes检查器下,找到并选择Localization下的Spanish。

现在选择Spanish的颜色方块。再次在属性检查器中,将Input Method改为8位十六进制,并将Hex值设置为#006847。

将你的方案的语言设置为西班牙语,然后建立并运行。你的标题现在将是绿色的。

如果你把你的方案的语言切换回英语,标题就会变成红色。
使用自动语法协议¶
苹果在iOS 15中引入的更独特和强大的东西之一是automatic grammar agreement。在英语中,名词复数化背后的规则相对简单。如果你有一个东西,你通常使用该名词的单数形式。当你有零或多于一个时,你就使用复数形式。有一些例外情况,但在大多数情况下,就是这样。
大多数英语使用者不必考虑的一件事是gendered nouns。许多语言对名词应用了语法上的性别,这可能会影响到与名词一起使用的冠词、动词和形容词。
举例来说,在西班牙语中,纸和房子这两个词有不同的性别。
Paper, masculine:el papelHouse, feminine:la casa
在为名词指定性别的语言中,复数词需要在数量和性别之间达成正确的*共识。对于那些不熟悉这一概念的人来说,规则可能会变得相当复杂,特别是在学习如何将可能包含不同性别的事物组合复数化时。在过去,开发者依靠一个stringsdict文件来说明事物的基本复数化。
下面的代码块可能是你处理字符串复数化的第一直觉:
if points == 0 {
print("0 points")
} else if points == 1 {
print("1 point")
} else {
print("\(points) points")
}
然而,这没有为翻译提供灵活性。使用stringsdict文件,你可以声明变量并为不同的数量提供不同的值。这样做效果非常好,可以实现各种可扩展性和本地化。不过,它也有自己的一套挑战。
进入自动语法协议。为了拉开序幕,打开ContentView.swift,找到属性scoreView。注意它用来在Text视图中显示分数的字符串。
Text("Score: \(quiz.score)")
你要改变这个字符串的本地化,在它后面加入一个适当的复数形式的point。打开Localizable.strings (English),添加以下一行:
"Score: %lld" = "**Score:** ^[%lld Points](inflect: true)";
你刚才添加的本地化条目做了以下工作:
- 将你的翻译值设置为包括加粗的标记
Score:。 - 将要转折的短语包裹在
^[]内。这告诉系统将有需要转折的值,或在运行时进行解释。 - 包括一个对前面的文本块进行转折的指令。
从本质上讲,你的本地化字符串会根据传递给字符串的整数值,自动将points字改为其自身的正确形式。如果你有一个点,它就会说1 Point。如果你有0个或超过1个,它将使用Points。
确保你的方案被设置为英语,然后建立并运行。逐步完成测验,注意用户界面左下角的分数,看看points一词是如何根据你的分数变化的。

Note
每个问题的答案如下。
- 哪一天是墨西哥独立日?9月16日
- 安道尔使用哪种语言?Catalan
- 美国的首都是什么?Washington, D.C.
在上面的例子中,自动语法协议正确地算出了Points是一个名词。但如果它不知道,你可以告诉它你的字符串中的一个词属于哪个语部。在Localizable (English).strings中,你可以将Score: %lld的条目改为如下:
"Score: %lld"
= "**Score:** ^[%lld ^[Points](grammar: { partOfSpeech: \"noun\" })]
(inflect: true)";
这个应用程序看起来会与之前的状态非常相似,但你做了这些改变:
- 把
Points包在括号里,告诉系统你要提供关于这个词的更多说明。 - 在圆括号内,提供了一个键值对,键值为
grammar。 - 在里面,你会看到另一个键值对,键为
partOfSpeech,值为"noun"。这需要转义字符,因为这个指令本身就存在于一个字符串中。
这将产生与之前相同的结果,因为Points已经被系统识别为一个名词。然而,如果你发现单词的行为不符合你的期望,你可以按图所示定制你的本地化。
称呼条款¶
在西班牙语中,甚至你欢迎用户的方式也可以取决于他们的性别。西班牙语用户可以在他们的Language & Region设置中配置一个称呼术语,所以你也可以用自动语法协议来说明这一点。在Localizable.strings (Spanish)中改变这一行:
"Welcome to _Country Quiz Time_" = "Bienvenido a _Country Quiz Time_";
为:
"Welcome to _Country Quiz Time_" = "^[Bienvenido](inflect: true,
inflectionAlternative:'Te damos la bienvenida') a _Country Quiz Time_";
这就是它的作用:
- 告诉系统对
Bienvenido进行转折。系统将根据用户的首选称呼设置,将其转为Bienvenido(masculine)或Bienvenida(feminine)。 - 如果用户没有指定称呼,系统会提供一个中性的替代词
Te damos la bienvenida来显示。
确保你的方案的语言设置为西班牙语,然后建立并运行。如果你没有配置一个称呼,你现在会看到中性的欢迎词。

敲定翻译¶
你就快完成了! 作为最后一步,您将添加所有可能的西班牙语翻译,并将这些值导入Xcode。下载的项目材料中包含一个文件,其中有一些西班牙文的翻译,已经准备好了。
在Xcode中,选择Product ▸ Import Localizations...。从下载的材料中导航到CountryQuizTime_Materials/translations/es.xcloc,并完成导入。
Note
当你试图导入时,Xcode可能会警告你缺少翻译。对于这个教程,如果你看到这一点,可以不用担心,继续。然而,当你在一个生产应用程序上工作时,审查丢失的条目是很重要的。
将你的方案的语言设置为西班牙语,构建并运行。

就这样,你已经完成了如何使用SwiftUI和本地化的最新功能的学习
接下来去哪?¶
你可以点击本教程顶部或底部的下载材料按钮,下载项目的完整版本。
你已经准备好在iOS 15中使用所有的新技术对你的应用程序进行本地化了。
如果你想了解更多关于本地化你的应用程序的信息,而不仅仅是使用SwiftUI,请看看[Internationalizing Your iOS App: Getting Started](https://www.raywenderlich.com/250-internationalizing-your-ios-app-getting-started)。你还可以参加由多部分组成的课程Multi-Language Support with Localization in iOS。要了解更多关于使你的应用程序欢迎来自不同文化的人的其他方法,请观看WWDC 2021的包容性设计的实践。
你可以在GitHub.上了解更多关于用Markdown格式化你的字符串的信息。
我们希望你喜欢这个教程。如果你有任何问题或意见,请加入下面的论坛讨论!