如何让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
相关产品推荐
相关产品推荐

