2024-05-10
i18n jsonファイルのキーの不一致をTypeScriptで防いでいる話 (Archived)
Archived: 旧ブログから移行した記事です。内容が古い可能性があります。
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の型チェック機構に乗っかるのではなく、普通に動的なテストとして実装してしまうのもありだとは思う。