大多数浏览器和
Developer App 均支持流媒体播放。
-
构建支持多语言的 App
确保你的 App 能够正常、高效地满足多语言用户的需求。学习有关文本输入、显示、搜索和格式设置的推荐做法。详细了解如何提供无需更换键盘的多语言输入体验。此外,探索 String Catalog 的最新改进如何帮你更轻松地实现本地化。
章节
- 0:00 - Introduction
- 2:24 - Input
- 8:08 - Display
- 11:01 - Localization
- 16:07 - Wrap-up
资源
相关视频
WWDC24
WWDC23
WWDC21
WWDC20
-
下载
Hello آداب (ādāb) नमस्ते (namaste) 大家好 (dàjiāhǎo) 我叫 Karan 很高兴向大家介绍 如何构建支持多语言的出色 App 当今世界上大多数人 在成长过程中说的语言不止一种 或是接触到多种语言 因此 无论你是在温哥华找书看 在台北吃刨冰 还是在德里坐地铁 你都身处一个多语言社会 周围的人都在用几十种不同 的语言交谈 发短信 阅读和倾听 我们每天都在使用 App 与人交谈 写手记 看电影 看书 我们希望我们的 App 能 很好地支持我们使用的所有语言
因此 今天我想与大家分享一些技巧 告诉大家如何让你的 App 为多语言用户带来出色的体验 开始之前 我将和大家分享一些我们 在 Apple 正忙于开展的工作 今年 我们将推出几项新功能 旨在提高多语言体验的标准 并扩展现有功能以支持更多语言 让我们来看看其中的几项新功能
首先 新的多语言键盘 可以让你输入多种语言 而无需手动切换语言
如果你既会说韩语又会说英语 那么在 iOS 18 中 我们将推出 独特的多脚本输入体验
许多现有功能都支持更多语言 比如“实况文本” 增加了对阿拉伯语的支持 而且为了让你的墙纸和表盘上的数字 更具个性 我们新增了 10 种印度文字 最后 很高兴与大家分享一个好消息 适用于全新 M4 iPad Pro 的妙控键盘 重新设计了阿拉伯语功能行 音量和亮度键现在可以 在硬件和软件之间 准确映射
这只是 Apple 将软件和硬件融合成 单一无缝体验的众多方式之一 我们非常期待你能尝试 所有这些出色的新功能 现在我们来讨论一下如何构建 支持多语言的出色 App 今天我将介绍三个部分: 输入、显示和本地化 让我们从输入开始 我们的多语言之旅 三位好朋友将在 旅程中助我们一臂之力 他们来自世界上不同的地方 说着不同的语言 在他们的帮助下 我们将能直观地看到一些典型的 多语言应用场景 我们按照 从东方到西方的顺序进行展示 要讨论输入问题 让我们看看 April 生活中的一天 April 住在新加坡 会说英语和中文 她和好朋友 Farah 用英语交谈
她和搭档用中文交谈
当 April 在 每个对话中输入内容时 键盘就会记住她和他们每个人 说的是哪种语言 我将向你展示 如何在你的 App 中做到这一点 真的非常简单 你只需覆盖 textInputContextIdentifier 这是 UIResponder 上的一个方法 可以返回唯一字符串标识符 现在 当 April 使用手写键盘写中文时 她经常拉长键盘让键盘变高 因为这样写起来更容易 当她这样做时 “信息”App 会 将自身的 UI 上移以腾出空间
这里有两种方法 首先 如果你需要在键盘正上方 放置视图 我推荐你使用 inputAccessoryView 它可以将视图停靠在键盘上方 还可以通过将视图停靠在屏幕底部 来处理使用硬件键盘输入的情况 另一种方法是使用 keyboardLayoutGuide 如果你想自定视图相对于 键盘的位置 它将很有帮助 此外 当你输入 中文或日文等语言时 键盘会使用带标记的文本 什么是带标记的文本呢? 我来给你演示一下 带标记的文本是在你选择文本建议 之前的临时文本 并且具有下划线外观 一旦文本建议得到确认 带标记的文本就会消失
在这里 我展示了一个错误 即 App 在每次按键时 都会修改文本 而且会中断输入 App 为何会这样做?这通常是为了 在你键入内容时提供自动填写功能 因此 让我们来看看如何 在 April 输入中文的同时 显示有用的内联补全 关键是在修改文本之前 要检查确保没有任何带标记的文本 如果 markedTextRange 返回空结果 我们可以放心地修改文本 以显示内联自动补全功能 请注意当存在带标记的文本时 你仍然可以根据相应文本执行搜索 并在表格视图等 其他地方显示建议 事实上 “聚焦”正是这样做的 你会看到 当我输入时 上面的建议就会更新 而“聚焦”只有在我确认关键词后 才会更改内联文本 谢谢你 April 现在 Raj 会用一些示例 向我们展示如何打造 出色的搜索体验
Raj 住在印度 他会说印地语和英语 他用印地语搜索时 有时找不到想要的内容 我们来看看为什么会出现 这种情况以及如何解决
每种语言都有规则来区分哪些 被视为完全不同的拼写 哪些是相关拼写
在英语中 我们在用 小写字母 a 搜索 apple 时 会匹配到带大写字母 A 的 Apple 德语等其他语言也有类似的规则 在 Raj 的这个例子中 他输入姓名时的拼写方式 与通讯录中姓名的输入方式 不完全一致 好消息是 我们只需修改 一行代码就可以解决这个问题 就是使用 localizedStandardRange 没错 localizedStandardRange 是一种遵循特定语言区约定 的标准搜索方式 今年 我们还对这个 API 的工作方式 进行了重大升级 现在它支持更多文字中 不同拼写约定的匹配 以及跨文字匹配数字的功能 有了 localizedStandardRange 和 iOS 18 中的新增强功能 Raj 就可以按照自己喜欢的方式输入 并轻松找到他要找的联系人
还有最后一个问题 Raj 在搜索栏位中输入时 有时会遇到这个问题 让我们来一探究竟
在某些 App 中 当 Raj 键入印地语时 文本看起来是零乱的
这是因为 App 突出显示了 他所输入的文字部分
在印地语和许多其他语言中 元音和其他标记都附着在字母上 为了给大家演示 我逐键输入一个词 क्षितिज (kshitij) 意思是地平线 当我这样做时 请注意它是如何变化的
正如你所见 在印地语中 按一个键有时无法在右侧插入字母 它还可以修改现有的字母
因为从技术上讲 正体和粗体是不同的字体 而且一种字体中的字母不能 与另一种字体中的符号相连接 这就是导致文本看起来零乱的原因 但好消息是 你可以使用一种颜色 来突出显示文本的某一部分 下面是我们为属性字符串 添加颜色属性的方法 说到这个话题 我还想 就斜体问题提个醒 这里有 hello 的 10 种语言版本 现在我要将它们全部标为斜体 请注意 这 10 种语言中只有 3 种 语言发生了变化 因为其他语言 没有斜体的概念 应用斜体不会产生视觉差异 因此请谨慎使用斜体 因为翻译时可能会失去这种差异
好了 现在我们使用 一种颜色而不是将文本加粗 Raj 就可以阅读搜索结果了 而且这种区分仍然可以让他看到 单词的哪个部分与 他输入的文本相匹配 “输入”部分到这里就讲完了 谢谢 Raj 和 April! 现在我们来讨论一下如何显示文本 为了便于展示 让我们通过 Ismat 的眼睛来看看这个方面
Ismat 现居美国 会说英语和乌尔都语
Ismat 注意到 虽然大多数 App 都能很好地显示乌尔都语文本 但她时不时会遇到一个 App 其中的文本显示得非常零乱 难以辨认 这里显示的是乌尔都语文字名称 一词 نستعلیق (Nastālīq) 左边显示正确 右边显示错误 解决方法很简单 使用我们的下一代文本引擎 TextKit 2 它能确保所有脚本正确渲染 好消息是 当你在 SwiftUI、 AppKit 或 UIKit 中使用标签或 文本视图时 你已经在使用它了
Ismat 正在重温 一段回忆过去的手记
值得称道的是 虽然运行的 这个“手记”App 是英文版 但得益于 TextKit 2 乌尔都语文本的渲染效果非常出色 不过 她注意到虽然“手记”和大多数 App 都能很好地兼容乌尔都语文本 但在某些 App 中 乌尔都语 的文本行会被挤在一起 难以阅读 尽管“手记”没有专门的 乌尔都语代码 它却能恰当处理乌尔都语文本 因为和许多 App 一样 它也使用了文本样式 这些样式不仅考虑了 App 的语言 而且就像 Ismat 的情况一样 它们还考虑了用户的所有语言 因此用户偏好的任何语言的文本 都能在任何 App 中正确呈现 文本样式的使用非常简单 以下是在 SwiftUI、 UIKit 和 AppKit 中 初始化文本样式的不同方法 还请记住 对于任何标签或文本视图 默认处于关闭状态的 clipsToBounds 应设置为 false 因为许多语言的文本 需要渲染到视图边界之外 否则会变得难以辨认
在某些情况下 你可能 需要显示特定语言的内容 并希望获得这个语言的 最佳排版效果 这种情况下 你可以 指定 typesettingLanguage
左边显示的是 默认设置 它将所有首选语言 都考虑在内 在 Ismat 的例子中 就包括乌尔都语 右边显示的是同一视图 其中 typesettingLanguage 设置为 “English” 这样就可以 减少行间距 最后 April、Raj 和 Ismat 都想强调正确显示姓名的重要性 通过使用格式化 API 无论 App 使用哪种语言 都能确保正确显示 任何语言和文字的姓名 例如 在中文和日文等语言中 只显示名字并不合适 正确的做法是 使用格式化程序中 的简短样式 这个样式只在适当的时候使用名字 对于字母组合 请使用缩写样式
现在我们向 April、Raj 和 Ismat 告别 接下来谈谈你每天对 App 进行 本地化时会用到的工具 都有哪些令人兴奋的增强功能
首先是 String Catalogs String Catalogs 是 Xcode 中 本地化的基础 通过保持字符串与代码同步 和提供强大的编辑工具 简化了项目中的翻译管理过程 我们来看看 String Catalogs 中的新功能
String Catalogs 现在可以检测到 常见的验证问题 比如格式说明符不匹配 甚至只需点按一下 就能修复这些问题
接下来 你现在可以将特定字符串 标记为“Don’t Translate” 这样就可以轻松向翻译人员表明 你仍在对内容进行最终定稿
最后 你现在可以轻松地 在特定字符串的 代码和目录之间来回跳转 我们希望 String Catalogs 的所有这些增强功能 都能帮助你加快 开发和本地化的工作流程 接下来 让我们来谈谈如何通过 更加个性化和更具包容性的方式 让 App 中的每个字符串都大放异彩
借助功能强大的 自动语法一致性引擎 你的 App 可以通过 “语言与地区”设置中的 称呼方式 更为个性化地称呼他人
你的 App 也可以 使用代词来指代他人 最后 有了语法引擎 你不仅可以确保 App 中的 所有文本语法正确 而且无需为单个文本 创建数十种字符串 变体 今年 我们将把语法引擎 引入另外两种语言 第一个语言是印地语 如果你使用印地语设备 现在可以在“语言与地区”设置中 指定称呼并选择你希望的称呼方式
我们还将语法引擎 引入韩语中 现在可以 根据所跟的词来改变助词的位置 现在如果你的 App 支持德语、英语、西班牙语、法语、 意大利语、葡萄牙语、印地语或韩语 你就可以利用自动语法一致性功能
接下来 让我们谈谈数字 英语使用阿拉伯数字 其他几十种语言也使用这些数字 不过 阿拉伯数字是世界各地 使用的多种不同编号系统之一 对于支持多种数字的语言 用户可以在“语言与地区”设置中 选择自己偏好的数字 好消息是 当你使用格式化程序时 数字会自动遵循 所有特定语言区规则 我将向大家展示一些不使用 格式化程序的简单的数字格式化方法 “天气”App 中的这个文本 表示的是“10 天预报” 通常你会有一个可本地化的字符串 来表示这个预报
有两种方法可以将它本地化 第一种方法是使用插值法 在代码中格式化数字 由于 numberOfDays 位于 Text 中 因此会根据当前的语言区 自动格式化 如果数字将在运行时确定 而且所有语言都将在这个字符串中 使用数字 这种方法就会很有效
今年 我们还引入了 第二种数字格式化方式 即 直接在字符串中进行格式化 在所有相同情况下 你都可以使用它 就像在代码中设置格式一样 不过 它最大的优点是 可以让你轻松地 在本地化字符串中格式化数字 代码量为零
在第一个示例中 我们可以使用这种方法 来本地化 10 天预报字符串 在这个示例中 源字符串为英文 翻译后的字符串为印地语 使用 formatNumber 这可确保在运行时使用 正确的编号系统 并遵循“语言与地区”设置中 的数字设置
除编号系统外 它还能处理数字格式的其他方面 如小数分隔符 这意味着对于西班牙语 一个字符串可同时 用于西班牙和美国
对于支持多种本地化语言的 App 如果用户的“语言与地区”设置中 有多种语言 就会自动显示语言设置 并允许用户独立于 整体设备语言 选择特定的 App 语言 这项功能非常受多语言用户的欢迎 因为他们经常使用 不同语言的不同 App 对于你的 App 如果你希望 在“设置”中始终显示语言选项 而不是仅在用户使用一种以上 首选语言时才显示 你可以在 InfoPlist 中将 UIPrefersShowingLanguageSettings 指定为 yes
如果需要 你还可以添加一项可见功能 如按钮 用于直接跳转到 App 的设置 最后 我想说的是 SF Symbols 是本地化工具箱中 的重要工具 因为这些符号已本地化为 多种语言和脚本 Apple App 中的所有常用符号 都使用 SF Symbols 例如 textformat 它用在调出格式控件的按钮上 即使是像签名这样具有奇思妙想 元素的符号 在每种语言中 都经过定制 看起来很自然 SF Symbols 支持从右到左的语言 以及不同的编号系统 所以每当你寻找 要在 App 中使用的符号时 首先请务必查看 SF Symbols 庞大的资源库
总之 在当今的多语言世界中 用户会在你的 App 中 使用他们的任何语言 你可以通过检查键盘输入和显示 是否在任何语言下都运行良好 来确保用户获得出色的体验 此外 利用 String Catalogs 等工具 你还可以大大加快 开发和本地化的工作流程 我已经迫不及待想看到 你们制作的多语言 App! Thank you شکریہ (shukriyā) धन्यवाद (dhanyavād) 謝謝 (xièxiè)
-
-
3:18 - Specify textInputContextIdentifier
override var textInputContextIdentifier: String? { uniqueID }
-
3:41 - Place a view directly above the keyboard
textView.inputAccessoryView = viewAboveKeyboard
-
4:00 - Use keyboardLayoutGuide to adapt to keyboard
view.keyboardLayoutGuide.topAnchor.constraint(equalToSystemSpacingBelow: textView.bottomAnchor, multiplier: 1.0).isActive = true
-
4:42 - Check for marked text before modifying
if textView.markedTextRange.empty { // Perform actions involving editing text }
-
5:58 - Use localizedStandardRange when searching
let range = text.localizedStandardRange(of: search)
-
7:24 - Use color differences to highlight text
attributedString[range].foregroundColor = highlightColor
-
9:39 - Text Styles
// Text Styles // SwiftUI Text("Hello, world!") // uses .body Text Style by default Text("Hello, world!").font(.title) // UIKit let label = UILabel() label.text = "Hello, world!" label.font = UIFont.preferredFont(forTextStyle: .body) // AppKit let textField = NSTextField(labelWithString: "Hello, world!") textField.font = NSFont.preferredFont(forTextStyle: .body) // Keep clipsToBounds off clipsToBounds = false
-
10:03 - Typesetting language
// Typesetting language // SwiftUI Text(verbatim: "Hello, world!").typesettingLanguage(.init(languageCode: .english)) // UIKit let label = UILabel() label.text = "Hello, world!" label.traitOverrides.typesettingLanguage = Locale.Language(languageCode: .english)
-
10:29 - Formatting names
// Formatting names let nameComponents = PersonNameComponents (givenName: "瑗珺", familyName: "汪", nickname: "珺珺") // Short Name (respects settings like “Prefer Nicknames”) let shortName = nameComponents.formatted(.name(style: .short)) // 珺珺 // Abbreviated Name (can be used for monograms) let monogram = nameComponents.formatted(.name(style: .abbreviated)) // 汪
-
12:20 - Examples of personalizing text
"^[Nuestro %@](inflect: true) está ^[hecho](agreeWithArgument: 1) de %@." "अगर आप पहुँच नहीं ^[पाते हैं](inflect: true)" "예: ‘^[%@을](inflect: true) 켤 때’"
-
13:43 - Format numbers using Text
Text("\(numberOfDays)-day forecast")
-
14:21 - Format numbers using AttributedString
AttributedString(localized: "10-day forecast") AttributedString(localized: "0.5× zoom")
-
15:23 - Launch to your app’s settings
// Launch to your app’s settings if let url = URL(string: UIApplication.openSettingsURLString) { await UIApplication.shared.open(url) }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。