如何用next-intl翻译Zod默认错误信息?求替代zod-i18n方案
用next-intl批量翻译Zod默认错误信息的方案
方案1:自定义错误映射表 + next-intl翻译函数(客户端组件场景)
这个方案通过提前在next-intl的消息文件中定义所有Zod默认错误的翻译,再通过工具函数批量匹配转换。
在next-intl消息文件中添加错误翻译
在对应语言的消息文件(如messages/en.json、messages/zh.json)中,为Zod的默认错误类型配置翻译:// messages/en.json { "zodErrors": { "invalid_type": "Expected {expected}, received {received}", "required": "This field is required", "invalid_string": { "email": "Invalid email address", "url": "Invalid URL", "default": "Invalid string format: {validation}" }, "too_small": { "string": "Must be at least {minimum} characters", "number": "Must be greater than or equal to {minimum}" } // 按需添加其他Zod默认错误类型的翻译 } }中文文件
messages/zh.json对应替换为中文翻译即可。编写批量翻译工具函数
利用next-intl的useTranslations钩子,将Zod抛出的ZodError对象批量转换为翻译后的错误信息:import { useTranslations } from 'next-intl'; import { ZodError, ZodIssue } from 'zod'; // 定义错误代码与翻译逻辑的映射 const zodErrorTranslators = { invalid_type: (issue: ZodIssue, t: any) => t('invalid_type', { expected: issue.expected, received: issue.received }), required: (_: ZodIssue, t: any) => t('required'), invalid_string: (issue: ZodIssue, t: any) => { const validationKey = issue.validation || 'default'; return t(`invalid_string.${validationKey}`, { validation: issue.validation }); }, too_small: (issue: ZodIssue, t: any) => { const typeKey = issue.type || 'default'; return t(`too_small.${typeKey}`, { minimum: issue.minimum }); } // 扩展其他错误类型的翻译逻辑 }; export const translateZodErrors = (zodError: ZodError) => { const t = useTranslations('zodErrors'); return zodError.issues.map(issue => { const translator = zodErrorTranslators[issue.code as keyof typeof zodErrorTranslators]; return translator ? translator(issue, t) : issue.message; }); };在组件中使用
当Zod校验失败时,调用工具函数获取翻译后的错误列表:import { useState } from 'react'; import { ZodError } from 'zod'; import { translateZodErrors } from '@/utils/translateZodErrors'; import { userSchema } from '@/schemas/userSchema'; export default function UserForm() { const [errors, setErrors] = useState<string[]>([]); const handleSubmit = (formData: FormData) => { try { userSchema.parse(Object.fromEntries(formData)); // 提交逻辑 } catch (err) { if (err instanceof ZodError) { setErrors(translateZodErrors(err)); } } }; return ( <form onSubmit={handleSubmit}> {/* 表单字段 */} {errors.length > 0 && ( <div className="error-list"> {errors.map((err, idx) => <p key={idx}>{err}</p>)} </div> )} <button type="submit">Submit</button> </form> ); }
方案2:服务端/无Hook场景的批量翻译
如果在服务端组件或API路由中处理Zod校验,可以使用next-intl的getTranslations方法实现批量翻译:
编写服务端翻译工具函数
import { getTranslations } from 'next-intl/server'; import { ZodError, ZodIssue } from 'zod'; const zodErrorTranslators = { invalid_type: (issue: ZodIssue, t: any) => t('invalid_type', { expected: issue.expected, received: issue.received }), required: (_: ZodIssue, t: any) => t('required'), invalid_string: (issue: ZodIssue, t: any) => { const validationKey = issue.validation || 'default'; return t(`invalid_string.${validationKey}`, { validation: issue.validation }); } }; export const translateZodErrorsServer = async (zodError: ZodError, locale: string) => { const t = await getTranslations({ locale, namespace: 'zodErrors' }); return zodError.issues.map(issue => { const translator = zodErrorTranslators[issue.code as keyof typeof zodErrorTranslators]; return translator ? translator(issue, t) : issue.message; }); };在API路由中使用
import { translateZodErrorsServer } from '@/utils/translateZodErrorsServer'; import { userSchema } from '@/schemas/userSchema'; export async function POST(request: Request) { const locale = request.headers.get('accept-language')?.split(',')[0] || 'en'; const data = await request.json(); try { userSchema.parse(data); return Response.json({ success: true }); } catch (err) { if (err instanceof ZodError) { const translatedErrors = await translateZodErrorsServer(err, locale); return Response.json({ errors: translatedErrors }, { status: 400 }); } return Response.json({ error: 'Unknown error' }, { status: 500 }); } }
内容的提问来源于stack exchange,提问作者OArnarsson
相关产品推荐
相关产品推荐

