Contents

william-weng/wwtranslationmanager

English | 正體中文

✨ 特色

  • 專為 UIKit 專案設計,無須將整個畫面改寫成 SwiftUI。
  • 使用 Apple Translation 框架提供端側(On-device)翻譯。
  • 支援系統原生語言包下載與授權對話框。
  • 以隱藏的 UIHostingController 作為 UIKit 與 SwiftUI 之間的橋樑。
  • 可透過 TranslationSession.Configuration 自訂翻譯設定。
  • 支援重複翻譯請求,並會自動更新或使既有設定失效。
  • API 受到 @MainActor 保護,適合在 UIKit 主執行緒中使用。
  • 管理器解除初始化時,會自動移除內部的 Hosting Controller。

⚠️ Apple Translation 的實際可用性會受到裝置系統版本、來源語言、目標語言、語言包狀態與裝置支援情況影響。

📦 安裝方式

Swift Package Manager

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

| API | 宣告 | 適用 | 說明 | | --- | --- | --- | --- | | init(attachingTo:) | init(attachingTo viewController: UIViewController) | 全部 | 初始化翻譯管理器,並將隱藏的 SwiftUI Hosting Controller 掛載至指定的 UIViewController。 | | translate(:completion:failure:) | func translate( text: String, completion: @escaping ( translatedText: String) -> Void, failure: @escaping (Error) -> Void) | WWTranslationManager | 透過 completion handler 非同步執行文字翻譯。 | | translate(batch:completion:failure:) | func translate(batch texts: [String], completion: @escaping (WWTranslationBatchResult) -> Void, failure: @escaping (Error) -> Void) | WWTranslationBatchManager | 批次翻譯多個字串(使用串流模式)。 | | translate(from:completion:failure:) | func translate(from texts: [String], completion: @escaping ([WWTranslationBatchResult]) -> Void, failure: @escaping (Error) -> Void) | WWTranslationBatchCoordinatorManager | 批次翻譯多個字串(一次回傳)。 | | translate(:) | func translate(_ text: String) async throws -> String | WWTranslationManager | 透過 Swift Concurrency 非同步執行文字翻譯。翻譯失敗時會拋出錯誤。 | | translate(batch:) | func translate(batch texts: [String]) async throws -> AsyncThrowingStream<WWTranslationBatchResult, Error> | WWTranslationBatchManager | 批次翻譯多個字串(使用串流模式)。 | | translate(from:) | func translate(from texts: [String]) async throws -> [WWTranslationBatchResult] | WWTranslationBatchCoordinatorManager | 批次翻譯多個字串(一次回傳)。 |

🧠 運作方式

WWTranslationManager 會在初始化時建立一個隱藏的 UIHostingController,並將它加入指定的 UIKit View Controller 階層中。

翻譯流程如下:

  1. 將待翻譯文字與成功、失敗回呼交給內部 Bridge。
  2. 將公開的 TranslationSession.Configuration 同步至 SwiftUI Bridge View。
  3. 由 Apple Translation 顯示必要的語言包下載或使用者授權介面。
  4. 使用端側翻譯功能處理文字。
  5. 透過 completion 或 failure 回傳結果。
  6. 管理器釋放時移除隱藏的 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 manager: WWTranslationManager?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        manager = .init(attachingTo: self)
    }
    
    @IBAction func translateAction(_ sender: UIButton) {
        
        guard let source = sourceLabel.text else { return }
        
        manager?.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) {
        
        manager?.configuration = .init(source: .init(identifier: "ja-JP"), target: .init(identifier: "en-US"))
        
        sourceLabel.text = "「映画ちいかわ 人魚の島のひみつ」"
        targetLabel.text = "<Loading...>"
    }
}

Package Metadata

Repository: william-weng/wwtranslationmanager

Default branch: main

README: README.md