ローカライズファイルをコードとして扱う — 変更のたびにチェックする
ローカライズファイルは、あらゆる意味でビルドの入力です。パースされ、動いているコードに文字列を供給し、壊れていればビルドを失敗させたり実行時に画面をクラッシュさせたりします。それにもかかわらず、多くのチームはそう扱っていません。ソースコードにはテストスイートとマージ前の必須チェックがある一方、その隣に置かれているCSVやJSONは、開いて目で見て、信用されているだけです。
解決策は特別なものではありません。コードに対して既に走らせている自動チェックと同種のものを、ローカライズファイルにも、変更のたびに、ビルドに届く前に走らせるだけです。
チェックする価値があるもの
有用なチェックは、決定的で安価なものに限られます。判断や好みの余地がなく、ファイル一つあたりミリ秒単位で機械が合否を出せるものです。
- パース可能性 — 主張しているCSVやJSONなどの形式として正しく整形されていて、区切り文字の混入や閉じ忘れがないこと
- エンコーディング — パイプラインが期待するエンコーディングで保存されていること。誤ったエンコーディングで保存されたファイルは文字化けを生みますが、多くのツールはそれを黙って素通りさせます
- プレースホルダーの整合性 — 原文に含まれる{0}や{playerName}、%sなどのトークンが訳文にもすべて存在し、勝手に新しいものが作られていないこと
- タグの対応 — <b>や<color=...>のようなマークアップ・リッチテキストタグが開閉で対になっていること。翻訳者のコピペミスで、画面上にスタイルタグが閉じられないまま残ることを防ぎます
- キーの重複 — 同じキーが異なる値で二重に存在しないこと。どちらが優先されるかは読み込み側のコードで未定義の挙動になりがちです
具体的な失敗パターン
スプレッドシートで作業する翻訳者が、繰り返し登場するプレースホルダーの一つだけ名前を変えて他をそのまま残す、あるいは文章を自然に読ませるために調整する過程でプレースホルダーごと落としてしまう、というケースがあります。意味を目で追う人間にはその行は問題なく見えます。実行時には、トークンがそのまま画面に表示されるか、あるいは期待されていた引数が渡されずフォーマット処理がエラーになります。
source: "You found {0} gold and {1} gems."
broken: "{0}ゴールドとジェムを見つけた。" // {1}が消えている
check: プレースホルダー不一致 — 訳文に{1}が存在しません決定的・高速・失敗時は明確に
これらのチェックが役立つのは、あらゆる変更に対してボトルネックにならない速さで走ること、そして失敗を見逃したり読み違えたりしないくらい明確に伝えることの両方が揃ったときだけです。「検証に失敗しました」とだけ言うメッセージは、報告者をふりだしに戻します。役立つメッセージは、具体的なキー、行、何がどう間違っているか(プレースホルダー{1}が欠落、<color>タグが閉じていない、キーui.button.confirmが重複、など)まで名指しし、デバッガを開かなくても直せるようにします。
根本的な目標は単純です。壊れたローカライズファイルが絶対にプレイヤーに届かないようにすること。つまりチェックはファイルがマージやエクスポートされる前に走らなければならず、後からではいけません。既に出荷されたビルドで同じ問題を見つけるのは、プルリクエストの段階で見つけるより明らかに高くつきます。
チームにとって何が変わるか
最大の変化は技術的なものではなく、フィードバックが返ってくるまでの時間です。自動チェックがなければ、翻訳者は誰かがビルドをプレイして気づくまで自分のファイルに問題があることを知りません。それは提出から数日から数週間後になることもあり、その頃には翻訳者は他の作業に移っていて、一度失った文脈を読み直すところから始めることになります。
変更のたびにチェックが走る環境では、同じ翻訳者は数分以内に、ファイルと文脈がまだ頭に残っているうちに気づけます。これによりローカライズのQAは、たまに発生する高コストな割り込みから、日常的で低コストなフィードバックへと変わります。これは自動テストがソースコードに対してすでに起こした変化と同じものです。