go-playground/validator 默认不支持中文提示因其内置错误信息为英文,需手动注入中文翻译器(ut.translator)并绑定到 validator 实例,且必须在校验前完成注册与设置,否则 translate() 无效。

为什么 go-playground/validator 默认不支持中文提示
因为它的内置错误信息全是英文,ValidationError.Translate() 依赖的是英文 tag 值(如 required、min=6)去查翻译映射表,没配中文翻译器就永远吐英文。你直接打 err.Error() 看到的 "Key: 'User.Password' Error:Field validation for 'Password' failed on the 'min' tag" 就是典型表现。
要让它吐中文,核心不是改 struct tag,而是替换或注入翻译器(ut.Translator),且必须在 validator 实例初始化后、校验前完成绑定。
- 别在
validate.Struct()之后才调Translate()—— 此时 error 已固化,再译无效 - 别用全局
ut.New(...)后忘记调RegisterFallback()—— 中文键缺失时会 fallback 到英文原值,看着像“部分中文”,其实是漏配 - struct tag 里不用写中文,保持
json:"password" validate:"required,min=6"即可,翻译逻辑和 tag 无关
如何注册中文翻译器并绑定到 Gin 的 validator 实例
Gin 内置的 gin.DefaultValidator 是个 *validator.Validate 实例,但默认没挂翻译器。你需要手动注入一个带中文映射的 ut.Translator,再通过 SetLabelNameFunc 或自定义 RegisterTranslation 控制字段名显示(比如把 Password 变成 密码)。
推荐做法:在 Gin 初始化后、启动前完成 translator 注册,并用中间件把翻译器透传到上下文,避免每次校验都重复查找。
- 用
ut.New(zh.New(), zh.New())创建支持中文的UniversalTranslator - 调
trans.RegisterTranslation("required", ..., func(ut ut.Translator, fe validator.FieldError) string {...})逐条注册规则中文文案 - 用
v.RegisterTagNameFunc(func(fld reflect.StructField) string { return fld.Tag.Get("zh") })支持按zh:"密码"tag 提取字段名(非必需,但比硬编码更可控) - 别漏掉
v.RegisterValidation("mobile", mobileValidator)这类自定义规则的中文翻译注册
Gin 请求校验失败时如何返回中文错误信息
不能依赖 c.Error(err) 或直接 err.Error(),得先用 err.(validator.ValidationErrors) 类型断言,再调 .Translate(trans)。Gin 的 c.ShouldBind 不自动做这事,必须自己包一层。
最简实践是写个封装函数:BindAndValidate(c *gin.Context, obj interface{}, trans ut.Translator) bool,内部调 c.ShouldBind(obj) + 错误类型判断 + 翻译 + c.AbortWithStatusJSON(400, ...)。
- 若用
c.ShouldBindJSON()失败,err可能是json.UnmarshalTypeError等非 validator 错误,需先errors.As(err, &ve)判断是否为validator.ValidationErrors - 字段名翻译建议用
fe.Field()+trans.Tr("field."+fe.Field())查表,而不是直接fe.Translate(trans)—— 后者对嵌套结构(如User.Profile.Age)容易崩 - HTTP 响应体建议统一为
{"code": 400, "msg": "参数错误", "errors": [{"field": "password", "msg": "密码长度不能少于6位"}]},前端好解析
常见中文提示失效的三个隐藏坑
90% 的“中文没出来”问题其实卡在这三处,和 validator 版本或 Gin 配置无关。
-
Validate实例被复用但trans没设进它 ——v := validator.New()后忘了v.SetTranslator(trans),导致Translate()调用静默失败 - 中文翻译 key 写错,比如注册了
"reqired"(少个 u)却在 tag 里写validate:"required",结果该字段永远显示英文 - Gin 的
c.Bind()和c.ShouldBind()行为不同:Bind会自动 abort 并返回 400,但不给你机会插翻译逻辑;必须用ShouldBind手动捕获 err
真正麻烦的不是加翻译,而是验证器、翻译器、字段名提取、错误组装这四层耦合在一起时,任意一层断链都会让中文消失。建议把 translator 和 validator 实例一起放进 app.Config 全局变量,所有校验路径走同一套实例。











