Apple 的 Translation 框架:免费、设备端运行,且比看上去更出色
Apple 的 Translation 框架在设备上完成文本翻译,免费,不需要 API 密钥,语言安装好之后翻译过程也不会发起任何网络请求1。它构建于 Core ML 模型之上,随系统一同提供,让应用获得与「翻译」app 相同的翻译引擎。过去要做多语言功能,往往意味着一笔云端翻译账单和一个隐私顾虑;如今这部分成本再次趋近于零。与 Foundation Models 框架 一样,真正有意思的并不是一帆风顺的主路径,而是演示会略过的那些边角:会卡住首次翻译的语言下载、悄无声息拒绝工作的模拟器,以及只支持 SwiftUI、从而决定了你如何接入它的接口面。
TL;DR
- 两个接口面,两个 iOS 版本。
translationPresentation会弹出 Apple 内置的翻译浮层(iOS 17.4+)。TranslationSession通过translationTask修饰符获取,可在你自己的 UI 中进行程序化翻译(iOS 18+)2。 - 程序化翻译是
async的。 在translationTask闭包内你会拿到一个TranslationSession;调用它即可翻译单个字符串或一批字符串3。 - 批量翻译是一等公民。 一次请求即可翻译一整个列表,并让每条结果与其输入保持对应,而不必逐条循环、逐条等待3。
- 翻译 UI 只支持 SwiftUI,并且无法在 iOS 模拟器中运行。 这两点都很容易在踩坑之后才发现;请据此进行设计和测试4。
- 离线意味着先要下载。 某个语言对的首次翻译会下载语言包,这是一个需要你处理的真实 UX 环节,而非细枝末节5。
- 值得记住的搭配:用 Translation 翻译用户输入,再用 Foundation Models 在其上进行推理——这样一个单语的设备端智能体功能,就能在设备所能安装的任何语言下工作。
两个接口面:系统浮层与你自己的 UI
这套框架给了你两种截然不同的翻译方式,而选对其中之一,决策也就完成了大半。
轻量的一种是 translationPresentation,从 iOS 17.4 起可用。你把它附加到某个视图上,绑定一个 isPresented 标志,并传入文本;当标志翻转为 true 时,系统会在你的内容上方滑出它自己的翻译浮层2:
.translationPresentation(isPresented: $showTranslation, text: selectedText)
你无需编写任何翻译逻辑,也看不到翻译结果;UI 与交互都由 Apple 掌控。对于「让用户翻译这段文字」这类需求,这就是功能的全部,再去动用更重的方案纯属白费力气。
程序化的接口面是 TranslationSession,从 iOS 18 起可用,你通过 translationTask 修饰符来获取它。该修饰符会运行一个异步闭包,并把一个由你自己调用的会话交给你,于是翻译后的文本会回到你的代码里,由你按自己的方式渲染2:
.translationTask(configuration) { session in
let response = try await session.translate("Good morning")
await MainActor.run { translated = response.targetText }
}
二者的分工很清晰。translationPresentation 用于在 Apple 的 UI 中向用户展示译文。TranslationSession 则用于把译文取回到你的数据和你的视图里。凡是不止于一次性「翻译这段」的应用,多半都会选用会话。
批量翻译:翻译整个列表,而不是循环逐条翻译
区分顺滑功能与卡顿功能的关键,在于批量处理。如果你要翻译一个列表(聊天消息、目录条目、一组标签),不要循环逐条 await 翻译。TranslationSession 接收一批请求,并一次性返回各自与请求匹配的响应3:
.translationTask(configuration) { session in
let requests = items.map { TranslationSession.Request(sourceText: $0.text, clientIdentifier: $0.id) }
for try await response in session.translate(batch: requests) {
store[response.clientIdentifier] = response.targetText
}
}
真正起作用的是 clientIdentifier:它会随响应一并返回,于是你无需依赖顺序,就能把每条译文对应到它所属的那一行。批量处理还让框架得以高效调度任务,省去在循环中逐次调用的开销。只要不止一个字符串,就走批量。
没人会截图展示的离线真相
这里就是那个把干净演示变成工单的边角。翻译固然在设备上运行,但语言得先到设备上才行。当你的应用首次翻译某个源语言到目标语言的语言对时,系统会下载该语言包,而这次下载既要花时间,也需要网络连接5。如果你发起翻译并直接渲染结果,却对下载毫无处理,那么对每一种新语言,你的功能在首次使用时都会显得像是卡死了。
请有意识地处理它。框架允许你检查语言可用性,并在真正需要之前提前准备(下载)某个语言对,这样你就能呈现一个「正在准备翻译」的状态,或在更从容的时刻预先下载,而不必在交互途中干等5。心智模型是这样的:把某个语言对的首次翻译当成一次一次性的资源下载,因为它本就如此。围绕「下载确实存在」这一前提来设计 UX,离线又免费的好处便能落地;忽视它,好处就被一次卡顿给遮住了。
还有两件事,若你太晚才知晓,会白白搭进一个下午。翻译 UI 的接口面是 SwiftUI 修饰符,因此 UIKit 界面需要承载一个 SwiftUI 视图(通过 UIHostingController)才能用上它们;不过 TranslationSession 也可以直接构造,用于无 UI 的场景4。另外,该框架在 iOS 或 iPadOS 模拟器中并不运行;翻译要在真机上测试4。这两点都没有被大声说明,而且都很容易一头撞上。
把 Translation 与 Foundation Models 搭配使用
值得带走的综合心得,把这套框架与设备端技术栈的其余部分联系到了一起。大多数设备端语言处理,都默认输入语言是你的逻辑和系统模型能够妥善处理的那一种。真实用户可不会配合。Translation 框架填上了这道缝隙:把用户的输入翻译成你的功能进行推理时所用的语言,对译文运行 Foundation Models 的处理,再把结果翻译回去。
它的形态像一个括号:译入、推理、译出。
// 1. translate the user's text into English (session configured for their language -> en)
let english = try await inboundSession.translate(userText).targetText
// 2. reason on-device, monolingually, in English
let summary = try await LanguageModelSession()
.respond(to: "Summarize in one line: \(english)").content
// 3. translate the result back (a session configured for en -> their language)
let localized = try await outboundSession.translate(summary).targetText
工单分流功能、笔记摘要器、意图提取器:每一个都可以只用单语言写一次,再通过用 Translation 把 Foundation Models 调用括起来,做到在设备所能安装的任何语言下工作。两个方向使用两套会话配置(用户语言到英文,再从英文回到用户语言),因为一个会话只针对一个语言对配置。两层都在设备上、都免费,且没有任何数据离开手机,所以多语言版本不会比单语言版本多付出任何隐私或云端账单的代价。这种组合(译入、推理、译出)正是让一个小巧的设备端功能真正走向全球的范式,而它之所以可能,正是因为两端都在本地免费运行。
何时不该用它
设备端翻译既免费又私密,这使它成为应用内翻译的正确默认选项。但在几种诚实的情形下,它是错误的工具。
- 你需要尽可能最好的翻译质量,或最广的语言覆盖。 设备端模型不错,但并非业界最佳,可安装的语言集合也是有限的。对于高风险翻译(法律、医疗、出版内容),专门的云端翻译服务在质量和广度上仍然胜出。
- 你无法容忍首次使用的下载。 对于必须在首次启动时无网络即刻可用的功能,除非你在引导流程中预先下载,否则下载这一要求本身就可能让设备端翻译出局。
- 你的应用是 UIKit,且没有承载 SwiftUI 的空间,或者你的流程必须在模拟器中运行(例如一项自动化 UI 测试)。只支持 SwiftUI、不支持模拟器这两条是硬约束,而非建议。
这套框架是设备端工具箱里较为低调的一项胜利:一个真正免费、私密的翻译引擎,大多数应用都能在一个下午内接入。所需的本领,与这套技术栈其余部分所奖赏的本领是同一种。搞清楚哪个接口面合适(系统浮层还是你自己的会话),有列表时就走批量,并为下载而设计,而不是假装它不存在。做到这些,翻译就不再是一项云端依赖,而成为一项你能与设备免费提供的其他一切组合起来的本地能力。
FAQ
Apple 的 Translation 框架是免费且设备端运行的吗?
是的。翻译在设备上运行且免费,没有任何数据离开手机,这使它成为应用内翻译的正确默认选项。代价是翻译质量和语言覆盖只能算不错,而非业界顶尖,因此高风险工作可能仍需云端服务。
为什么首次翻译会卡住或需要下载?
翻译在设备上运行,但语言对必须先到设备上才行。当你首次翻译某个源语言到目标语言的语言对时,系统会下载该语言包,这需要时间和网络连接5。请检查可用性并提前准备(预先下载)该语言对,以便在真正需要之前就绪,这样你就能展示一个「正在准备」状态,而不必在交互途中干等。
给应用添加翻译有哪两种方式?
一种是通过 SwiftUI 的展示修饰符弹出系统浮层,另一种是用直接构造的 TranslationSession 驱动你自己的 UI,用于无 UI 的场景4。由于这些接口面都是 SwiftUI 修饰符,UIKit 界面需要通过 UIHostingController 承载一个 SwiftUI 视图才能用上它们。
Apple 的 Translation 框架能在模拟器中运行吗?
不能。该框架在 iOS 或 iPadOS 模拟器中并不运行,因此翻译要在真机上测试4。这条约束是硬性的,而非建议,它也意味着无法在自动化的模拟器 UI 测试中进行翻译。
如何让一个 Foundation Models 功能在任何语言下工作?
把它括起来:将用户的输入翻译成你的逻辑进行推理时所用的语言,对译文运行 Foundation Models 的处理,再把结果翻译回去。两个方向使用两套 TranslationSession 配置(用户语言到英文,再从英文回到用户语言),因为一个会话只针对一个语言对配置。两层都在设备上且免费,所以多语言版本不会多付出任何隐私或云端账单的代价。
什么时候不该使用设备端翻译?
当你需要尽可能最好的质量或最广的语言覆盖时(法律、医疗、出版内容);当某项功能必须在首次启动时无网络即刻可用而你又无法预先下载时;或者当你的流程必须在模拟器中运行、或为没有承载 SwiftUI 空间的 UIKit 时。最后这几条是硬性限制。
-
Apple Developer, “Translation” framework. A first-party framework for on-device, system-provided translation built on Core ML models, performing translation locally without a network call once language assets are installed. ↩
-
Apple Developer, “translationPresentation(isPresented:text:attachmentAnchor:arrowEdge:replacementAction:)” (iOS 17.4+) presents the system’s built-in translation UI over a view; “translationTask(_:action:)” (iOS 18+) runs an async closure that provides a
TranslationSessionfor programmatic translation in your own interface. ↩↩↩ -
Apple Developer,
TranslationSessionandTranslationSession.Request.translate(_:)handles a single string; the batch API takes an array of requests, each carrying aclientIdentifierthat returns on the matching response so results can be reassociated with their inputs independent of order. ↩↩↩ -
The Translation framework’s translation APIs are surfaced through SwiftUI view modifiers (
translationTask,translationPresentation) and have no UIKit entry point; a UIKit screen hosts a SwiftUI view (for example viaUIHostingController) to use them. The framework also requires a physical device and does not function in the iOS Simulator. See the Translation framework documentation and “Translating text within your app”. ↩↩↩↩↩ -
Apple Developer, “Translating text within your app” and
LanguageAvailability. The first translation of a source-to-target language pair downloads the required language assets; the framework exposes language-availability checks and a way to prepare (download) a pair in advance so apps can manage the download moment rather than stall on first use. ↩↩↩↩ -
Author’s related analysis on composing on-device capabilities: Apple Foundation Models: The On-Device LLM Framework, On-Device LLMs with Apple’s Foundation Models, and Writing Tools API adoption. The translate-in, reason, translate-out pattern brackets a monolingual Foundation Models call with Translation to make an on-device feature multilingual without leaving the device. ↩