AI & 技術

翻訳したローカライズファイルがアプリを壊す理由(そして手を触れてはいけない部分)

9月 23, 2026

翻訳したローカライズファイルがアプリを壊す理由(そして手を触れてはいけない部分)

ローカライズファイルは、文章を含んだコードである

PO、YAML、.properties、RESX、XLIFF、JSONのロケールファイルは文章のリストのように見えますが、文章なのは値だけです。キーはコードが参照する識別子であり、プレースホルダーは実行時にコードが埋めるスロットであり、エスケープやマークアップはパーサーが依存する構文です。行全体をテキストとして扱う翻訳者は、読みやすいのに読み込めないファイル、あるいは読み込めても画面が初めて数値をフォーマットした瞬間にクラッシュするファイルを生み出してしまいます。この不具合は通常ひとつの言語にしか現れないため、レビューで最も見つけにくい種類のバグになります。

翻訳後もそのまま残すべき5つのもの

  • キーとリソース名——コードはキーで文字列を参照するため、翻訳されたキーは単に存在しない文字列になります。
  • プレースホルダー——%s、%1$d、{0}、{name}、ICUの引数はコードによって埋められます。ひとつでも名前を変えたり削除したりすると、書式が崩れるか実行時に例外が発生します。
  • エスケープとエンコーディングの規則——従来のProperties.load(InputStream)メソッドで読み込まれる.propertiesファイルはLatin-1として扱われ、それ以外の文字は\uXXXXエスケープで記述されます。そのため、非ラテン文字をそのまま書くと文字化け(mojibake)することがあります。
  • 文字列内のマークアップ——HTMLタグ、XLIFFのインラインコード、Markdownは対応が取れた状態を保つ必要があり、そうでないと描画された文字列が周囲のページを壊します。
  • ファイル構造——YAMLのインデント、ブロックスカラー、アンカーとエイリアス、XMLの入れ子は構文です。インデントがずれたり、アンカー名が翻訳されたりすると、ファイルの意味が変わってしまいます。

複数形は単数形に「s」を付けたものではない

英語には複数形が2つあるため、どの言語も同じだと考えがちです。GNU gettextマニュアルは、それがなぜうまくいかないかを示しています。各言語は独自のPlural-Forms規則を宣言し、英語はnplurals=2を使いますが、ロシア語では数値の末尾の桁によって選ばれる3つの形が必要です。ロシア語に翻訳されたPOファイルに2つの形しか埋められていない場合、コンパイルも実行もできますが、末尾が2、3、4で終わるすべての数に対して誤った文法を表示します。複数形を正しく扱うとは、原文の言語にたまたまあった数ではなく、翻訳先の言語が必要とする数のmsgstrエントリを用意することです。

プレースホルダーは移動が必要なこともある

語順は言語によって異なるため、2つの値を含む文では、それらを逆の順序に置かなければならないことがよくあります。%1$sや%2$sのような位置指定プレースホルダー、あるいは{count}や{user}のような名前付きプレースホルダーは、まさにそのために存在します。翻訳で順序を入れ替えてもかまいませんが、それぞれが必ずちょうど1回ずつ現れなければなりません。単なる%sのような匿名プレースホルダーは安全に並べ替えられないため、翻訳後ではなく翻訳前に原文の文字列で修正しておくべき問題です。

翻訳前に知っておきたいフォーマットごとの規則

フォーマットにはそれぞれ落とし穴があります。YAMLでは、仕様がリテラルブロック(|)と折り畳みブロック(>)に異なる改行の扱いを定めており、アンカー(&)とエイリアス(*)によってあるノードが別のノードを再利用できるため、どちらもそのまま残す必要があります。XLIFF 2.1は元の書式を<ph>や<pc>のようなインライン要素で囲んでおり、翻訳ではそれらを正しい語句の周りの位置に保たなければなりません。.NETでは、RESXリソースは実行時に現在のUIカルチャに合わせて名前で検索されるため、名前が変わったdata要素は黙って既定の言語にフォールバックします。そしてJavaの.propertiesファイルは、最初のエスケープされていない=、:、または空白をキーの区切りとして扱うため、翻訳されたキーに紛れ込んだコロンがキーを2つに分割してしまいます。

