はじめに
以下の投稿で、SvelteKit で作成したアプリに入力チェックを導入しました。ただし、エラーメッセージの日本語化を実現できずに、保留していました。
日本語化できる方法が分かったので、その方法を記載します。
修正内容
前述の記事に記載した users.js
を以下の様に修正することで、日本語を表示できました。
import { z } from 'zod';
+import i18next from 'i18next';
+import translation from 'zod-i18n-map/locales/ja/zod.json' assert {type: "json"};
+import { makeZodI18nMap } from 'zod-i18n-map';
+
+i18next.init({
+ lng: 'ja',
+ resources: {
+ ja: { translation },
+ },
+});
+
+z.setErrorMap(makeZodI18nMap({ t:i18next.t, ns: 'translation' }));
// See https://zod.dev/?id=primitives for schema syntax
/**
* ユーザーのスキーマ
*/
export const userSchema = z.object({
id: z.string().max(20),
name: z.string().min(2).max(20).endsWith("!"),
email: z.string().email()
});
...
アプリを起動してアクセスすると、日本語が表示されることを確認できましたー。
解決までの経緯
この解決策に至った経緯は、node_modules
フォルダ内にダウンロードされた zod-i18n-map のソースコードを確認していたところ、makeZodI18nMap()
関数の引数に t
と ns
を指定できることに気づいたのがきっかけでした。
...
type ZodI18nMapOption = {
t?: i18n["t"];
ns?: string | readonly string[];
handlePath?: HandlePathOption;
};
...
これ以前にいろいろ調べた結果、i18next.init(..)
した状態を zod-i18n-map
側で共有できていないっぽいことに気づいていました。ですので、この t
に i18next.t
を指定すれば良さそう!と思いついた次第です。
z.setErrorMap(makeZodI18nMap({ t:i18next.t }));
しかし、これだけでは日本語化されませんでした。
いままで、エラーメッセージが undefined
だったのが、英語で表示されるようになっただけでした。
さらに zod-i18n-map のソースコードを追っていくと、ns
というパラメータがデフォルトで zod
となっていることが分かりました。このパラメータは i18next におけるネームスペースに相当します。
前述の index.d.ts
より、makeZodI18nMap()
関数の引数に ns
というパラメータがあります。この関数のソースコードを確認すると、ここで指定した値(option.ns
)によって ns
が上書きされることが分かりました。
...
var defaultNs = "zod";
var makeZodI18nMap = (option) => (issue, ctx) => {
const { t, ns, handlePath } = {
t: import_i18next.default.t,
ns: defaultNs,
...option, // t と ns が上書きされる
handlePath: {
context: "with_path",
ns: option?.ns ?? defaultNs,
keyPrefix: void 0,
...option?.handlePath
}
};
...
そして、あてずっぽうで ns
に undefined
を指定したところ、日本語でエラーメッセージを取得できました。
z.setErrorMap(makeZodI18nMap({ t:i18next.t, ns: undefined }));
2023/4/20 追記:
あてずっぽうではいかんですよねw
というわけで、i18next についてもっと調査してみたところ、ちゃんと理解できましたー。
以下の様に、ns
に translation
を指定すれば良かったのです。
(どうやら、まだJavaScript の省略記法に慣れてなかったようです。。。)
参考: https://www.i18next.com/principles/fallback#namespace-fallback
import { z } from 'zod';
import i18next from 'i18next';
import translation from 'zod-i18n-map/locales/ja/zod.json' assert {type: "json"};
import { makeZodI18nMap } from 'zod-i18n-map';
i18next.init({
lng: 'ja',
resources: {
ja: { translation }, // この記述は実際には ja: { translation: translation }。キー名が ns に相当する。
},
});
-z.setErrorMap(makeZodI18nMap({ t:i18next.t, ns: undefined }));
+z.setErrorMap(makeZodI18nMap({ t:i18next.t, ns: 'translation' }));
// See https://zod.dev/?id=primitives for schema syntax
/**
* ユーザーのスキーマ
*/
export const userSchema = z.object({
id: z.string().max(20),
name: z.string().min(2).max(20).endsWith("!"),
email: z.string().email()
});
...
ちなみに、動的に言語を切り替える場合は以下の様に addResourceBundle()
と changeLanguage()
を使うことで実現できます。
import ja from 'zod-i18n-map/locales/ja/zod.json' assert { type: "json" };
import es from 'zod-i18n-map/locales/es/zod.json' assert { type: "json" };
i18next.init();
i18next.addResourceBundle('ja', 'translation', ja);
i18next.changeLanguage('ja');
console.log("ja:", i18next.t('errors.custom')); // → ja: 入力形式が間違っています。
i18next.addResourceBundle('es', 'customNs', es); // 独自のネームスペースを設定可能。
i18next.changeLanguage('es');
console.log("es:", i18next.t('customNs:errors.custom')); // → es: Entrada inválida
// 独自のネームスペースをコロン区切りで先頭に記載することでネームスペースが適用される。
まとめ
これで SvelteKit で入力チェックエラーを日本語化することに成功しました。一安心です。
ドキュメントを見ただけでは把握できなかったことが、ソースコードを追うことで把握できました。JavaScritp の場合、node_modules
にソースコード一式がダウンロードされるし、直接修正してデバッグもできるので便利です。
まだまだ、JavaScript 初心者なところがあるので、もっと鍛えていこうと思います。