
iOSアプリの多言語対応: .xcstrings を XLIFF で言語ごとに分割・統合する
現在、開発中のアプリで多言語対応を進めている。対応言語が増えるにつれて Localizable.xcstrings の管理に苦労するようになり、翻訳ワークフローの見直しが必要になった。
Xcode 15 で導入された String Catalog(.xcstrings)は全言語・全キーを1つの JSON ファイルに集約する設計になっているため、日本語 + 英語の2言語であればまだ管理できるが、10言語対応ともなるとファイルサイズは一気に膨れ上がり、数千〜数万行の JSON になる。
こうなると問題になるのが 翻訳ワークフローの運用 だ。全言語が混在した巨大な JSON をそのまま翻訳者に渡しても、対象言語以外のデータが大量に含まれているため作業効率が悪く、レビューも困難になる。Git での差分管理も見通しが悪くなり、意図しない変更の混入リスクが高まる。
本記事では、.xcstrings を言語単位に分解し、翻訳作業を安全かつ効率的に回すための実践的なワークフローを紹介する。
検証環境
- macOS 15.7.3
- Xcode 26.1.1
なぜ .xcstrings は肥大化するのか
まず .xcstrings の内部構造を確認する。中身は以下のような JSON だ。
{
"sourceLanguage": "ja",
"strings": {
"home_greeting": {
"comment": "ホーム画面の挨拶",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Hello!"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "こんにちは!"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "你好!"
}
}
}
}
}
}
1つのキーに対して、対応言語の数だけ localizations が入れ子になる。つまりファイルサイズは キー数 × 言語数 に比例して肥大化する。
| キー数 | 言語数 | おおよその行数 | ファイルサイズ目安 |
|---|---|---|---|
| 100 | 2 | ~800行 | ~20KB |
| 100 | 10 | ~3,500行 | ~90KB |
| 500 | 10 | ~17,000行 | ~450KB |
| 1,000 | 10 | ~35,000行 | ~900KB |
500キー × 10言語で約17,000行。これだけの規模になると、特定言語の翻訳状況を把握するだけでも一苦労だ。差分レビューで変更箇所を追うのも困難になり、翻訳者に必要な部分だけを渡すことも難しくなる。
XLIFF による言語単位の分割と統合
XLIFF とは
XLIFF(XML Localization Interchange File Format)は、翻訳業界の標準フォーマットだ。Xcode は .xcstrings を XLIFF 形式でエクスポート/インポートする機能を標準で備えている。
最大のポイントは 1言語1ファイル に分かれることである。
エクスポート手順
xcodebuild -exportLocalizations \
-project MyApp.xcodeproj \
-localizationPath ./Localizations \
-exportLanguage en \
-exportLanguage ja \
-exportLanguage zh-Hans \
-exportLanguage es
実行すると、以下のようなディレクトリ構造が生成される。
Localizations/
├── en.xcloc/
│ └── Localized Contents/
│ └── en.xliff
├── ja.xcloc/
│ └── Localized Contents/
│ └── ja.xliff
├── zh-Hans.xcloc/
│ └── Localized Contents/
│ └── zh-Hans.xliff
└── es.xcloc/
└── Localized Contents/
└── es.xliff
Finder 上では以下のように表示される。

en.xcloc をダブルクリックで起動すると、XLIFFエディタが起動する。GUIで文字列リソースを操作したい場合はこのエディタで編集することができる。

