验证码错误提示不自动多语言,必须将验证规则消息设为'{%captcha_error}'模板,并确保lang/zh-cn/validate.php中定义'captcha_error'=>'验证码错误',且验证器在语言环境就绪后实例化。

验证码错误提示本身不自动多语言,必须手动映射
ThinkPHP 的 captcha 验证规则(如 'captcha'=>'require|captcha')触发的默认错误信息是硬编码英文字符串,比如 'captcha' => 'captcha error.',它**不会自动读取语言包**。你看到的中文提示,大概率是框架某处覆盖了默认值,或你项目里已做了手动处理——但这是“结果”,不是“机制”。
真正可控的方式,是把错误消息替换成语言变量,再由 lang() 解析:
-
'captcha' => '{%captcha_error}'是标准写法,{%xxx}会被lang('xxx')替换 - 对应语言包(如
lang/zh-cn/validate.php)里必须有'captcha_error' => '验证码错误' - 不能写成
'captcha' => lang('captcha_error')—— 验证器初始化时语言环境可能未就绪,会取到空或默认值
验证器必须在语言设置后初始化
如果你在中间件里用 Lang::setLang($lang) 设置语言,但验证器对象在控制器方法里才 new 出来,那没问题;但若你在构造函数、静态属性或服务提供者中提前创建了验证器实例,它会固化使用初始化那一刻的语言状态,后续切换无效。
典型踩坑场景:
立即学习“PHP免费学习笔记(深入)”;
- 定义了一个全局验证器类,
protected $msg = ['captcha' => '{%captcha_error}'],但该类被容器提前加载,此时Lang还没设语言 - 用了
Validate::rule(...)->message(...)静态链式调用,而message()内部没触发lang(),只是存字符串
安全做法:所有验证器实例都延迟到请求上下文就绪后创建,且 $msg 数组只存带 {%key} 的模板,不提前解析。
lang/ 目录结构和文件名必须严格匹配
验证码相关语言项应放在 lang/zh-cn/validate.php(不是 common.php),因为 ThinkPHP 验证器默认加载的是 validate 分组。如果放错位置,{%captcha_error} 就永远解析失败,静默回退为原字符串 captcha_error。
注意几个硬性要求:
- 目录名必须小写,
zh-cn✅,ZH-CN❌(Linux 服务器上直接失效) - 文件必须是
.php,返回数组,不能是 JSON 或 YAML -
validate.php里不能漏掉return [...],也不能多输出任何字符(包括 BOM、空行) - 键名大小写敏感:
'captcha_error'和'Captcha_Error'是两个不同 key
调试时最容易忽略的静默失败点
当 lang('captcha_error') 返回 'captcha_error' 本身,而不是翻译后的中文,90% 是以下之一:
- 当前语言包没加载:检查
Lang::getLangSet()返回值是否是你预期的zh-cn,不是en-us或空 - 语言包路径不对:运行
Lang::load(app()->getAppPath() . 'lang/zh-cn/validate.php')手动加载试试,看是否报错 - 键名拼写不一致:验证器里写
{%captcha_err},但语言包里是captcha_error,差一个字母也不会匹配 - CLI 环境没设语言:命令行跑单元测试时,
Lang::setLang('zh-cn')必须显式调用,否则 fallback 到框架默认en-us
线上环境尤其危险——它不报错,只默默返回原始 key,用户看到的就是一串英文或乱码,而日志里没有任何异常提示。



















