You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让next-intl与Zod校验规则实现错误信息国际化?

Zod 校验错误与 next-intl 国际化结合实现方案

步骤1:配置国际化资源文件

在项目的 locales 目录下,为每种语言添加校验错误的翻译键值对,示例如下:

en.json

{
  "validation": {
    "firstNameRequired": "First name is required",
    "firstNameMax": "First name must be less than 32 characters",
    "lastNameRequired": "Last name is required",
    "lastNameMax": "Last name must be less than 32 characters",
    "passwordRequired": "Password is required",
    "passwordInvalid": "Password must contain at least one uppercase letter, lowercase letter, number, and special character, and be at least 8 characters long"
  }
}

zh.json

{
  "validation": {
    "firstNameRequired": "请输入名字",
    "firstNameMax": "名字长度不能超过32个字符",
    "lastNameRequired": "请输入姓氏",
    "lastNameMax": "姓氏长度不能超过32个字符",
    "passwordRequired": "请输入密码",
    "passwordInvalid": "密码必须包含至少一个大写字母、小写字母、数字和特殊字符,且长度不少于8位"
  }
}

步骤2:改造 Zod Schema 为动态生成函数

将原静态 Schema 改为接收翻译函数的工厂函数,确保错误信息能实时匹配当前语言:

/* eslint-disable camelcase */
import { TypeOf, object, string } from "zod";
import { UseTranslationsResponse } from 'next-intl';

const passwordValidation = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/u

// 接收validation命名空间的翻译函数,返回带国际化错误的Schema
export const createUserSchema = (t: UseTranslationsResponse<'validation'>) => 
  object({
    firstName: string({ required_error: t('firstNameRequired') })
      .min(1, t('firstNameRequired'))
      .max(32, t('firstNameMax')),
    lastName: string({ required_error: t('lastNameRequired') })
      .min(1, t('lastNameRequired'))
      .max(32, t('lastNameMax')),
    password: string({ required_error: t('passwordRequired') })
      .regex(passwordValidation, t('passwordInvalid'))
  });

// 导出Schema类型供业务代码使用
export type CreateUserInput = TypeOf<ReturnType<typeof createUserSchema>>;

步骤3:React 组件中使用示例

结合 React Hook Form(若项目使用),在组件内生成带国际化错误的 Schema 并使用:

import { useTranslations } from 'next-intl';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { createUserSchema, CreateUserInput } from '@/schemas/user';

export default function RegisterForm() {
  // 获取validation命名空间的翻译函数
  const t = useTranslations('validation');
  // 生成带国际化错误的Schema
  const userSchema = createUserSchema(t);

  const { register, handleSubmit, formState: { errors } } = useForm<CreateUserInput>({
    resolver: zodResolver(userSchema)
  });

  const onSubmit = (data: CreateUserInput) => {
    // 处理表单提交逻辑
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>名字</label>
        <input {...register('firstName')} />
        {errors.firstName && <span className="error">{errors.firstName.message}</span>}
      </div>
      <div>
        <label>姓氏</label>
        <input {...register('lastName')} />
        {errors.lastName && <span className="error">{errors.lastName.message}</span>}
      </div>
      <div>
        <label>密码</label>
        <input type="password" {...register('password')} />
        {errors.password && <span className="error">{errors.password.message}</span>}
      </div>
      <button type="submit">注册</button>
    </form>
  );
}

步骤4:服务端(API路由)使用示例

在API路由中初始化 next-intl 客户端后生成 Schema,完成校验:

import { NextRequest, NextResponse } from 'next/server';
import { createI18nClient } from 'next-intl';
import { createUserSchema } from '@/schemas/user';

export async function POST(request: NextRequest) {
  // 从请求头获取当前语言,默认用英文
  const locale = request.headers.get('accept-language')?.split(',')[0] || 'en';
  
  // 初始化next-intl客户端
  const i18n = createI18nClient({
    locale,
    messages: (await import(`@/locales/${locale}.json`)).default
  });
  
  const t = i18n.useTranslations('validation');
  const userSchema = createUserSchema(t);

  const body = await request.json();
  const validationResult = userSchema.safeParse(body);

  if (!validationResult.success) {
    // 返回格式化后的国际化错误信息
    return NextResponse.json(
      { errors: validationResult.error.format() },
      { status: 400 }
    );
  }

  // 处理用户创建逻辑
  return NextResponse.json({ success: true, data: validationResult.data });
}

注意事项

  • 不要在静态Schema中直接引用i18n实例,非React上下文(如服务端初始化阶段)可能无法获取正确的翻译上下文。
  • 统一将校验错误放在validation命名空间下,便于维护和扩展。
  • 若不使用React Hook Form,直接调用schema.parse()或schema.safeParse()也能得到国际化后的错误信息。

内容的提问来源于stack exchange,提问作者Alexandre Dissi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 10:15:36