チェック・トラブル対処Read this article in English

ローカライズファイルのキー重複 — エラーを一切出さないバグ

CSV、JSON、キーバリュー形式のリソースファイルなど、ほとんどのローカライズファイル形式はキーの一意性を要求せず、ほとんどのパーサーもチェックしません。この組み合わせが、キー重複を検出しづらい厄介なバグにしています。何もクラッシュせず、パースエラーも出ず、ファイルは読み込まれてゲームは動きます。唯一の症状は、そのキーに対する2つの値のうち片方が、静かに一度も使われないまま終わることです。

実際にどう混入するか

キーの重複はわざと打ち込まれることはほとんどありません。日常的な作業の摩擦から積み上がります。

  • スプレッドシートのコピー&ペースト — 新しい行を上や下に挿入する際に既存行が複製され、キー列もそのままコピーされる
  • マージ — 2つのブランチがそれぞれ同じ新しいキーに対して行を追加し(同じ機能に対して独立に作業した結果であることが多い)、マージ時に競合として扱われず両方が残る
  • 連結された書き出し — ビルドスクリプトが複数の原本ファイルやスプレッドシートのタブを1つの出力ファイルにまとめる際、同じキーが複数箇所に存在するかをチェックしない

なぜ「後勝ち」が問題を覆い隠すのか

重複キーを含むファイルをパーサーが読み込むとき、典型的な挙動 — JSONオブジェクトでも、CSVの行から構築された辞書でも、たいていのキーバリューローダーでも — は、同じキーに対する新しい値が前の値を単純に上書きする、というものです。ファイル内で最後に現れたエントリが勝ち、それより前のものは、パース時にもビルド時にも実行時にも、一切の警告なく破棄されます。

この性質こそが、重複を単なる「乱雑さ」ではなく「危険」にしています。キーが重複したビルドは、あらゆる観測可能なシグナルにおいて、重複していないビルドとまったく同じに見えます。目に見えるエラーもなく、追跡できる分かりやすい症状もありません。ただ、書かれなかったかのように振る舞う文字列があるだけです。実際、機能的にはその通り書かれなかったのですから。

本当に危険なのは「値が食い違う」重複

すべての重複キーが同じ程度に有害というわけではありません。同じキーの2つの行がたまたま同一の値を持っているなら、後勝ちの挙動は機能的には無害です — 冗長ではあっても間違ってはいません。危険なのは、同じキーの2つの行が異なる値を持っている場合です。古い下書きの行が改訂版と一緒にファイルに残っていたり、2人の翻訳者がそれぞれ独立に同じ欠落キーを異なる文言で埋めたりするケースです。この場合、どちらの値が実際に出荷されるかは誰かの意図ではなくファイル内の順序で決まり、ファイルが並び替えられたり別の順序で再書き出しされたりすると、ビルド間で静かに入れ替わることさえあります。

ファイルをまたいだ重複

同じ失敗パターンは、1ファイル内だけでなくファイル間でも起こります。プロジェクトがローカライズ内容を画面別・機能別・コンテンツ種別で複数ファイルに分割している場合、同じキーが異なる値で2つのファイルに定義されてしまうと、結果はビルド時にそれらのファイルがどの順序で読み込まれる・マージされるかに完全に依存します。これはファイル内の重複より目視で見つけにくいものです。単体で見ればどのファイルもそれ自体は間違って見えないからで、衝突はプロジェクト全体を合わせたキー空間のレベルで初めて存在するからです。

なぜパーサーは何も文句を言わずに重複を受け入れるのか

これはパーサーのバグではなく、これらの形式が一般的にメモリ上でどう表現されるかの直接的な帰結です。JSONオブジェクトやほとんどの言語内蔵の辞書型は、同じキーへの繰り返し代入を、後書き優先という通常の挙動として受け入れるように作られています。これはそのデータ構造にとって標準的で明確に定義された挙動であり、パース失敗ではありません。CSVに至っては、アプリケーション側がどの列をキーとみなすか決めるまで、そもそも「キー」という概念自体が存在しません。一意性は、フォーマットの上にローカライズのワークフロー側が課すべきルールであって、フォーマットが代わりに保証してくれるものではありません。

取り込み時の検出と、単一の原本による予防

フォーマットにもパーサーにも重複を自動で表に出す仕組みがない以上、検出は意識的な工程として組み込む必要があります。取り込み時に全ファイルのキー一覧を走査して重複をチェックし、見つかったら見過ごしていい警告ではなくブロッキングな問題として扱うべきです。重複はビルドに出荷される値を静かに入れ替え得るからです。このチェック自体は文字列リストの重複を比較するだけで軽く、たまにではなく取り込みのたびに毎回実行する価値があります。

より恒久的な対策は構造そのものにあります。キー一覧を単一の原本として一箇所で生成・書き出しし、複数のスプレッドシートやブランチ、後で結合される複数ファイルにそれぞれ独立にキーを打ち込ませない、という運用です。キーを作れる場所が正確に一つしかなければ、同じキーが2回作られる隙間そのものが存在しなくなります。

関連記事