一個專為 UIKit 設計的 Apple Translation 框架管理員,透過封裝隱藏的 SwiftUI UIHostingController,讓 UIKit 應用程式可以在不大幅調整既有架構的情況下,使用 Apple Translation 的端側翻譯功能,以及系統原生的語言包下載授權對話框。
Example.mp4
- 專為 UIKit 專案設計,無須將整個畫面改寫成 SwiftUI。
- 使用 Apple Translation 框架提供端側(On-device)翻譯。
- 支援系統原生語言包下載與授權對話框。
- 以隱藏的
UIHostingController作為 UIKit 與 SwiftUI 之間的橋樑。 - 可透過
TranslationSession.Configuration自訂翻譯設定。 - 支援重複翻譯請求,並會自動更新或使既有設定失效。
- API 受到
@MainActor保護,適合在 UIKit 主執行緒中使用。 - 管理器解除初始化時,會自動移除內部的 Hosting Controller。
⚠️ Apple Translation 的實際可用性會受到裝置系統版本、來源語言、目標語言、語言包狀態與裝置支援情況影響。
swift package add https://github.com/William-Weng/WWTranslationManager.git或在 Xcode 中:
File → Add Packages → https://github.com/William-Weng/WWTranslationManager.git
| 名稱 | 型別 | 說明 |
|---|---|---|
configuration |
TranslationSession.Configuration |
翻譯配置設定。預設為 .init(),會依系統環境自動判斷翻譯語言。 |
| API | 宣告 | 說明 |
|---|---|---|
init(attachingTo:) |
init(attachingTo viewController: UIViewController) |
初始化翻譯管理器,並將隱藏的 SwiftUI Hosting Controller 掛載至指定的 UIViewController。 |
translate(_:completion:failure:) |
func translate(_ text: String, completion: @escaping (String) -> Void, failure: @escaping (Error) -> Void) |
透過 completion handler 非同步執行文字翻譯。 |
translate(_:) |
func translate(_ text: String) async throws -> String |
透過 Swift Concurrency 非同步執行文字翻譯。翻譯失敗時會拋出錯誤。 |
WWTranslationManager 會在初始化時建立一個隱藏的 UIHostingController,並將它加入指定的 UIKit View Controller 階層中。
翻譯流程如下:
- 將待翻譯文字與成功、失敗回呼交給內部 Bridge。
- 將公開的
TranslationSession.Configuration同步至 SwiftUI Bridge View。 - 由 Apple Translation 顯示必要的語言包下載或使用者授權介面。
- 使用端側翻譯功能處理文字。
- 透過
completion或failure回傳結果。 - 管理器釋放時移除隱藏的 Hosting Controller。
這種設計可以將 SwiftUI
translationTask相關邏輯集中在內部,而 UIKit 呼叫端只需要處理簡單的文字輸入與結果回呼。
- 請在主執行緒建立與使用
WWTranslationManager,因為此類別標記為@MainActor。 - 管理器應該由畫面或對應的 Coordinator 持有,避免剛建立後便被釋放。
attachingTo傳入的 View Controller 必須已經具有有效的 View 階層。- 首次翻譯特定語言組合時,系統可能要求下載語言包。
- 端側翻譯不代表所有語言組合都能離線使用,實際結果取決於裝置上的語言包。
- 如果連續送出多個翻譯請求,建議在上一個請求完成後再開始下一個請求,以避免回呼結果互相覆蓋。
- 發生錯誤時,請在
failure閉包中處理 UI 狀態,例如顯示提示、保留原文或提供重試按鈕。
import UIKit
import WWTranslationManager
final class ViewController: UIViewController {
@IBOutlet weak var sourceLabel: UILabel!
@IBOutlet weak var targetLabel: UILabel!
private var translationManager: WWTranslationManager?
override func viewDidLoad() {
super.viewDidLoad()
translationManager = WWTranslationManager(attachingTo: self)
}
@IBAction func translateAction(_ sender: UIButton) {
guard let source = sourceLabel.text else { return }
translationManager?.translate(source, completion: { [weak self] translatedText in
self?.targetLabel.text = translatedText
}, failure: { error in
print("翻譯失敗或使用者取消下載: \(error.localizedDescription)")
})
}
@IBAction func translateEnglish(_ sender: UIButton) {
sourceLabel.text = "Offer in-app translations with the Translation framework. You can use the built-in UI and let the system offer a translation to users on your behalf. Or you can use the framework to customize the translation experience."
targetLabel.text = "<Loading...>"
}
@IBAction func translateJapanese(_ sender: UIButton) {
translationManager?.configuration = .init(source: .init(identifier: "ja-JP"), target: .init(identifier: "en-US"))
sourceLabel.text = "「映画ちいかわ 人魚の島のひみつ」"
targetLabel.text = "<Loading...>"
}
}