私はTraverba——クラウドAPIを一切使わず、スマートフォン上で100%動作するリアルタイム翻訳アプリ——を開発しました。音声認識、108言語への翻訳、92の文字体系に対応したカメラOCR、画面翻訳オーバーレイ、ファイル文字起こし、そして最大7人がBluetoothで繋がるオフライングループチャットを搭載しています。
アプリ全体はFlutter + 重い処理を担うネイティブプラットフォームコードで構築されています。このアプリが何をするのか、なぜFlutterが正しい選択だったのか、そしてiOS・Android両プラットフォームへのリリースで直面した最大の課題について紹介します。
アプリの機能
Traverabaには5つのコア機能があり、すべてオフラインで動作します。
リアルタイム音声翻訳 — ある言語で話すと、別の言語で翻訳が聞こえ、画面にも表示されます。3つのモード:アプリ内、フローティングシステム音声キャプチャ(Zoom/Teams/Meetの音声を翻訳)、フローティングマイクに対応。ターゲット言語への自動読み上げ機能付き。
カメラ翻訳 — メニュー、標識、フォーム、書類にカメラを向けるだけ。2つのOCRエンジンを搭載:Basic モード(ML Kit)はすばやい読み取りに、Professional モード(PaddleOCR)はCJK・アラビア語・キリル文字・デーヴァナーガリーなど複雑な文字体系に対応。92言語をサポートし、画像上の外国語テキストを翻訳済みテキストに直接置き換えます。
画面翻訳 — あらゆるアプリ内のテキストを翻訳するフローティングオーバーレイ。アニメを観ている、韓国語のニュースを読んでいる、日本語のゲームをプレイしている、WeChatのメッセージを受け取った——オーバーレイボタンをタップするだけでテキストがその場で翻訳されます。アプリ切り替え不要。
ファイル文字起こし — 音声・動画ファイル(mp3、m4a、wav、mp4)をインポートし、タイムスタンプ付きの原文と翻訳の対訳文字起こしを取得できます。
オフライングループチャット — 最大7人がBluetoothメッシュで接続。それぞれが自分の言語で話し、すべてのメッセージがリアルタイムで各参加者の言語に翻訳されます。インターネット不要、サーバー不要。各スマートフォンが独立してASRと翻訳を処理します。
これらすべてがスマートフォンのプロセッサ上でローカルに動作します。APIキー不要、サーバーコスト不要、端末外へのデータ送信なし。
なぜFlutterを選んだのか
開発開始時に Flutter、React Native、完全ネイティブ(Swift + Kotlinの別々のコードベース)を比較検討しました。
単一コードベースからのクロスプラットフォーム対応は譲れない条件でした。 このような複雑なアプリの2つのネイティブコードベースをソロ開発者が維持するのは非現実的です。設定画面、チャットバブル、文字起こしビュー、オンボーディングツアー、ダウンロードシート、言語ピッカーといったDart UIレイヤーは、総コードの約60%を占めます。これを2回書くとプロジェクトの期間が2倍になっていたでしょう。
Flutterのプラットフォームチャンネルシステムがネイティブ統合を現実的にしました。 AI処理の重い部分(ASR推論、LLM翻訳、OCR、Bluetoothメッシュ、フローティングオーバーレイ)はプラットフォームチャンネルとメソッドチャンネルを通じてネイティブのSwift/Kotlinで動作します。FlutterはAIランタイムになろうとしているわけではなく、ネイティブエンジンをつなぐUIとオーケストレーションレイヤーです。
パフォーマンスは十分に良好でした。 このようなアプリでFlutterを使う懸念はオーバーヘッドです——会話中にASR→翻訳→TTSを連鎖させる際、ミリ秒単位での差が重要になります。実際には、重い計算がネイティブコードで行われるためFlutterレイヤーのレイテンシへの影響は無視できる程度です。Dartレイヤーは状態管理、ルーティング、UIアップデートを処理しており、これらは得意分野です。
ホットリロードがUIの反復を大幅に加速しました。 5つの異なる機能サーフェス(音声、カメラ、画面、ファイル、グループチャット)それぞれに複数の状態とモードがある中で、リビルドなしにUIを反復できる能力は生産性を大幅に向上させました。
アーキテクチャ:オーケストレーターとしてのFlutter
アプリは明確な役割分担に従っています。
Flutter/Dartレイヤーが担当するもの:
- すべてのUI(Material 3、スマートフォンとタブレット向けアダプティブレイアウト)
- 状態管理と機能ルーティング
- 言語・ロケール選択とユーザー設定
- オプショナルモデルのダウンロード管理
- コインエコノミーとサブスクリプション制御
- オンボーディングガイドツアー
ネイティブレイヤーが担当するもの(プラットフォームチャンネル経由):
- ASR推論(sherpa-onnx / whisper.cpp)
- LLM翻訳(オンデバイスランタイム経由のGemma)
- 従来のML翻訳(ONNX NMTモデル)
- カメラOCR(ML Kit + PaddleOCR)
- テキスト読み上げ
- グループチャット用Bluetoothメッシュ
- フローティングオーバーレイ(画面翻訳 + システム音声キャプチャ)
- メモリバジェット管理とモデルのロード/エビクション
これらのレイヤー間の橋渡しは、明確に定義されたコントラクトを持つプラットフォームチャンネルのセットです。Dart側はコマンドを送り(「広東語のASRを開始」「このテキストを日本語から英語に翻訳」「画面をキャプチャしてOCR」)、ネイティブ側が非同期で結果を返します。
3つのASRモデル、自動ルーティング
単一の音声認識モデルですべての言語を高品質にカバーすることはできません。一部の言語で品質を妥協する代わりに、アプリは3つの特化モデルを搭載しています。
- Parakeet 英語に最適化。ダウンロード不要で即時の英語ASRのためにアプリにバンドル済み。
- Whisper.cpp 30言語以上にわたる幅広い多言語対応。
- Qwen3-ASR 広東語専用サポートを含むCJK最適化。
ユーザーが入力言語を選択すると、Dartのルーティングレイヤーが最適なモデルを選び、どのエンジンをアクティベートするかをネイティブ側に伝えます。ユーザーはモデル選択を意識する必要はありません。
難しいのはメモリ管理です。これらのモデルはRAMで500MBから1.9GBに及びます。3つを同時にロードすると大多数のスマートフォンがクラッシュします。メモリバジェットプランナー(Dartで記述し、ネイティブのメモリ統計を照会)が何がロードされているかを追跡し、新しいモデルのロードが必要になると最も長く使われていないモデルをエビクトします。バックエンド設定ストアは各デバイスでどのコンピュートバックエンド(NPU、GPU、CPU)が動作したかを記憶し、次回の実行時に失敗したバックエンドをスキップします。
2段階の翻訳による108言語対応
翻訳は2つの経路で行われます。
Tier 1 — 従来のNMTモデル(61言語): 高速、低メモリ、リソースが豊富な言語ペアで高品質。両プラットフォームでsherpa-onnx経由で動作するONNXモデル。
Tier 2 — オンデバイスGemma LLM(追加47言語): 専用NMTモデルがない言語(ヨルバ語、アムハラ語、ラオス語、ミャンマー語など)向けに、量子化されたGemma 2Bモデルがローカルで翻訳を処理します。Tier 1より低速ですが、従来のモデルが十分に対応できない言語へのカバレッジを拡大します。
Dartレイヤーが言語ペアに基づいてどちらのTierを使用するかを管理します。ユーザーには統一されたエクスペリエンスが提供されます。
カメラOCR:デュアルエンジン、92言語
カメラ翻訳ではユーザーが選択できる2つのエンジンが動作します。
Basic モードはGoogleのML Kitのオンデバイステキスト認識を使用します。速く、ラテン文字・キリル文字・CJKに対応。リアルタイムのビューファインダー使用に適しています。
Professional モードはオンデバイスで動作するPaddleOCRを使用します。複雑なレイアウト、混在する文字体系、曲線テキスト、低コントラストでより高い精度を発揮します。より計算負荷が高いため、コインゲート(1回のキャプチャで5コイン)が適用されます。
実際の開発で苦労したこと:iOSはデフォルトではラテン文字のOCRスクリプトモデルしかバンドルしていません。CJK、デーヴァナーガリー、アラビア語にはPodfileにスクリプト固有のポッドを明示的に追加する必要があります。失敗は完全にサイレントで——ML Kitはエラーなしに未対応スクリプトに対して空の結果を返します。ML KitでiOSの多言語OCRを構築する場合は、Podfileを確認してください。
フローティングオーバーレイ:FlutterではなくネイティブUI
画面翻訳とシステム音声機能はFlutterのレンダリングサーフェス外のネイティブオーバーレイとして動作します。Androidでは、これはKotlinで書かれたシステムオーバーレイサービスです。iOSではSwiftの画面キャプチャAPIを使用します。
これらのオーバーレイはプラットフォームチャンネルを通じてFlutterの翻訳エンジンと通信します。アーキテクチャ上の課題はライフサイクル管理です——メインのFlutterアクティビティがバックグラウンドに移行してもオーバーレイは生き続けなければなりません。
重要な教訓:iOSではネイティブブリッジのインスタンスは強参照で保持されなければなりません(staticまたはシングルトンによる保持)。ブリッジが何も保持しないインスタンス変数である場合、ARCがガベージコレクトし、すべてのハンドラクロージャがサイレントにno-opになります。ブリッジがコレクトされていたために、ダウンロードプログレスインジケーターが0%で止まったまま何日も費やしました。クラッシュもエラーもなく、ただのサイレント障害でした。
Bluetoothグループチャット
オフライングループチャットはBluetoothメッシュプロトコルを使用します。各スマートフォンは受信者と中継者の両方として機能します。メッセージは接続されたすべてのデバイスにブロードキャストされます。各デバイスが独立してASRと翻訳を実行し——「ホスト」や「サーバー」となるスマートフォンは存在しません。
FlutterはチャットUI、メッセージ状態、参加者管理を担当します。Bluetoothスタックは完全にネイティブ(iOSはCoreBluetooth、AndroidはAndroid Bluetooth API)でプラットフォームチャンネルでブリッジされています。
音声からASR、翻訳、ブロードキャスト、表示までのパイプライン全体のレイテンシは約1秒です。Dart UIは楽観的表示(送信時に即「翻訳中...」を表示)を使用しているため、会話は軽快に感じられます。
数字で見るTraverba
- 言語数: 108言語(ML 61 + AI 47)
- ASR言語数: 30のオンデバイス音声認識
- OCR言語数: 92のカメラテキスト認識
- アプリ基本サイズ: 約150MB+ダウンロード可能なモデル
- プラットフォーム: 単一のFlutterコードベースからiOS+Android
- Dart/Flutterコード: 総コードベースの約60%(UI、状態管理、ルーティング、ビジネスロジック)
- ネイティブコード: 約40%(ASR、LLM、OCR、Bluetooth、オーバーレイ、メモリ管理)
- チーム規模: 1人
このプロジェクトでFlutterが正解だった理由
複雑なUIのための単一コードベース。 5つの機能サーフェス、それぞれ複数モード、ダウンロードシート、オンボーディングツアー、アダプティブレイアウト——これを2回ではなく1回書けたことが、ソロ開発者としてリリースできたかどうかの分かれ目でした。
プラットフォームチャンネルが適切に設計されている。 Dartとネイティブコードのブリッジはクリーンでよくドキュメント化されており信頼性が高い。重い計算はネイティブで行い、ユーザーエクスペリエンスはFlutterというアプリには、まさに正しいアーキテクチャです。
高速な反復のためのホットリロード。 レイアウトのチューニング、状態のテスト、UXフローの改善——ホットリロードにより、数百のUI変更にわたって反復時間が分単位から秒単位に短縮されました。
エコシステムが成熟している。 状態管理(Riverpod)、ナビゲーション、ローカライゼーション(117ロケール)、アダプティブデザイン——Flutterエコシステムはこれらすべてにプロダクション品質のソリューションを持っています。
難しかったこと
メモリプレッシャーはFlutterの盲点です。 Flutterは細かいメモリ制御を公開していません。Flutterエンジンと並行して複数のネイティブMLモデルを共同ロードする場合、ネイティブレベルでメモリを管理し、それをDartに伝える必要があります。「アプリがどれだけRAMを使用しているか」や「システムのメモリプレッシャーはどの程度か」を知るためのFlutterの組み込みAPIはありません。
ネイティブオーバーレイのライフサイクル。 Flutterサーフェス外で動作するフローティングオーバーレイはアーキテクチャ的に複雑です。Flutterアクティビティがバックグラウンドに移行してもオーバーレイはアクティブなままである可能性があります。ブリッジを生き続けさせ、状態同期を管理し、プラットフォーム固有のライフサイクルイベントを処理するには、Flutterが抽象化できない大量のネイティブコードが必要でした。
iOSのビルドの複雑さ。 ML Kitスクリプトポッドを含むCocoaPods、エンタイトルメント(ASR + LLMを共同ロードするためのincreased-memory-limit)、App Tracking Transparencyのタイミング、コードサイニング——iOSのビルドパイプラインには多くの可動部分があり、Flutterのツールチェーンが完全に管理できるわけではありません。
試してみる
Traverabaは両プラットフォームで無料です。音声、カメラ、画面翻訳、テキスト翻訳はすべて無料で無制限に利用できます。プレミアム(月額$1.99)でグループチャット、ファイル文字起こし、会議サマリーが使えるようになります。
Google PlayとApp Storeで入手できます。詳細はtraverba.comをご覧ください。
アーキテクチャ、Flutter + ネイティブ統合パターン、またはオンデバイスAI全般についてのご質問はお気軽にどうぞ。