XLIFF の中身
生成された XLIFF はシンプルな XML だ。
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="MyApp/Localizable.xcstrings" source-language="ja" target-language="en" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="26.1.1" build-num="17B100"/>
</header>
<body>
<trans-unit id="cart_empty_message" xml:space="preserve">
<source>カートに商品がありません</source>
<target state="translated">Your cart is empty</target>
<note>カート画面の空状態メッセージ</note>
</trans-unit>
<trans-unit id="home_greeting" xml:space="preserve">
<source>こんにちは!</source>
<target state="translated">Hello!</target>
<note>ホーム画面の挨拶</note>
</trans-unit>
<trans-unit id="settings_title" xml:space="preserve">
<source>設定</source>
<target state="new"/>
<note>設定画面のタイトル</note>
</trans-unit>
</body>
</file>
</xliff>
<source> に原文(日本語)、<target> に翻訳先言語のテキストが入る。state="new" は未翻訳を意味する。
この形式であれば、翻訳者には「<target state="new"/> になっている箇所を翻訳して state="translated" に変えてください」と明確に依頼できる。対象言語のデータしか含まれていないため、作業範囲が一目瞭然だ。
補足:
.xcstringsと XLIFF の state 属性値の違い
.xcstrings(JSON)内部と XLIFF(XML)では state 属性の命名規則が異なる。.xcstringsではneeds_review(アンダースコア)が使われるが、XLIFF 1.2 仕様ではneeds-review-translation、needs-review-adaptation、needs-review-l10n(ハイフン区切り)が正式な値となる。Xcode がエクスポート/インポート時に自動変換するため実務上は意識する必要がないが、XLIFF を手動編集する場合やサードパーティツールで処理する場合は注意が必要だ。
インポート手順
翻訳済みの XLIFF を .xcstrings に書き戻すのも1コマンドで完了する。
xcodebuild -importLocalizations \
-project MyApp.xcodeproj \
-localizationPath ./Localizations/en.xcloc
これで Localizable.xcstrings の英語部分だけが更新される。他の言語やキーには一切影響しない。
まとめてインポートするオプションは用意されていない。日本語(ja)を更新したい場合は、以下のように個別に実行する。
xcodebuild -importLocalizations \
-project MyApp.xcodeproj \
-localizationPath ./Localizations/ja.xcloc
推奨ワークフロー
XLIFF を活用することで、以下のメリットが得られる。
- 1言語1ファイルなのでサイズが小さく、翻訳対象が明確になる
- source / target の対が明確で、翻訳箇所を間違えにくい
- state 属性で未翻訳・翻訳済み・要レビューを機械的に判別できる
- Xcode 公式のワークフローに乗っているので安定性が高い
- 翻訳会社に外注する場合も XLIFF はそのまま使える業界標準フォーマットである
これらを踏まえ、最終的に推奨するワークフローは以下の通りだ。
.xcstringsに日本語でキーを追加する。Xcode の String Catalog エディタで編集するxcodebuild -exportLocalizationsで XLIFF に分解し、言語ごとの.xliffファイルを生成する- 必要に応じて未翻訳キーだけ抽出する。
state="new"のtrans-unitを対象にする - 各言語の XLIFF を翻訳する。翻訳者への外注・社内翻訳・AI ツールの活用など。
targetを埋めてstateを"translated"に更新する xcodebuild -importLocalizationsで.xcstringsに書き戻す- 未翻訳キー検出とビルド確認を CI で実施する
手順3の未翻訳キー抽出は、たとえば以下のように grep で件数を確認できる。
# 未翻訳キーの件数を確認
grep -c 'state="new"' Localizations/en.xcloc/Localized\ Contents/en.xliff
# 未翻訳キーの id を一覧表示
grep -B1 'state="new"' Localizations/en.xcloc/Localized\ Contents/en.xliff | grep 'trans-unit id'
まとめ
.xcstrings は Xcode の String Catalog として非常に便利な仕組みだが、多言語対応が進むにつれてファイルが肥大化し、管理やレビューが困難になるという課題がある。
XLIFF による言語単位の分割は、翻訳者との協業を円滑にするだけでなく、差分レビューの見通しを良くし、意図しない変更の混入を防ぐ効果もある。「巨大な JSON を丸ごと扱う」のではなく、「構造的に安全な単位に分解してから作業する」という考え方は、多言語対応に限らずファイル管理全般に通じる原則だろう。
多言語対応の運用に悩んでいる方の参考になれば幸いである。










