如何在NestJS+Next.js中实现后端错误(含校验信息)多语言前端展示?
前端本地化处理后端错误信息方案(基于next-i18next)
核心思路
由于后端不改动错误逻辑,前端需将后端返回的标准化错误(尤其是class-validator生成的校验错误)映射到前端翻译key,通过next-i18next的翻译能力渲染多语言文本,同时覆盖自定义后端错误。
具体实现步骤
1. 明确后端错误结构
先梳理后端返回的错误格式,class-validator的校验错误通常为数组结构,示例如下:
{ "statusCode": 400, "message": [ { "property": "username", "constraints": { "isString": "username must be a string", "isEmpty": "username should not be empty", "minLength": "username must be longer than or equal to 10 characters" } } ], "error": "Bad Request" }
2. 配置前端翻译文件
在public/locales下的多语言文件夹(如zh、en)中创建errors.json,存放错误翻译内容:
英文(en/errors.json)
{ "validation": { "username": { "isString": "Username must be a string", "isEmpty": "Username cannot be empty", "minLength": "Username must be at least 10 characters long" }, "common": { "badRequest": "Invalid request", "serverError": "Server error occurred" } }, "customErrors": { "userNotFound": "User not found" } }
中文(zh/errors.json)
{ "validation": { "username": { "isString": "用户名必须为字符串", "isEmpty": "用户名不能为空", "minLength": "用户名长度不能少于10个字符" }, "common": { "badRequest": "请求无效", "serverError": "服务器发生错误" } }, "customErrors": { "userNotFound": "用户不存在" } }
3. 编写错误映射与翻译工具
创建utils/errorTranslator.ts,实现后端错误到前端翻译key的映射:
import { useTranslation } from 'next-i18next'; // 映射class-validator错误文本到翻译key const validatorErrorMap: Record<string, string> = { 'username must be a string': 'validation.username.isString', 'username should not be empty': 'validation.username.isEmpty', 'username must be longer than or equal to 10 characters': 'validation.username.minLength', // 新增其他校验错误映射 }; // 通用错误文本映射 const commonErrorMap: Record<string, string> = { 'Bad Request': 'validation.common.badRequest', }; export const translateError = (error: any) => { const { t } = useTranslation('errors'); // 处理class-validator的数组型校验错误 if (Array.isArray(error.message)) { return error.message.map(err => { return Object.values(err.constraints).map(msg => { return validatorErrorMap[msg] ? t(validatorErrorMap[msg]) : msg; }).join(', '); }).join('; '); } // 处理单个错误文本 if (typeof error.message === 'string') { if (validatorErrorMap[error.message]) return t(validatorErrorMap[error.message]); if (commonErrorMap[error.message]) return t(commonErrorMap[error.message]); // 自定义错误匹配 if (error.message.includes('User not found')) return t('customErrors.userNotFound'); // 兜底返回原文本 return error.message; } // 未知错误兜底 return t('validation.common.serverError'); };
4. 组件中使用翻译工具
在表单或API请求的错误处理逻辑中调用翻译函数:
import { useState, FormEvent } from 'react'; import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { translateError } from '../utils/errorTranslator'; export default function LoginPage() { const { t } = useTranslation('errors'); const [errorMsg, setErrorMsg] = useState(''); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); try { const res = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ username: 'test' }) }); if (!res.ok) { const error = await res.json(); setErrorMsg(translateError(error)); } } catch (err) { setErrorMsg(t('validation.common.serverError')); } }; return ( <div className="p-4"> {errorMsg && <p className="text-red-500 mb-4">{errorMsg}</p>} <form onSubmit={handleSubmit}> <input type="text" placeholder={t('validation.fields.username')} className="border p-2 mb-2" /> <button type="submit" className="bg-blue-500 text-white p-2">Login</button> </form> </div> ); } export async function getStaticProps({ locale }: { locale: string }) { return { props: { ...await serverSideTranslations(locale, ['errors']), }, }; }
优化建议
- 动态字段映射:将字段名也加入翻译(如
validation.fields.username对应“用户名”),通过正则匹配后端错误中的字段名,动态生成翻译key,减少硬编码。 - 全局错误拦截:在axios等请求库的拦截器中统一处理错误翻译,避免每个组件重复编写错误逻辑。
- 批量生成映射表:根据class-validator的默认错误模板(如
{{property}} must be a string),批量生成错误文本与翻译key的映射关系,提升扩展性。
内容的提问来源于stack exchange,提问作者lCode
相关产品推荐
相关产品推荐

