アプリを多言語対応しよう
00要約Overview
01物語Story
Situation
1.2 のとおり、まず 2 言語対応を目指す。最初に手をつけるのは、アプリ本体の表示文字列である。
Complication
しかし、画面のコードに文字列を直書きしていると、後から各言語へ置き換えるのは地獄になる。どこに何の文字列があるか探し回ることになり、言語を 1 つ足すたびに全画面を触ることになる。
Question
アプリの表示文字列を、どうすれば低コストで多言語化できる構造にできるか?
02解決Solution
Criteria
- 表示文字列が一か所に集約されていること
- 翻訳を AI にまとめて任せられること
- 後から言語を足すのが容易なこと
Answer
表示文字列を「言語ファイル」として外に出す。コードからは文字列そのものではなくキーで参照し、実際の文言は言語ごとのファイルに持たせる。翻訳は、その言語ファイルを AI に渡して「この言語ファイルを○語に翻訳して」と頼むだけ。最終確認はするが、大部分は AI が仕上げてくれる。
Reason
文字列が一か所に集約されていれば、翻訳も差し替えも一度で済むからである。 コードに散らばっていると、AI もどこを訳せばいいか分からず、言語追加のたびに全画面を触ることになる。言語ファイルにまとめておけば、AI がまとめて訳せて、言語を足すのもファイルを 1 枚増やすだけになる。
大事なのは、翻訳の技術ではない。
多言語化しやすい構造で、最初から実装しておくことである。
ただし、一つ落とし穴がある。キーと値だけを AI に渡すと、その単語がどの画面の何を指すのか分からず、誤訳する。「Home」が家なのか先頭画面なのか、AI には判断できない。だから各文字列に、「どの画面の、何のための言葉か」という文脈をコメントで添えておく。これが訳の精度を大きく左右する。
この 2 つから、2 言語対応で意味を予測し、意味情報として開発時に意味を残すことで、飛躍的に精度を上げることができる。
Options
- 画面に文字列を直書きする — 最初は速いが、多言語化の段になって全画面を触る羽目になる。だから採らなかった。
- 機械翻訳 API を都度呼ぶ — 文脈理解が弱く誤訳が増える上、運用コストもかかる。AI にファイルごと文脈つきで訳させる方が質が高い。
03結果Result
Good
構造さえ整えれば、2 言語対応がそのまま 5 言語・50 言語へスケールする。手順は同じで、増えるのは言語ファイルの枚数だけ。地味で膨大な翻訳を、AI に任せられるようになった。
Bad
AI も、文脈がなければ誤訳する。特に専門用語や、言葉遊びを含む表現は要注意で、最終確認は欠かせない。構造と文脈を用意するのは、あくまで人の仕事である。
Follow-up
アプリ本体が多言語化できたら、次はそれを世に出すためのストア申請情報を多言語化する。続きは 1.2.2 アプリ申請を多言語対応しよう。
■ 再現できる方法
この構成を自分で再現したい人向けのメモ。読み物ではないので、指示文は数行だけ表示してスクロールにしてある。
はじめに、下記を貼り付けで実行。
このアプリを多言語対応にしたいですが、今はまだ UI を作っている途中なので、
最初は日本語と英語の 2 言語だけで土台を作ってください。
(あとで全言語に広げるので、そのとき楽になる構造にしておいてほしい)
【作ってほしいもの】
1. i18n-js のセットアップ(i18n.js)
- expo-localization で端末の言語を判定
- 対応外の言語は英語にフォールバック(defaultLocale = 'en')
- 今は locales/ja.json と locales/en.json の 2 つだけ import
- 将来ここに import を足すだけで言語を増やせるコメントを残す
2. 翻訳ファイル
- locales/ja.json(日本語=マスター)
- locales/en.json(英語)
- キーは snake_case で意味を表す名前にする
- 変数の埋め込みは %{変数名} 形式にする
3. アプリのコードの文字列を i18n.t('キー') に置き換える
- 画面に直書きされている日本語・英語の文字列を全部キー化する
【ルール】
- 翻訳キーは ja.json を正とし、en.json はその対訳にする
- ハードコードされた表示文字列が残らないようにする
(i18n.t() を通さない生の文字列が画面に出ていないかチェック)
作り終えたら、今後 UI に文言を足すときにどういう手順でキーを追加すれば
いいか(ja.json に足す → en.json に足す → i18n.t で参照)を簡単に
まとめてください。
次に多言語対応するときに、下記を貼り付けで実行。
UI がほぼ固まったので、多言語対応を全言語に広げてください。
ja.json をマスターとして、そこから各言語に翻訳します。
【やってほしいこと】
1. UI 文字列(i18n)を 44 ロケールに拡張
- 対象言語:
zh, zh-TW, ko, es, fr, de, pt, it, nl, ar, ru, tr, hi, uk, he,
id, ms, th, vi, da, sv, no, fi, pl, cs, sk, hu, ro, hr, el, ca,
bn, gu, kn, ml, mr, or, pa, ta, te, ur, sl
(= ja / en に上記を足して 44)
- locales/<lang>.json を各言語分そろえる。ja.json のキー構造を完全に踏襲し、
値だけ各言語に翻訳する(キーの過不足がないこと)
- i18n.js の import を全言語分そろえる
- 繁体字(zh-TW / zh-HK 等)と簡体字(zh)を取り違えないように
2. アプリ名(plist)を 50 ロケールに拡張
- locales/plist/<lang>.json に CFBundleDisplayName だけを持つ
- 対象は 50 ロケール:
ja, en-US, en-AU, en-CA, en-GB, zh-Hans, zh-Hant, ko, es-ES, es-MX,
fr-FR, fr-CA, de-DE, it, pt-BR, pt-PT, nl-NL, ru, uk, tr, ar-SA, he,
hi, id, ms, th, vi, da, sv, no, fi, pl, cs, sk, hu, ro, hr, el, ca,
bn-BD, gu-IN, kn-IN, ml-IN, mr-IN, or-IN, pa-IN, ta-IN, te-IN, ur-PK,
sl-SI
3. app.json の言語設定を拡張
- ios.infoPlist.CFBundleLocalizations に上記ロケールを列挙
- locales セクションに各 plist/<lang>.json へのパスを列挙
【ルール・注意】
- 翻訳はすべて ja.json / plist/ja.json を正として訳す
- 変数埋め込み %{変数名} は各言語でもそのまま残す(訳して壊さない)
- 未対応言語は英語にフォールバックする方針は維持
- アラビア語・ヘブライ語など RTL 言語も文字列は用意する
(レイアウトの RTL 対応が必要な箇所があれば指摘してください)
作り終えたら、キーの数が全言語で一致しているか(欠けや余りがないか)を
確認した結果を報告してください。