Zodのバリデーションエラーメッセージを日本語化・カスタム表示する方法

TypeScript Zod Validation Frontend
結論

一括日本語化は z.setErrorMap() を設定し、個別は z.string({ required_error: "必須です" }) を指定します。

// Zod公式仕様:グローバル ErrorMap によるエラーメッセージ共通化
import { z } from "zod";

const customErrorMap: z.ZodErrorMap = (issue, ctx) => {
  if (issue.code === z.ZodIssueCode.invalid_type) {
    if (issue.received === "undefined") {
      return { message: "必須項目です" };
    }
    return { message: "入力形式が正しくありません" };
  }
  if (issue.code === z.ZodIssueCode.too_small) {
    return { message: `${issue.minimum}文字以上で入力してください` };
  }
  return { message: ctx.defaultError };
};

z.setErrorMap(customErrorMap);

Zod公式のメッセージ評価優先順位

Zod(v3/v4)におけるエラーメッセージ決定の優先順位は以下の通り厳格に規定されています。

  1. 最優先: 各スキーマメソッドの個別メッセージ (z.string().min(5, "5文字以上"))
  2. 第2優先: 型宣言時のオプション (z.string({ required_error: "必須入力" }))
  3. 第3優先: グローバル設定 (z.setErrorMap(customMap))
  4. 最低: Zod デフォルト英語メッセージ

実際に起こるトラブル:setErrorMap を書いたのに英語のままになる罠

GitHub Issues(colinhacks/zod #2653 等)で最も頻出するトラブルと解決策です。

原因1: インポートのタイミング依存(インスタンス化の遅延評価漏れ)

エントリーポイントで z.setErrorMap を呼び出すに、別ファイルから z.object({...}) スキーマがモジュール読み込み(import)されていると、そのスキーマには旧デフォルトの ErrorMap がバインドされたままになります。

// ❌ 反映されないダメな順序
import { userSchema } from "./schema"; // この時点で Zod インスタンスが初期化完了
import { z } from "zod";
z.setErrorMap(myCustomMap); // 遅すぎる

原因2: zod-i18n-map 使用時の i18next 初期化漏れ

コミュニティ標準の zod-i18n-map を使用する場合、i18next.init() 呼び出しより前に z.setErrorMap(zodI18nMap) を呼ぶとメッセージが undefined またはフォールバック表示になります。


トラブル回避・正しい設定手順

  1. アプリケーションのエントリーポイントの最上部(他のスキーマファイルの import より先)で z.setErrorMap を実行する
  2. zod-i18n-map を使う場合は i18next.init() の完了直後に z.setErrorMap をセットする
  3. 個別フィールドの文言を上書きしたい場合のみ、スキーマ定義側に { message: "..." } を付与する
// ⭕ 正しいインポート・初期化順序
import i18next from "i18next";
import { z } from "zod";
import { zodI18nMap } from "zod-i18n-map";

// 1. 先に i18next を初期化
i18next.init({
  lng: "ja",
  resources: { ja: { zod: require("zod-i18n-map/locales/ja/zod.json") } },
});

// 2. その後即座に setErrorMap を登録
z.setErrorMap(zodI18nMap);

// 3. 最後にスキーマを読み込む
import { userSchema } from "./schema";