WWDC Quick Look 💓 By SwiftGGTeam
Code-along: Explore localization with Xcode

Code-along: Explore localization with Xcode

观看原视频

Highlight

Xcode 26 用端侧模型自动为 String Catalog 生成翻译注释,并支持把字符串编译成类型安全的 Swift 符号。


核心内容

翻译人员通常看不到代码,也碰不到运行中的 app。一个孤零零的 “Landmarks” 字符串送出去,对方只能猜:是 app 名,还是地图上的地标?同样,”%@ is contained in %@” 里两个占位符到底是什么含义,没有上下文就无法翻译正确。这是本地化项目里最常见的痛点:开发者写完字符串就走了,翻译人员只能反复回来追问。

Andreas 用 Landmarks 示例项目从零演示 Xcode 的本地化流水线,重点放在 Xcode 26 的两个新工具:自动注释生成符号生成。前者用端侧模型分析字符串出现的代码位置,自动写出 “The text label on a button to cancel the deletion of a collection” 这样的描述;后者把 String Catalog 里的条目编译成 LocalizedStringResource 上的静态属性或函数,写代码时享受自动补全和编译期检查。两个特性都在解决同一类问题:让字符串和它的上下文之间不再脱节。


详细内容

起步:让 Xcode 自动收集字符串01:34

新建一个名为 Localizable 的 String Catalog,build 一次,Xcode 就会扫描代码并把所有可本地化的字符串写进 catalog。SwiftUI 里大部分 API(TextButton 等)默认就走本地化通道,Foundation 这边用 String(localized:) 显式标记。

// import SwiftUI
Text("Featured Landmark", comment: "Big headline in the hero image of featured landmarks.")

Button("Keep") { }

// import Foundation
String(localized: "New Collection", comment: "Default name for a new user-created collection.")

关键点:

  • Text("Featured Landmark", comment:):SwiftUI 的 Text 直接接受字符串字面量作为本地化键,comment 参数同时作为翻译人员的上下文写进 String Catalog。
  • Button("Keep") { }Button 的标题参数同样默认本地化,无需任何额外标注。
  • String(localized:):Foundation 侧的对应 API,用在 ViewModel、Model、工具函数等非 SwiftUI 代码里。

字符串中带 %lld 这类占位符时,可以右键选 “Vary by Plural”,分别填写 one / other 形式,运行时系统按数字自动选择。

给翻译人员加上下文06:00

Andreas 把好注释拆成三件事:用在哪个界面元素(按钮、副标题、tab bar)、周围有什么界面元素、占位符里会出现什么内容。

Text("Delete",
comment: "Delete button shown in an alert asking for confirmation to delete the collection.")

String(localized: "Shared by Friends", comment: "Subtitle of post that was shared by friends.")

关键点:

  • 第一行注释明确了 “Delete” 出现在 alert 里、是确认删除的按钮,避免翻译人员把它误当成一个动词标题。
  • 第二行注释说明 “Shared by Friends” 是 post 的副标题,翻译时可以选用更口语化的表达。

Xcode 26 在字符串右键菜单里新增 “Generate Comment”。它用端侧模型分析字符串所在的代码位置,自动写出注释,开发者可以接受、改写或在其上追加内容。在 Settings → Editing 里打开 “automatically generate string catalog comments” 后,所有新提取的字符串都会自动获得注释。

导出的 XLIFF 文件里,自动生成的注释带有 auto-generated 标记,方便翻译工具识别(09:13):

<trans-unit id="Grand Canyon" xml:space="preserve">
<source>Grand Canyon</source>
<target state="new">Grand Canyon</target>
<note from="auto-generated">Suggestion for searching landmarks</note>
</trans-unit>

多 target 项目:#bundletableName09:58

项目拆出 framework 或 Swift Package 之后,每个 target 可能有自己的 String Catalog。本地化 API 需要 bundle 参数才能在运行时找到正确的资源。

// 主 app 内
Text("My Collections",
comment: "Section title above user-created collections.")

// Swift Package 或 Framework 内
Text("My Collections",
bundle: #bundle,
comment: "Section title above user-created collections.")

关键点:

  • 主 app 里省略 bundle 参数等价于 Bundle.main,Xcode 直接到主 bundle 找资源。
  • #bundle 是 Xcode 26 引入的宏,编译期展开成当前 target 对应的 bundle,写在 framework 或 Swift Package 里就找到 framework 自己的资源。
  • #bundle 在旧版本 OS 上同样工作,迁移时无需检查可用性。

