asazutaiga.dev
🌍

i18n jsonファイルのキーの不一致をTypeScriptで防いでいる話

2024/05/10
#i18n
この記事は3分で読めます

i18nあるある

json ファイルの typo を防ぎたい を読んで、あるある、と思った。

翻訳リソースファイルを複数用意する場合に、キーが微妙に一致してなくて...みたいな問題が起こりがち。片方には作成済みだがもう片方には漏れていて、みたいなのもありがち。

なので、TypeScriptでキーが一致することを検証するようにしている。

(ただし、2ヶ国語対応だからできているという面もある。大量の言語、大量のJSONファイル、大量のキーがある場合にワークする方法ではないと思う。)

TSファイルの動的生成

翻訳ファイルが存在しているディレクトリを読みに行き、以下のようなファイルを生成している。

(genI18NextDts.js の詳細は省略。普通に、fs でディレクトリを読んで、いい感じにテンプレートリテラルなど使って内容を組み立てているだけなので。)

/** This file is generated by scripts/genI18NextDts.mjs */
import authJa from '../../../public/locales/ja/auth.json';
import commonJa from '../../../public/locales/ja/common.json';
//...
import authEn from '../../../public/locales/en/auth.json';
import commonEn from '../../../public/locales/en/common.json';
// ...
type IsEqual<T, U> = (T extends U ? (U extends T ? true : false) : false) &
  ([T, U] extends [{[k: string]: unknown}, {[k: string]: unknown}]
    ? {
        [K in keyof T | keyof U]: K extends keyof T
          ? K extends keyof U
            ? IsEqual<T[K], U[K]>
            : false
          : false;
      }[keyof T | keyof U] extends true
      ? true
      : false
    : true);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function assertTypeIsEqual<T extends true>(): void {}

// Check that all locales have the same keys
assertTypeIsEqual<IsEqual<typeof authJa, typeof authEn>>();
assertTypeIsEqual<IsEqual<typeof commonJa, typeof commonEn>>();
// ...

こうしておくと、ビルド時にチェックが走る。えらい。 ただし、「どのキーが漏れているか」まではエラーとして出せていないので、改善の余地はある。 また、そもそもTSの型チェック機構に乗っかるのではなく、普通に動的なテストとして実装してしまうのもありだとは思う。

Asazu Taiga
@AsazuTaiga
© 2024 Asazu Taiga