iOS 多言語ローカライズワークフロー
xcstrings (String Catalog) を起点とした iOS アプリの多言語対応を自動化する。 xcodebuild によるエクスポート/インポートと、XLIFF ファイルの AI 翻訳を組み合わせる。
全体フロー
xcstrings → xcodebuild -exportLocalizations → xcloc (XLIFF) → AI翻訳 → バリデーション → xcodebuild -importLocalizations → xcstrings
ステップ 0: 対象言語の特定
翻訳対象の言語はプロジェクトの設定から動的に取得する。ハードコードしない。
方法 A: xcstrings から検出(推奨)
プロジェクト内の .xcstrings ファイルは JSON 形式で、sourceLanguage と各言語の翻訳情報を保持している。
# xcstrings ファイルを探す
find . -name "*.xcstrings" -not -path "*/.*"
# 対応言語の一覧を取得(sourceLanguage 以外のキーが翻訳対象)
python3 -c "
import json, sys
with open(sys.argv[1]) as f:
data = json.load(f)
src = data.get('sourceLanguage', 'ja')
langs = set()
for key, val in data.get('strings', {}).items():
langs.update(val.get('localizations', {}).keys())
langs.discard(src)
print(f'開発言語: {src}')
print(f'翻訳対象: {\" \".join(sorted(langs))}')
" Localizable.xcstrings
方法 B: Xcode プロジェクト設定から検出
# .xcodeproj 内の knownRegions を取得
grep -A 20 'knownRegions' *.xcodeproj/project.pbxproj | grep -oE '"[a-z]{2}(-[A-Za-z]+)?"'
方法 C: ユーザー指定
ユーザーが「英語とフランス語に翻訳して」のように明示した場合は、その言語のみを対象とする。
言語コードの対応表(参考)
よく使われる言語コード:
en(英語),zh-Hans(中国語簡体字),zh-Hant(中国語繁体字)ko(韓国語),fr(フランス語),de(ドイツ語)es(スペイン語),it(イタリア語),pt-BR(ポルトガル語・ブラジル)th(タイ語),vi(ベトナム語),id(インドネシア語)ar(アラビア語),hi(ヒンディー語)
ステップ 1: xcloc パッケージのエクスポート
ステップ 0 で特定した言語を -exportLanguage に指定してエクスポートする。
# 例: LANGUAGES 変数に検出結果を格納してエクスポート
xcodebuild -exportLocalizations \
-project <プロジェクト名>.xcodeproj \
-localizationPath ./Localizations \
$(printf -- '-exportLanguage %s ' ${LANGUAGES})
注意事項
- プロジェクトの場合は
-project、ワークスペースの場合は-workspaceと-schemeを指定する -exportLanguageを省略すると開発言語のみエクスポートされる- コマンドは全ターゲットをビルドするため、コンパイルエラーがあると失敗する
- 出力先に
Localizations/{lang}.xcloc/が言語ごとに生成される
ステップ 2: XLIFF ファイルの翻訳
2-1. 対象ファイルの特定
xcloc パッケージ内の XLIFF ファイルパスは以下の規則に従う:
{言語コード}.xcloc/Localized Contents/{言語コード}.xliff
2-2. 翻訳対象の判定
XLIFF ファイル内で以下の条件に合致する <trans-unit> を翻訳対象とする:
<target>要素が空、またはstate="new"である<source>要素にテキストが存在する
<!-- 翻訳が必要な例 -->
<trans-unit id="greeting_message" xml:space="preserve">
<source>こんにちは</source>
<target state="new"/>
<note>ホーム画面の挨拶メッセージ</note>
</trans-unit>
<!-- 翻訳済みの例(スキップ) -->
<trans-unit id="app_name" xml:space="preserve">
<source>ヘルステイクアウト</source>
<target state="translated">HealthTakeout</target>
</trans-unit>
2-3. 翻訳ルール
絶対に守るべきルール
<source>要素を変更しない — Xcode はインポート時に source でマッチングするid属性・original属性を変更しない- フォーマット指定子を正確に保持する:
%@,%d,%lld,%1$@,%2$@ - stringsdict の変数キーをそのまま保持する:
%#@variable@ - 翻訳した
<target>にstate="translated"を設定する xml:space="preserve"を維持する
翻訳品質のルール
<note>要素がある場合、コンテキストとして翻訳に活用する- UI 要素のラベル(ボタン、タブ等)は簡潔に翻訳する
- アプリ名・ブランド名は翻訳しない(references/glossary.yaml を参照)
- 位置指定子(
%1$@,%2$@)は語順に合わせて並べ替え可能だがインデックスは維持する
言語固有のガイダンス
翻訳前に references/language-guide.yaml を読み込み、対象言語の以下の情報を確認する:
- CLDR 複数形カテゴリ: 言語によって必要なカテゴリ数が異なる
- 敬語・丁寧さのレベル: デフォルトのフォーマリティ設定
- テキスト長の傾向: 日本語と比較した文字数の増減
- 特記事項: RTL(右から左)レイアウト、文字体系固有の注意点等
language-guide.yaml に未掲載の言語が対象の場合は、CLDR の複数形ルールを調べて適用する。
2-4. 複数形・デバイスバリエーションの扱い
trans-unit の id に |==| セパレータがある場合、複数形またはデバイスバリエーションを示す:
<!-- 複数形の例 -->
<trans-unit id="item_count_%lld|==|plural.one" xml:space="preserve">
<source>%lld件</source>
<target state="new"/>
</trans-unit>
<trans-unit id="item_count_%lld|==|plural.other" xml:space="preserve">
<source>%lld件</source>
<target state="new"/>
</trans-unit>
重要: 複数形カテゴリはターゲット言語の CLDR ルールに従う。
ソース言語(日本語)には other のみだが、ターゲット言語によっては
zero, one, two, few, many, other が必要になる。
XLIFF エクスポート時に Xcode が必要なカテゴリの trans-unit を生成するため、
生成されたすべての trans-unit を漏れなく翻訳すること。
同一キーのデバイスバリエーション(device.iphone, device.mac 等)も全て翻訳する。
ステップ 3: 翻訳後のバリデーション
翻訳完了後、各言語の XLIFF ファイルに対して以下のチェックリストをすべて検証する。 1件でも問題があれば、該当箇所を修正してからステップ 4 に進むこと。
チェックリスト
3-1. XML の整合性
- XLIFF ファイルが well-formed な XML であること
- 開きタグ・閉じタグの対応が正しいこと
&,<,>等の特殊文字が適切にエスケープされていること
3-2. 未翻訳の残存
state="new"の<trans-unit>が残っていないこと<target>が空のままstate="translated"になっていないこと
3-3. プレースホルダーの一致
- 各
<trans-unit>の<source>と<target>で、以下が完全に一致すること:- フォーマット指定子の種類と数(
%@,%d,%lld等) - 位置指定子のインデックス(
%1$@,%2$@等) - stringsdict 変数キー(
%#@variable@)
- フォーマット指定子の種類と数(
- 不一致がある場合: 該当 trans-unit の id を報告し、target を修正する
3-4. 翻訳禁止用語
references/glossary.yamlのdo_not_translateリストの用語が翻訳されていないこと- アプリ名・ブランド名がそのまま保持されていること
3-5. state 属性
- 翻訳済みの全
<target>にstate="translated"が設定されていること - 元から翻訳済み(スキップした)trans-unit の state が変更されていないこと
バリデーション結果の報告
問題が見つかった場合は、以下の形式で報告してから修正する:
### バリデーション結果: {言語コード}
- ✅ XML 整合性: OK
- ❌ 未翻訳残存: 2件(greeting_message, settings_title)
- ⚠️ プレースホルダー不一致: 1件(item_count_%lld — %lld が target に欠落)
- ✅ 翻訳禁止用語: OK
- ✅ state 属性: OK
→ 修正後、再検証する。
ステップ 4: xcloc パッケージのインポート
翻訳済みの xcloc パッケージを Xcode プロジェクトにインポートする。 言語ごとに個別に実行する(一括インポートは不可)。
for lang in ${LANGUAGES}; do
xcodebuild -importLocalizations \
-project <プロジェクト名>.xcodeproj \
-localizationPath "./Localizations/${lang}.xcloc"
done
注意事項
- 問題のある翻訳はインポート時に警告が出るがスキップされる
- String Catalogs プロジェクトでは xcstrings の該当言語部分のみ更新される
- インポート後、Xcode で String Catalog を開いて結果を確認する
実行例
例 1: 全言語を自動翻訳
ユーザーが「ローカライズして」と言った場合:
- xcstrings ファイルから対応言語を自動検出
- 全言語をエクスポート → 翻訳 → バリデーション → インポート
- 翻訳結果のサマリーを言語別に報告
例 2: 特定言語のみ翻訳
ユーザーが「フランス語とイタリア語だけ翻訳して」と言った場合:
- 指定された
fr,itのみをエクスポート - 2言語のみ翻訳 → バリデーション → インポート
例 3: 差分翻訳
ユーザーが「新しく追加した文字列だけ翻訳して」と言った場合:
- 全言語をエクスポート
state="new"の trans-unit のみ翻訳(既存翻訳はスキップ)- バリデーション → インポート
報告フォーマット
翻訳完了後、以下の形式でサマリーを報告する:
## ローカライズ結果
| 言語 | 翻訳数 | スキップ | 警告 | ステータス |
|------|--------|---------|------|-----------|
| en | 42 | 5 | 0 | ✅ 完了 |
| fr | 42 | 5 | 1 | ⚠️ 要確認 |
| it | 42 | 5 | 0 | ✅ 完了 |