Highlight
Apple 把 Translate App 的设备端 ML 模型开放给开发者,提供
.translationPresentation()系统浮层和TranslationSession灵活 API 两层能力。
核心内容
你做了一个泛欧洲徒步 App,用户评论可能是德语、日语、荷兰语——只做本地化不够,因为用户生成内容的语言你无法控制。之前要解决这个问题,得自己接入第三方翻译服务,或者让用户复制文本到系统翻译 App 里再切回来。
Apple 在 iOS 18 里把 Translate App 背后的设备端 ML 模型通过 Translation 框架开放出来。第一层是 .translationPresentation() —— 一个 SwiftUI modifier,加一行代码就能在你的 App 里弹出系统翻译浮层,体验和系统全局翻译一模一样。第二层是 TranslationSession —— 一个更灵活的 async API,让你拿到翻译结果后自己决定怎么展示,支持批量翻译、语言检测、自动语言选择。
这两套 API 共享同一套设备端 ML 模型。语言包已经在系统中存在(用户下载过的),你的 App 可以直接使用。如果用户没下载某个语言,框架会自动弹出下载提示,下载在后台进行,用户离开你的 App 也不影响。今年新增了印地语支持,覆盖了和 Translate App 相同的语言列表。
详细内容
系统浮层翻译适合”偶尔翻译一下”的场景——用户遇到不懂的文本,点一下看到结果就够了。实现方式是给 view 加一个 .translationPresentation() modifier,配合一个 state 变量控制弹窗显隐(03:00):
// 存储控制翻译浮层的状态
@State private var showsTranslation = false
// 在上下文菜单里加一个按钮触发翻译
ContextMenu {
Button("Translate") {
showsTranslation = true
}
} // ([02:51-03:09](https://developer.apple.com/videos/play/wwdc2024/10117/?time=171))
// 给 view 加上 translationPresentation modifier
.translationPresentation(isPresented: $showsTranslation, text: reviewText)
@State private var showsTranslation = false—— 存储翻译浮层的显示状态showsTranslation = true—— 用户点击翻译按钮时设置为 true,触发浮层.translationPresentation(isPresented:text:)—— 当 isPresented 为 true 时自动弹出系统翻译浮层,text 是要翻译的内容
这种实现非常轻量,用户还能自己切换目标语言。但如果你需要同时显示多条翻译(比如一整个评论列表),或者想把翻译结果嵌入到自己的 UI 里,就需要用 TranslationSession。
TranslationSession 是一个 async API,你不能直接实例化它——而是通过 .translationTask() modifier 从框架获取实例(04:30):
// 存储翻译 session 的配置
@State private var configuration: TranslationSession.Configuration?
// 给 view 加上 translationTask modifier
.translationTask(configuration) { session in
// 配置变化时这个闭包会被调用
let requests = filteredReviews.map { review in
TranslationSession.Request(text: review.text)
}
// 批量翻译,结果流式返回
for await result in session.translate(requests) {
switch result {
case .success(let translation):
updateReview(with: translation)
case .failure(let error):
handleError(error)
}
}
}
@State private var configuration—— 存储翻译配置,设置为 nil 时不会触发翻译.translationTask(configuration:)—— 当 configuration 从 nil 变成非 nil 时,框架会调用闭包并传入 sessionTranslationSession.Request(text:)—— 封装要翻译的文本session.translate(requests)—— 返回 AsyncSequence,结果流式到达for await result in—— 处理每个翻译结果,成功就更新 UI,失败就处理错误
触发翻译的逻辑是给 configuration 赋一个新值(06:48):
if configuration == nil {
// 首次翻译,用默认配置(自动检测源语言、自动选择目标语言)
configuration = TranslationSession.Configuration()
} else {
// 重复翻译,调用 invalidate() 让框架知道内容变了
configuration?.invalidate()
}
configuration == nil判断是否首次翻译TranslationSession.Configuration()默认构造函数会自动选择语言configuration?.invalidate()让 SwiftUI 知道配置变了,重新运行 translationTask 闭包
关于语言选择,你可以手动指定源语言和目标语言,也可以传 nil 让框架自动处理(09:00):
// 手动指定语言
TranslationSession.Configuration(
source: Locale.Language(identifier: "es"),
target: Locale.Language(identifier: "en")
)
// 自动检测源语言、自动选择目标语言
TranslationSession.Configuration(
source: nil, // 自动检测内容语言
target: nil // 基于用户偏好语言自动选择
)
source: nil—— 框架会自动识别文本语言,识别不出来会询问用户target: nil—— 框架根据源语言和用户偏好语言自动选择目标语言
关于语言检测,框架推荐直接传 nil 给 TranslationSession,而不是自己调 NLLanguageRecognizer(10:03)。如果你确实需要单独检测语言,可以这样:
let recognizer = NLLanguageRecognizer()
recognizer.processString(text)
if let dominantLanguage = recognizer.dominantLanguage {
let language = Locale.Language(identifier: dominantLanguage.rawValue)
// 把 language 传给 TranslationSession
}
检查语言支持情况用 LanguageAvailability API(11:53):
// 获取所有支持翻译的语言
let supportedLanguages = LanguageAvailability.supportedLanguages
// 检查某对语言组合是否支持翻译
let status = LanguageAvailability.status(
from: Locale.Language(identifier: "en"),
to: Locale.Language(identifier: "hi")
)
switch status {
case .supported:
print("支持")
case .installed:
print("支持且已下载")
case .unsupported:
print("不支持")
}
supportedLanguages—— 返回所有支持翻译的语言列表,和 Translate App 一致status(from:to:)—— 检查从源语言到目标语言的翻译是否支持.installed表示该语言对支持翻译,且语言包已下载到设备
批量翻译时有个重要约束:同一批请求里的所有文本必须是同一种语言(13:37)。混着德语和西班牙语一起翻译会输出乱码。正确做法是按语言分批:
// ❌ 错误:把不同语言混在一批请求里
let mixedRequests = [
TranslationSession.Request(text: germanText1),
TranslationSession.Request(text: spanishText1)
]
// ✅ 正确:分开两批翻译
for await result in session.translate(germanRequests) {
// 处理德语翻译结果
}
for await result in session.translate(spanishRequests) {
// 处理西班牙语翻译结果
}
按语言分批时,session 的源语言必须设为 nil,这样框架才能对每批做语言识别。如果想提前让用户批准下载语言包(比如用户要离线使用翻译),可以调用 prepareTranslation()(14:50):
try await session.prepareTranslation(
from: Locale.Language(identifier: "en"),
to: Locale.Language(identifier: "ja")
)
最后注意 TranslationSession 的生命周期问题——它被绑定到 view 上,如果 view 消失了 session 就不可用了。不要把 session 存到持久化模型对象里(15:13)。
核心启发
-
先用
.translationPresentation()验证需求,再用TranslationSession做深度集成- 为什么值得做:系统浮层零成本接入,能快速验证翻译对你 App 的价值——如果用户很少用,就不值得投入资源做 UI 集成
- 怎么开始:给需要翻译的 view 加
.translationPresentation(),配合一个菜单按钮触发,观察用户使用频率
-
为离线场景提前下载语言包
- 为什么值得做:翻译功能在户外旅行、地铁通勤等弱网场景最有价值,提前下载能保证体验不中断
- 怎么开始:检测到用户进入”离线模式”或者网络不稳定时,调用
prepareTranslation()请求下载批准
-
按语言分批做批量翻译
- 为什么值得做:混着不同语言的一批请求会输出乱码,按语言分批才能保证翻译质量
- 怎么开始:先用
NLLanguageRecognizer或者让框架自动检测每条文本的语言,按检测到的语言分组,再分别调用session.translate()
关联 Session
- Bring your app to Siri — 用 App Intents 把翻译功能暴露给 Siri,用户可以说”翻译这条评论”
- Bring your app’s core features to users with App Intents — 通过 App Intents 让系统搜索和快捷指令调用你的翻译功能
- What’s new in Foundation — 了解 Locale.Language 等 Foundation 语言处理 API
- Integrate your app with Productivity broadcasts — 通过广播触发翻译任务等自动化场景
评论
GitHub Issues · utterances