iOS18で使えるTranslationAPIをUIKitで使ってみた
私は、主にiOS/Flutterの開発をしている takashico といいます。
今回は、WWDC24のセッションで紹介された Translation API について UIKitのアプリへの組み込み方について紹介できればと思います。
Translation API とは
Appleの翻訳アプリで使用されている、言語翻訳機能がAPIで提供されるようになります。
セッション内の例では、多言語で書かれてたレビューの一覧をTrasnlastion APIを使って一括翻訳を行うユースケースが紹介されていました。これが無料で各アプリ内で使えるのは魅力的ですよね。それだけでなく、この翻訳はデバイス上で行われるため、オフラインで実行できるのも良いですね。
ぜひ使ってみたいところですが、全ての環境で使用できるわけではありません。
このAPIはSwiftUIでのみ提供される予定のため、UIkitでは簡単に使用することはできません。
そこで今回は、UIKitベースの画面でも使用できるようにする方法について検討してみたので、紹介します。
いざ実装
以下の環境で開発をしています。
Tool | Version |
---|---|
macOS | Sonoma 14.5(23F79) |
Xcode | 16.0.0 Beta 3 |
iOS | iOS 18 Beta 3 |
完成画面
UIKitの画面にSwiftUIで作成した翻訳機能を持ったボタンを配置して実装していきます。
実装については以下で共有していますので、気になる方はぜひ覗いてみてください。
実装構成
ここでは、Translation APIをUIKit上から使用する一例を紹介します。先ほども説明した通り、Translation APIはSwiftUIでのみ使用することができます。そこで、下の図のような構成で実装を進めていきます。
SwiftUI側の実装
まずは、SwiftUI単体でボタン押下をトリガーに Translation APIを実行するViewを作成してみます。
struct TranslationButtonView: View {
private let sourceText = "こんにちは"
var body: some View {
Button {
triggerTranslation()
} label: {
Image(systemName: "translate")
}
.translationTask(configuration) { session in
do {
print(response.targetText)
} catch (let error) {
print(error.localizedDescription)
}
}
}
}
private extension TranslationButtonView {
func triggerTranslation() {
if configuration == nil {
configuration = .init(source: Locale.Language(identifier: "ja"),
target: Locale.Language(identifier: "en"))
} else {
configuration?.invalidate()
}
}
}
続いて、以下の機能を実装します。
- sourceTextを外部から受け取れるようにする
- 翻訳済みのテキストをコールバックできるようにする
struct TranslationButtonView: View {
final class DataSource: ObservableObject {
@Published var sourceText = ""
@Published fileprivate(set) var targetText = ""
}
init(dataSource: DataSource) {
self.dataSource = dataSource
}
@State private var configuration: TranslationSession.Configuration?
@ObservedObject private var dataSource: DataSource
var body: some View {
Button {
triggerTranslation()
} label: {
Image(systemName: "translate")
}
.translationTask(configuration) { session in
do {
let response = try await session.translate(dataSource.sourceText)
dataSource.targetText = response.targetText
} catch (let error) {
dataSource.targetText = error.localizedDescription
}
}
}
}
private extension TranslationButtonView {
func triggerTranslation() {
if configuration == nil {
configuration = .init(source: Locale.Language(identifier: "ja"),
target: Locale.Language(identifier: "en"))
} else {
configuration?.invalidate()
}
}
}
これで、翻訳機能を担うSwiftUI側の実装は完了です。
続いて、この TranslationButtonView
をUIKit側に繋ぎ込んでみましょう。
UIKit側の実装
先ほど実装した、TranslationButtonView
を使って以下の手順で実装を進めていきます。
- NavigationItemに
TranslationButtonView
を配置する - TextFieldに入力されたテキストを
TranslationButtonView.Input
と同期する - 翻訳済みテキストを受け取り、Viewへ反映する
では、それぞれ見ていきましょう。
TranslationButtonView
を配置する
NavigationItemにUIHostingControllerを使用して、SwiftUIで作成したTranslationButtonView
をUIKitの画面に組み込みます。
private var translationButtonViewDataSource = TranslationButtonView.DataSource()
let translationButton = TranslationButtonView(dataSource: translationButtonViewDataSource)
let hostingController = UIHostingController(rootView: translationButton)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
hostingController.view.backgroundColor = .clear
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: hostingController.view)
UIHostingController
https://developer.apple.com/documentation/swiftui/uihostingcontroller
TextFieldに入力されたテキストを TranslationButtonView.Inputと同期する
func textFieldDidChangeSelection(_ textField: UITextField) {
// 入力値の同期
translationButtonViewDataSource.sourceText = textField.text ?? ""
}
翻訳済みテキストを受け取り、Viewへ反映する
translationButtonViewDataSource.$targetText
.receive(on: RunLoop.main)
.assign(to: \.translatedTextView.text, on: self)
.store(in: &cancellableSet)
以上で、TranslationButtonView
の表示と入力値と出力値の処理ができましたので、正常に動作するか見てみましょう。
完成
ボタン押下をトリガーに翻訳処理が実行できています🎉
まとめ
今回の記事では、WWDC24で紹介されたTranslation APIをUIKitベースのアプリケーションで使用する方法について解説しました。Translation APIは本来SwiftUIでのみ使用可能ですが、UIKitとSwiftUIを組み合わせることで、UIKitベースのアプリでも翻訳機能を活用することができます。
次回は、Flutter (Add to App)で使ってみたらどうなるのか?について紹介できればと思います🙋
Discussion