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(Text、Button 等)默认就走本地化通道,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 项目:#bundle 与 tableName(09: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(如 TITLE、SUBTITLE),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
- Discover String Catalogs — 深入 String Catalog 的能力、互操作性与翻译工具协作
- Code-along: Elevate an app with Swift concurrency — 另一场 code-along,演练 Swift 并发改造流程
- What’s new in Swift — Swift 语言与工具链的年度更新,含宏与符号相关进展
- What’s new in Xcode — Xcode 26 的整体新特性概览,含本地化工作流
评论
GitHub Issues · utterances