大多数浏览器和
Developer App 均支持流媒体播放。
-
简化本地化的字符串
当您在自己的 App 内本地化文本时,您可以帮助使自己的 App 更方便全球受众无障碍访问。探索构建本地化工作流程的最佳实践,包括如何准确写入和格式化字符串,并了解如何使用 Xcode 以不同语言准备用于本地化的字符串。
资源
相关视频
WWDC22
WWDC21
WWDC20
WWDC19
-
下载
♪ (简化本地化字串) 大家好 欢迎来到WWDC 我是来自苹果本地化团队的托马斯 今天 我将向各位演示如何简化 本地化字串 您将看到让应用程序在多语言环境中 工作的最简单方法 我们会先从编写用户界面代码说起 接下来我们将讨论如何 在目标中组织字串 然后 我们会让Xcode 为我们完成繁重的工作 最后 我们将学习关于 高级字串的新技术 通过以上过程 我们将 确保您的应用程序 为出色的翻译做好准备 这非常重要 因为文本在 我们的生活中随处可见 我们使用文本来彼此交流 在现实世界中获得信息 在联网设备上更是如此 我们每天都会在这些设备上 观看各种应用程序 通知和文章 文本的可读性 准确性和易懂性 比以往任何时候都更加重要 假设您完全迷失在这条街上 想要在那么多的招牌中找一家小店 对于您的应用程序用户来说也是如此 文本是核心特征 它能引导用户并带来熟悉感 因此 应用程序本地化 是一个让您可以 触达世界各地用户的绝佳机会 尤其是在App Store服务 覆盖的市场中 作为开发者 您对文本已经非常熟悉 因为开发人员喜欢给东西命名 我们把文本称为“字串” 字串无处不在: 标题 按钮 用户内容 线上内容 搜索查询 图像 可访问性标签 不胜枚举 不妨把应用程序中的所有 字串看成是电影字幕 在您观看电影时 您希望所有字幕 都用正确的语言在正确的时间 显示正确的内容 并将这种正确性贯穿整部电影 就字串而言同样如此 您的字串将其含义 传达给您的视觉内容 引导用户正确使用应用程序 现在 让我们从开发者的角度来看看 本地化过程是怎样的 您的用户界面在视图中显示字串 这些字串保存在各自的文件中 这有助于区分不同 视觉内容之间的关注点 这对所有的语言 和您的具体翻译来说是通用的 在二者之间 Foundation框架 将用户界面和字串联系起来 在本视频中 我将详细介绍 这些组件 内容看起来很多 但我们将逐步一一介绍 让我们先从定义用户界面的 字串开始说起 结合运用苹果的最新技术 就能轻松显示本地化字串 我鼓励您在您的 应用程序中使用这些技术 如此一来 您在进行本地化工作时 就不用考虑再三了 您在用户界面中定义的任何 字串需要进行本地化处理 可视字串分为两类 一是您在创建视图时 直接定义的字串 在这里 我在SwiftUI和 Storyboard中定义了标签 没错 这两个标签都可以本地化 我早就说了很简单 第二种类型的字串更为常见 可以在SwiftUI的 示例代码中找到 也可以在AppKit 或UIKit中看到 您可能需要在变量中存储字串 或者从函数中返回字串 在所有这些情况下 您都可以使用 NSLocalizedString 作为iOS 15和macOS Monterey中的新功能 您可以使用我们针对Swift进行 改进的String(localized:)方法 让我们先来看看 SwiftUI中的第一个方法 您在界面中呈现的所有东西 实际上都是一个视图 无论是文本标签还是按钮 在默认情况下都是可本地化的 准备接纳存储 您之后已完成翻译的字串 由于这是默认开启的 所以如果您有预览等模拟内容 请确保采用逐字方式退出 这将避免不必要的转译工作 您可以在这个相关部分学到更多技巧 现在 我们已经掌握了基础知识 让我们来看看如何使 字串更具动态效果 我正在给大家展示的是 一个用来下单的本地化按钮 各位可能不知道我喜欢观看音乐会 更喜欢和朋友们一起去看现场演出 在这款应用程序中,不容易看到 我为朋友们订购了多少张票 因此 我们来改进这个按钮 将我已选定的门票数量显示出来 这很简单 我只需要在字串中 插入一个变量就行了 就像我在Swift中所做的那样 在这里 count将在程序运行时 被实际计数数字三替换 既然我们已经看到了所有的选项 我想指出一个常见的陷阱: 带(format:)的字串 带(format:)的字串 很不错 但它不是用于本地化的字串 让我们借用一个阿拉伯语 的例子来说明原因 在该例中 文本方向和 数字可能是不同的 好消息是可使用 String(localized:) 对二者进行自动管理 根据用户在“设置”中的数字偏好 数字“三”已在该按钮中 用阿拉伯语正确书写 String(localized:) 也支持复数 以及字串各个部分的分离 这意味着 词语在双向文本中不会被打乱 最后 请注意不要过度使用变量 将字串连接在一起很简单 但可能导致翻译问题 在这里 “订购”一词 在某些语言中的书写可能会有所不同 无论是现在订购 还是以后订购 更保险的做法是 使用两个单独的字串 好吧 我们还需要了解 我到现在还没有告诉 各位的一个重要方面 您定义的字串需要由 您以外的人进行翻译 翻译人员在翻译一个个字串时 并没有看到完整的用户界面 而他们必须使所有的字串保持连贯 所以 您需要帮助他们 就像您帮助同事理解您的代码一样 方法就是添加代码注释 我坚持认为 无论是哪种字串 您都应该添加注释 别忘了您的 Storyboard文件 它们在标识检查器中有一个注释字段 让我来分享几个帮助翻译者的技巧 首先 注释应该说明 字串在哪里是可见的 例如 这是按钮吗?是标签吗? 还是 VoiceOver文本? 知道它是动词——订购 还是名词——订单——这个至关重要 其次 注释应该说明背景信息 当我点击Order 我是在完成一笔交易 还是在对列表进行排序? 最后 注释应该对变量加以说明 当本地化者看到您的字串时 他们并没有看到代码 因此他们不知道变量的名称 从而无法从中获得线索 他们看到的只是一个通用占位符 因此 就这里的最后一个例子而言 “Odered”之前的 那个数字代表了什么? 是历史订单中的门票数量吗? 还是刚刚订购的订单中的门票数量? 没错 多亏了注释 我才知道它代表已订购的门票总数 许多语言的翻译者 能够根据“门票” 一词的词性 进行正确的翻译 有时 最简单的方法是在注释中 也写下变量的示例值 记住这一点就大功告成了 您的用户界面代码 已做好了本地化的准备 现在 我们将了解如何单独从字串 维护用户界面代码 基本上来说 您可以组织安排 字串的位置 为了做到这一点 让我们来看看Foundation 将如何确保您的代码加载了 正确的本地化字串文件 当然 本地化从一种语言开始 您可以进入项目设置 添加一种新的语言 让我们来看看Xcode 选择项目并点击加号按钮 添加一个本地化项目 在最新版本中 您将看到 菜单里新增了很多语种 让我们来看看…… 在我们的本地化应用程序列表中 首先是排在最上面的Base 没错 用户界面元素就在这里 因为它们是跨语言共享的 例如 Storyboard文件 是共享的 Siri Intents文件 也是共享的 它们需要被添加到Base 因此 请确保您点击了 Localize按钮 对所有共享资产进行本地化 好了 但从另一方面来说 字串不是共享的 字串只属于一种语言 所以 您的字串分为英语字串 和阿拉伯语字串 想要用某种语言的字串 来测试应用程序 您可以在SwiftUI中 更改预览环境 或者更改应用程序方案设置 如果您的应用程序不支持 设备所使用的语言 Foundation会试图找到 最佳替代方案 如果我的手机使用墨西哥西班牙语 Foundation会优先转换到 拉丁美洲西班牙语 其次是西班牙语 然后是您的应用程序的开发语言 比如英语 最后 如果字串来自于服务器 请确保尊重用户偏好的语言 很好 我们已经知道 每种语言都有一个字串集合 这些字串可以进一步分配到文件中 名为“表” 您可以使用这种功能 随意分配所有字串 例如 可以为每个功能或每个屏幕 设置一个表 由于这是默认选项 所有字串都存储在 Localizable表中 具体来说 这意味着 所有字串都存储在 名为Localizable.strings 的文件中 让我们通过一个示例 来回顾一下我们到目前为止 学到的内容 下面的代码 声明了一个带有变量的字串 一个自定义表名和一条注释 假设我的应用程序支持法语 在Xcode中 您需要一个 UserProfile.strings文件 其中包含从英语到法语的翻译 该文件将被存储在一个 法语资源文件夹中 此文件夹按惯例 被命名为fr.lproj 对于法语翻译者来说 那条注释非常有用 他们用“e”来标记阴性词 例如“places” 在法语中 门票是阴性词 让我们加点花样 谈谈bundle 该参数允许跨目标加载字串 bundle参数默认是main 在您自己的应用程序中 您不需要它 Main就是应用程序 在您的应用程序扩展中 main 是指您自己的扩展 所以您也不需要它 但假设您想在应用程序和扩展之间 共享一个字串 在您的扩展中 您需要提供 主应用程序的bundle 这样做可以避免二者的字串重复 您也可以从框架中获取字串 在这种情况下 要么您在应用程序代码中 指定框架的bundle 从而直接使用框架的字串 要么框架提供您可以直接使用的变量 这些本地化的字串变量 通过指定框架自己的bundle 已经在框架中被定义 让我们回顾一下我们的模型 只不过现在我们将更新它 使我们的应用程序 加载框架提供的字串 框架定义了字串 并通知Foundation 翻译内容存储在 其bundle内部的 一个字串文件中 如果没有这个参数 字串将从 托管应用中获取 且无法找到 在实践中 情况看起来就像这样 您提供存储文本的bundle 框架会在它自己的 bundle中查找字串 然后对您的应用程序来说 事情就很简单了 字串可以在一行简单的代码中使用 此外 如果您以这种方式 在您的框架中实施本地化 您甚至不需要创建 包含complete 的字串文件 现在让我们来看看原因 我们已经知道您可以 在代码中做哪些事情 来声明和分配字串 但我们不知道如何真正地创建 保存翻译内容的文件 事实证明您不必创建 那些字串文件 Xcode可以替您创建 所有的字串文件 当您使用Export Localizations 它会读取您的代码 并提取所有字串 这很不错 因为您 不必维护字串文件 如果您经常忘记将您 刚刚在用户界面中 编码的字串进行本地化 那么这款工具非常适合您 如果您不喜欢genstrings 这款工具也适合您 今年 在Xcode 13中 我们增添了对Swift字串 提取的编译器支持 另外 我们现在 完全支持workspace 这进一步分割了逻辑 和翻译之间的关注点 Xcode将利用我们 之前讨论过的Swift 和Foundation方法 来探测和提取文本 请注意 如果您您有包装这些API 的自定义代码 默认情况下这将不起作用 您通常不需要使用方法或宏 但如果真的需要 可以将它们添加到构建设置 置于本地化字串宏名称下 至于其他情况 Xcode 将为了本地化而提取 Info.plist中定义的 应用名称和隐私描述 以及在Xcode的检查器中 被标记为已本地化的所有资产 如果您有现成的本地化 您可以转换到Xcode的导出 用于新的用户界面 新的字串将自动添加到 现有的文件中 如果你想按照自己的节奏转换项目 这非常适合您 作为奖励 您的用户界面 测试的截图现已包括在内 这对本地化者来说很好 这样他们就能得到 字串位置的上下文 也有助于您在App Store上 展示已本地化的 应用程序截图 好吧 Xcode提取了 所有的本地化目录 因此你会认为 该轮到翻译者工作了 其实 您也可以自己做! 作为Xcode 13中的新功能 导出的本地化目录 可以在Xcode中直接查看和编辑 您可以在左边的bundle中 看到生成的每个文件 每张表 您可以通过选择 看到所有包含的 字串,图像和文件 您可以对字串进行筛选和排序 查看注释 屏幕截图甚至进行翻译! 这非常便于 您开发和翻译自己的应用程序 它也能让您自己检查 和修复字串错误 当翻译者发回已翻译的字串目录时 您可以使用Xcode中 的Product 菜单 把它们导入您的项目 突然之间 您的字串文件 stringsdicts 和其他资产将被创建和更新 您可以使用命令行等价工具 在持续集成系统上 进行自动的导出和导入 通过定期调用它们 您的项目可以获得最新的字串 并快速翻译您的新用户界面 您可以在今年的“SwiftUi 应用本地化”会议上 看到增强的工作流程 并在介绍部分了解更多信息 就是这样! 您已经看到字串如何 在应用程序中诞生和存在 最后 我将向大家演示如何 处理更复杂的字串 我相信各位会喜欢我们准备的新功能 让我们先从一个出色的功能说起 我们改进了属性字串 使其具有内置本地化功能 在Markdown语法的支持下 这是可以实现的! 现在您可以在不丢失任何格式的同时 将您的字串本地化 不再需要高风险的字符操作 只需要把词语加粗 说到这里 我用星号 来强调“完成” 我鼓励您查看 “Foundation新功能” 学习如何添加链接 强调 等宽字体 等等 我们已经看到 您在代码中定义的字串 将在字串文件中拥有对应的翻译 但有时 您需要字串 有多种表现形式 这可以通过 stringsdict文件来实现 该文件是字串的集合 适应您定义的各项规则 例如 还记得我们想要 订购几张门票的例子吗? 在英语中 如果不止一张门票 我们会加一个“s”作为后缀 如果只有一张门票则不加“S” 为了使代码简洁正确 您需要利用stringsdict 来定义这种复数规则 因为如果您进行应用程序本地化 这些规则对于每种语言都有所不同 看看俄语中的一些例子 您不希望在代码中处理这些 这是本地化 应该解决的问题 让我们来看看如何实现这一点 不需要修改代码 我们仍将使用以上的现有代码 首先 与为您制作的字串文件相比 stringsdict需要 手动选择使用 因此需在Xcode模板中手动创建 并在检查器中 确认点击Localize 好了 一切都从您在代码中定义的 字串开始 如果代码中有多个复数字串 您可以在这里为每个 字串添加这个根条目 在内部 您定义实际呈现的值 这个值遵循搜索-替换机制 在本例中 我定义了一个名为“门票”的令牌 它将保存最终完整的字串 最好将文本的大部分 内容包含在令牌中 但这一字段是可以本地化的 说不定翻译者需要添加前缀 后缀 或移动令牌 以防您有多个变量 好了,那个令牌将 根据代码中的一个变量而变化 接下来让我们定义门票令牌 首先我们确认我们有一些复数 然后我们用c风格的格式化程序d 声明该变量的类型是数字 最后 我们撰写复数规则 在英语中 我们可以将该规则 表示为"一"和"其他" 如果您喜欢的话 还可以用“零” 我们为每个条目 编写门票令牌的实际值 然后 如果有三张门票 在英语中将使用“其他” 并生成“订购三张门票” 用数字三代替%d 如果某种语言需要更多的案例 请别担心 Xcode会在导出时为您添加案例 在俄语中 它将在现有案例上方 添加或多或少的案例 就是这样! 我们的字串将在运行时被复数化 在继续接下来的内容之前 我想说一个案例 虽然stringsdict 应该用于复数 但它原本是用于包含数字的字串 在上一个案例中 我们知道“一”表示英语中的单数 在俄语中确实表示数字一 但它也用于表示21 31等等 因此在那个案例中使用 stringsdict是不正确的 因为你仅仅是想等于“一”而已 而在本例中 复数根据这个 两者或所有而变化 没有数字 请不要使用stringsdict 假设我为朋友们订了21张票 在俄语中 我会看到 “订购这张门票” 而不是“订购所有门票” 我会问“我的钱哪里去了?” 使用简单但有效的if/else 来正确地表示这三个字串 在所有语言中的复数形式 那是复数支持 但stringsdict可以处理 更多的字串变体类型 请观看相关介绍以了解更多信息 这很好 但我们想为你 提供一个更简单的方法 Foundation今年学会了 如何为你处理语法! 随着属性字串增加 对Markdown的支持 使用这种带有 inflect 属性的新格式 您将在运行时得到正确的计算值 比如这个按钮 这是 iOS 15和macOS Monterey的一项新功能 目前适用于部分语言 如果您想要获得更多的控制 可以使用stringsdict 我们没有停止添加复数支持 我们希望我们的软件 字串更具有包容性 例如 当应用程序欢迎用户时 英语文本相当简洁明了 但西班牙语却不是这样 因为它依赖于用户的称呼 到目前为止 你不得不呈现 一个非个性化的字串 对大多数西班牙用户来说 该字串是正确的 但有些生硬 字面意思是“我们欢迎您” 如果我们可以为用户 定制该字串呢? 有了新的Markdown标记 您现在可以做到这一点! 该字串将遵循您 在设备的语言设置中 选择的称呼 所以“bienvenida”用于 女性用户 “bienvenido”用于男性 如果我们不知道性别 我们将使用现有的屈折语 复数和称呼的内联屈折变化 可以在代码 或者翻译中定义 对于语言选择的这一新增功能 我们感到非常兴奋 等不及想看到您的应用使用它 今天我们终于讲完了 编写本地化字串的所有方法 但如果您希望展示数据 您应该让框架为您编写 我们的格式化程序可处理 语言和区域的数百种组合 以及各种类型和单位 所以不要硬编码 让我们来做这项艰苦的工作吧 从今年开始 在Swift中采用格式化程序 比以往任何时候都要简单 我们让格式化程序很方便使用 字串插值中的内联 查看今天的 “ Foundation新功能” 寻找您心仪的新API 获取内天所讲内容的详细信息 如果您需要部署以前的版本 或者想要详细了解格式化程序 请查看去年的内容 好了 我今天希望大家知道的是 如果您使用现代API编写代码 Xcode将为您生成所有的字串 我们已经知道如何 跨bundle组织字串 我们也发现了新的API 让您可以轻松 处理语法和格式 如果您采用这些方法 添加一种新语言并不需要修改代码! 最后,必须要进行测试 不管您花费多大精力 进行字串本地化 您都应该测试您的应用程序 确保它在所有语言环境中都正常运行 我已迫不及待地想要使用 您完全本地化的应用程序 订购音乐会门票 请继续观看其他WWDC课程 感谢观看 ♪
-
-
3:30 - Declaring a string
Button("Order")
-
3:44 - Declaring a string anywhere else
button.title = NSLocalizedString("Order", comment: "…") button.title = String(localized: "Order")
-
4:20 - Declaring a string in a SwiftUI view 2
Text("Your order is ready.") Button("Order") { // Action… }
-
4:33 - Declare a string in SwiftUI view with verbatim
Text(verbatim: "Sample data")
-
4:54 - Button to place an order
// SwiftUI Button("Order") { … } // Swift button.title = String(localized: "Order")
-
5:15 - Button to place an order with a variable
let count = 3 // SwiftUI Button("Order \(count) Tickets") { … } // Swift button.title = String(localized: "Order \(count) Tickets")
-
5:36 - Button to place an order with a variable 2
let count = 3 // Supports user’s preferred numbers, // pluralization, RTL variables isolation… // Previously: .localizedStringWithFormat() String(localized: "Order \(count) Tickets")
-
6:21 - Use 2 separate strings
// Recommended for all languages String(localized: "Order Now") String(localized: "Order Later")
-
6:57 - Button to place an order 2
// SwiftUI Text("Order") // Swift String(localized: "Order")
-
7:09 - Button to place an order with comment
// SwiftUI Text("Order", comment: "Button: confirms concert tickets booking”) // Swift String(localized: "Order", comment: "Button: confirms concert tickets booking")
-
7:36 - What makes a good comment
Text("Order", comment: "Button: confirms concert tickets booking") Text("Order", comment: "Button: confirms concert tickets booking") Text("\(ticketCount) Ordered", comment: "Order summary: total number of tickets ordered")
-
10:52 - Request server strings in the user's language
Bundle.preferredLocalizations(from: allServerLanguages).first
-
11:37 - Declare a string with a variable, customized table name, and a comment
Text("\(ticketCount) Ordered", tableName: "UserProfile", comment: "Profile subtitle: total number of tickets ordered")
-
13:49 - Using a framework
/* —-----------—------------—-—---- In TicketKit Framework —---------—------------—-—---- */ // TicketKit/OrderStatus.swift public enum OrderStatus { case pending, processing, complete, canceled, invalid(Error) var displayName: String { switch self { case .complete: return String(localized: "Complete", bundle: Bundle(for: AnyClassInTicketKit.self), comment: "Standalone ticket status: order finalized") /* —-----------—-----------—---—--- In Host App —---------—------------—-—---- */ import TicketKit Text(OrderStatus.complete.displayName)
-
17:43 - Import translated strings catalogs
xcodebuild -exportLocalizations -workspace VacationPlanet.xcworkspace -localizationPath ~/Documents xcodebuild -importLocalizations -workspace VacationPlanet.xcworkspace -localizationPath ~/Documents/de.xcloc
-
18:28 - Localized attributed strings
AttributedString(localized: "Your order is **complete**!", comment: "Ticket order confirmation title")
-
19:22 - Plural with stringsdict
String(localized: "Order \(ticketCount) Ticket(s)")
-
22:46 - Plural for strings without a number
if ticketCount == 1 { button.text = String(localized: "Order This Ticket") } else if ticketCount == 2 { // If needed button.text = String(localized: "Order Both Tickets") } else { button.text = String(localized: "Order All Tickets") }
-
23:31 - Automatic grammar agreement
AttributedString(localized: "Order ^[\(ticketsCount) Ticket](inflect: true)")
-
25:45 - Format data in strings
["pop", "rock", "electronic"].formatted(.list(type: .or)) // pop, rock, or electronic Text("Total: \(price, format: .currency(code: "USD"))", // Total: $9.41 comment: "Order subtitle: total price of all tickets")
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。