ほとんどの破損を見つけるレビューチェックリスト

  1. すべての原文と訳文の文字列でプレースホルダーを数えます——順序が違っても、多重集合は一致していなければなりません。
  2. 値ではなくキーを比較します——原文にあって訳文で欠けている、または名前が変わっているキーはすべてバグです。
  3. リリース前に、翻訳したファイルを実際のパーサー(msgfmt、YAMLローダー、ResXResourceReader)で読み込みます。
  4. 複数形のエントリは、原文の言語ではなく翻訳先の言語の規則に照らして確認します。
  5. 最も長い訳文を最も狭いUI要素で表示します——他の言語のテキストは英語より長くなることがよくあります。
なぜアプリがある言語でだけクラッシュし、他の言語では問題ないのですか?

ほとんどの場合、その言語でだけプレースホルダーが変更された——名前が変わった、削除された、または重複した——ためです。コードはすべての言語に同じ引数を渡すため、それと一致しなくなった唯一の訳文が、その画面をフォーマットする際に失敗します。

英語は2つなのに、なぜロシア語には3つの複数形の文字列が必要なのですか?

複数形の規則はソフトウェアではなく言語に属するものです。gettextでは各言語が独自の規則を宣言でき、ロシア語は数値の末尾の桁に基づいて3つの形から選ぶため、2つの形しかない訳文は多くの数に対して誤った文法を表示します。

なぜJavaの.propertiesファイルでアクセント付き文字が文字化けするのですか?

従来のProperties.load(InputStream)がファイルをLatin-1として読み込むためです。その範囲外の文字は\uXXXXエスケープで記述するか、正しいエンコーディングを使うリーダーでファイルを読み込む必要があります。

翻訳でプレースホルダーの順序を変えてもよいですか?

はい。位置指定(%1$s、%2$s)や名前付き({count}、{user})の場合は可能で、自然な語順のために並べ替えが必要になることもよくあります。ただし、すべてのプレースホルダーはちょうど1回ずつ現れなければなりません。単なる%sプレースホルダーは安全に並べ替えられません。

なぜキーは決して翻訳してはいけないのですか?

キーはテキストではなく識別子だからです。コードはキーで文字列を要求するため、キーが翻訳されると検索が失敗し、アプリは生のキーを表示するか既定の言語にフォールバックします。

値は翻訳し、コードはそのままに

PO、YAML、.properties、RESXファイルをアップロードすると、キー、プレースホルダー、エスケープ、構造が元の位置のまま翻訳されて戻ってきます。

無料でロケールファイルを翻訳
タイムスタンプを保持したままSRT字幕を翻訳する方法featured image

AI & 技術

タイムスタンプを保持したままSRT字幕を翻訳する方法

字幕翻訳が精密な作業である理由 SRTファイルは厳密に構造化されたフォーマットです。各字幕ブロックには、シーケンス番号、タイムスタンプの範囲(開始→終了)、そして1〜2行のテキストが含まれます。タイムスタンプに触れずにテキストだけを翻訳す…

Amanda Leeのプロフィール写真

Amanda Lee

PowerPointプレゼンテーションをオンラインで翻訳する方法featured image

AI & 技術

PowerPointプレゼンテーションをオンラインで翻訳する方法

プレゼンテーション翻訳の本当の難しさ PowerPointのプレゼンテーションは単なるテキストではありません。各スライドは緻密に設計されたキャンバスであり、テキストボックスは視覚的なバランスのために配置され、フォントサイズが階層を作り出し…

Amanda Leeのプロフィール写真

Amanda Lee