新たにリリースされたCache-Controlアナライザーの英語版に、日本語が表示されていました。ステータスが「Fresh」ではなく「新鮮」となっていたのです。このミスは、ページ側が英語のラベルのみを提供しているにもかかわらず、共有ロジックがハードコードされた日本語の文字列を返していたことに起因していました。

開発者は一連の軽量なブラウザツールを構築しており、それぞれに英語のページと日本語のページがあります。これらは同じパース関数とコアロジックを再利用しています。表示される文言だけが異なるべきでした。Cache-Controlアナライザーをリリースした際、英語のインターフェースには正しいラベルが表示されていましたが、レンダリングされた値はロジック層から来ており、そこにはまだ日本語のリテラルが含まれていました。コンソールエラーは発生せず、ページは正常に見えましたが、英語を話すユーザーに提示された情報は間違っていました。

なぜ共有ロジックが翻訳を裏切ることがあるのか

このバグは設計上の選択に起因していました。何を表示するかを決定するコア関数が、日本語のリテラル文字列を返していたのです。周囲の英語テキストを担当するページ層は、それらの値を置き換える機会がありませんでした。ロジックとUIが明確に分離されていたため、テスト中には問題が目に見えない状態でした。技術的にはすべてが「動作」していましたが、ユーザー向けの言語は誤っていたのです。

欠点は、共有モジュール内で使用されている言語が、それを利用するすべてのフロントエンドのデフォルトになってしまうことです。別の言語が必要な場合、そのデフォルトが隠れたバグとなります。

修正策:キー、パック、そしてセーフティネット

著者は関心の分離を行うためにアーキテクチャを書き換えました。

  • メッセージパック (Message packs) が、各言語の人間が読めるすべての文字列を保持するようになりました。
  • 共有ロジック (Shared logic) は、生のテキストではなく、シンボリックなキーのみを返します。
  • ページ (Pages) は、キーに基づいて関連するパックから適切な単語を検索します。

メッセージに数値を含める必要がある場合、新しいコードではテンプレート文字列ではなく、小さな関数を使用します。これにより、各言語が数値の配置場所を決定できるようになり、語順の違いに対応できます。

また、単純な静的解析ステップも追加されました。ビルドプロセスが共有ファイルをスキャンして日本語文字を探します。もし見つかった場合は、開発者に即座に通知され、ハードコードされた外国語が紛れ込むのを防ぎます。

この経験から著者が学んだこと

  1. 翻訳はレビュー工程として機能する。 英語のメッセージを書いている際、著者は一部の日本語の訳語が曖昧であることに気づきました。翻訳することで、両方の言語においてより明確な言い回しを強制することができました。
  2. 文字列を返す共有関数は、全員に対して言語を固定してしまう。 関数が言語を決定してしまうと、異なる言語を期待するすべての利用者がその間違いを引き継ぐことになります。このバグはUIの不具合ではなく、ロジックの欠陥なのです。

多言語ツールをメンテナンスするすべての人への推奨事項

  • コア関数からは文字列ではなくキーを返す。 ローカライズはUI層に任せましょう。
  • または、必要な文字列をパラメータとして関数に渡す。 これにより、ロジックを言語に依存させないようにできます。
  • 共有モジュールにハードコードされたネイティブ言語のテキストがないか監査する。 非ASCII文字を素早く検索することで、隠れた問題を見つけ出すことができます。
  • 共有コード内の外国語文字に対して、ビルド時のチェックを追加する。 リリース後の混乱を防ぐには、早期発見が勝ります。

次に注意すべきこと

まとめ: プロジェクト内で言語バージョン間でコードを共有している場合は、共有部分が文言を決定しないようにしてください。各ページに独自の単語を提供させることで、英語のページが誤って日本語を話してしまうという恥ずかしい事態を避けることができます。