SanasがExpoを使って3か月でリアルタイム動画翻訳アプリを構築した方法
Sanasは、世界初のインスタント翻訳対応ビデオ通話アプリをExpoでわずか数ヶ月で開発・リリースしました。リアルタイム音声翻訳は25以上の言語に対応し、平均2秒未満のレイテンシで動作します。
長年にわたり、エンジニアは「真のユニバーサル翻訳機」を夢見てきました。異なる言語を話す相手同士が、まるで同じ言語を共有しているかのように会話できる技術です。Sanasではその実現を目指し、小規模チームがExpoを軸に、LLMを用いた高度な翻訳研究とモダンなモバイル開発ツールを組み合わせて、このビジョンを製品化しました。
リアルタイム低レイテンシ音声翻訳の課題
リアルタイムかつ低レイテンシの音声翻訳は通信技術の中でも最も難しい問題の一つです。自然に感じられるためには、次を同時に満たす必要があります。
- 音声を即座にキャプチャしてストリーミングすること
- 通話中に翻訳して合成音声を高速に生成すること
- 話者のトーンや感情を可能な限り保持すること
- 不安定なネットワーク状況で安定して動作すること
- 25以上の言語にスケールすること
モバイル動画通話の文脈では、レイテンシ、パフォーマンス、音声忠実度を同時に解決する必要があり、数百ミリ秒の遅延でも会話がぎこちなくなってしまいます。iOSとAndroidの両方で利用者が違和感なく使える体験を作るため、短いイテレーションで改善を繰り返せる開発基盤が必要でした。
なぜSanasはExpoを選んだのか
チームのメンバーはStanfordでExpoを使った経験があり、コースの推奨フレームワークでもありました。ExpoはネイティブAPIへのアクセスを維持しつつ、開発スピードを落とさずに進められる点が決め手でした。
Expoを使うことで、以下が実現しました。
- 毎日ビルド・テスト・デプロイを行いながら、パフォーマンスに重要な部分を制御できること
- 統一された開発体験と高速なイテレーションサイクル
- ネイティブAPIへの深い統合(Expo SDK 54)
- iOS/AndroidのCI/CDを簡潔に扱えるEAS Build
- 内部テスターや早期ユーザーへ即時OTA配信が可能なEAS Update
ビルドやネイティブ設定の複雑さをExpoに委ねることで、我々は翻訳精度、レイテンシ、自然さといった本質的な問題に集中できました。
主要Expoモジュールと統合
インスタント翻訳とスムースなコミュニケーションを実現するため、以下のExpoモジュールとカスタムネイティブ統合を組み合わせました。
- expo-audio: ライブキャプチャと合成音声の再生
- expo-camera: リアルタイム動画ストリーミングと参加者フィード制御
- expo-webrtc (カスタム統合): 暗号化されたピアツーピア通話層
- expo-haptics: 翻訳イベントを知らせる微細な触覚フィードバック
- expo-notifications: 通話招待、未応答アラート、翻訳アップデート
- expo-secure-store: 認証情報やAPI資格情報のローカル暗号化
- expo-updates: シームレスなOTA機能ロールアウト
- カスタムネイティブモジュール: LLMのストリーミング推論やデバイス上のボイスクローン
これらを組み合わせることで、小規模チームでもセキュリティ、音声忠実度、翻訳速度を犠牲にせずプロダクショングレードの体験を作り上げることができました。
Sanasアプリケーションのアーキテクチャ(概要)
アプリはエッジの知能とデバイス上の効率性のバランスを取ったシンプルなアーキテクチャで動作します。各レイヤーは遅延を最小化し、忠実度を最大化するよう設計されています。
- クライアント: React Native + Expoアプリ。UI、状態管理、ローカルの音声/映像処理を担当。
- エッジサーバー: 微小レイテンシで音声認識、翻訳、音声合成を行うファインチューニング済みの多言語LLMをホスト。
- WebRTCトランスポート: 双方向のリアルタイム通信を管理し、ネットワーク負荷下でも安定する動的な品質適応を提供。
- EAS Build / Update: ビルド自動化、ステージングチャネル、A/Bレイテンシテストのためのフィーチャーフラグ運用をオーケストレーション。
デバイスとエッジ間で計算を賢く分散することで、ユーザーにとって即時に感じられる翻訳体験をプラットフォーム横断で実現しました。
例: リアルタイムでのストリーミング翻訳
インスタント翻訳で最も難しい部分の一つは、同一接続でライブ音声キャプチャ、逐次文字起こし、翻訳、合成音声の再生を低レイテンシで調停することです。Sanasでは単一の永続的なWebSocketで逐次処理を多重化するアプローチを採りました。これにより個別のAPI呼び出しによる往復遅延を避けられます。
ws.onmessage = event => {
const message = JSON.parse(event.data);
switch (message.type) {
case 'ready':
// Server is ready — start streaming audio chunks
LiveAudioStream.on('data', (data: string) => {
ws.send(JSON.stringify({ type: 'audio', data }));
});
LiveAudioStream.start();
break;
case 'transcription':
// Progressive transcription with complete + partial words
const completeText = message.complete.map(w => w.word).join('');
const partialText = message.partial.map(w => w.word).join('');
updateTranscription(completeText, partialText);
break;
case 'translation':
// Full translation arrives after user stops speaking
displayTranslation(message.complete.map(w => w.word).join(''));
break;
case 'speech':
// Streamed TTS audio chunks with character-level timestamps
audioChunks.push(base64ToUint8Array(message.audio));
break;
}
};
この方法により、ユーザーは話している間に逐次的な文字起こしを見られ、発話終了後ほぼ即座に翻訳音声を聞くことができます。
Expoで作ることのROI
わずか3か月で小さなチームがプロトタイプからプロダクションまで持っていき、App StoreとGoogle Playにローンチしました。現在、25以上の言語をサポートし、話者の自然なトーンや個性を保持した翻訳を提供しています。平均翻訳レイテンシは2秒未満で、デバイス間でネイティブに近い滑らかな体験を実現しています。
Expoがこのスピードを可能にしました。統一されたツールチェーン、オープンソースの柔軟性、クラウドベースのビルドシステムにより、複雑なネイティブ設定を管理する代わりにコアの翻訳モデルやライブ体験の向上に注力できました。エンジニアの一人はこう言っています: 「Expoのおかげで、ビルドシステムを設定するのではなく翻訳の限界を押し上げることに集中できた。」
次の一手
作業は終わっていません。チームは現在、リアルタイム翻訳をより表現豊かで人間らしくする次の段階に注力しています。Expoは引き続き重要な役割を果たし、迅速で信頼できるアップデート機構によりモデル改善や新機能を毎週のようにユーザーに届けています。
Sanasの目標はシンプルです: 言語に関係なく、人が理解し、理解される手助けをすること。Expoはそのビジョンを現実にするのを助けています。