要把字符串按功能分文件管理时,加 tableName 参数即可(10:56):

Text("My Collections",
tableName: "Discover",
comment: "Section title above user-created collections.")

Text("My Collections",
tableName: "Discover",
bundle: #bundle,
comment: "Section title above user-created collections.")

关键点:

  • tableName: "Discover" 把这条字符串提取到 Discover.xcstrings 文件里,而不是默认的 Localizable.xcstrings
  • 一个 String Catalog 文件就是一张 table,用 table 把字符串按 feature、screen、user flow 分组,避免单文件过大。

类型安全的符号生成17:31

Xcode 26 支持反向流程:先在 String Catalog 里手动添加 key(如 TITLESUBTITLE),Xcode 自动在 LocalizedStringResource 上生成对应的静态属性或函数;带占位符的条目生成函数,占位符名作为参数标签。

// SwiftUI 中使用符号
Text(.introductionTitle)

.navigationSubtitle(.subtitle(friendsPosts: 42))


// Foundation 中使用符号
String(localized: .curatedCollection)


// 自定义类型接受 LocalizedStringResource
struct CollectionDetailEditingView: View {
    let title: LocalizedStringResource

    init(title: LocalizedStringResource) {
        self.title = title
    }
}
CollectionDetailEditingView(title: .editingTitle)

关键点:

  • Text(.introductionTitle):无占位符的 key 生成静态属性,前面加 . 直接调用,类型推断成 LocalizedStringResource
  • .subtitle(friendsPosts: 42):含占位符的 key 生成函数,占位符名 friendsPosts 作为参数标签,编译期检查类型。
  • String(localized: .curatedCollection):Foundation 的 String(localized:) 同样接受 LocalizedStringResource,符号在 SwiftUI / 非 SwiftUI 代码间通用。
  • 默认 table Localizable 的符号直接挂在 LocalizedStringResource 上;自定义 table(例如 Discover)的符号嵌套在 LocalizedStringResource.Discover 命名空间下,需要 .Discover.xxx 访问。
  • 新建项目默认开启;老项目打开 build setting “Generate String Catalog Symbols” 即可启用。

两种工作流可以通过 “Refactor > Convert Strings to Symbols” 互转,Xcode 会预览每个调用点的改写效果(19:37)。


核心启发

  • 做什么:在团队 Xcode Settings 里默认开启 “automatically generate string catalog comments”。

    • 为什么值得做:翻译人员的返工大头来自上下文缺失,端侧模型生成的描述能覆盖大部分日常字符串,开发者只在不准确时手工微调。
    • 怎么开始:Settings → Editing → 勾选该选项;导出 XLIFF 时确认条目带 auto-generated 标签,告诉翻译工具这是机器初稿。
  • 做什么:把多 target 项目里的硬编码 Bundle(for:) 替换成 #bundle 宏。

    • 为什么值得做:#bundle 在编译期展开,避免运行时反射查找类型,也省去为每个 framework 写 helper 的重复代码。
    • 怎么开始:在 framework 和 Swift Package 里全文搜索 Bundle(for:Bundle.module,逐个换成 bundle: #bundle,build 一次确认 String Catalog 的 bundle 列正确指向当前 target。
  • 做什么:给规模超过 200 条字符串的 catalog 拆 table,并按 feature 命名(如 Discover、Settings、Profile)。

    • 为什么值得做:单文件 String Catalog 过大时 Xcode 编辑卡顿,且翻译协作冲突高;拆 table 让多人改不同 feature 互不干扰。
    • 怎么开始:在每个 feature 模块新建 <Feature>.xcstrings,调用处加 tableName: "<Feature>",配合 #bundle 让 framework 自动定位。
  • 做什么:成熟 feature 用 “Refactor > Convert Strings to Symbols” 转成符号引用。

    • 为什么值得做:符号让 key 和 value 解耦,文案迭代时只改 catalog,不动代码;自动补全减少拼写错误;编译期检查占位符类型。
    • 怎么开始:先在新 feature 试点,开启 build setting “Generate String Catalog Symbols”;用预览界面逐条确认 key 名(如把自动生成的 feedTitle 改成更语义化的名字)后再 commit。

关联 Session

评论

GitHub Issues · utterances