ThinkPHP多语言必须严格遵循lang/zh-cn/common.php小写路径,Lang::setLang()需在中间件handle开头调用,且cookie键名必须为think_lang,allow_url_lang建议设为false以防URL参数覆盖。

lang/目录结构必须小写且路径固定
ThinkPHP只认 lang/zh-cn/common.php 这种格式,大小写错一个字母(比如 lang/ZH-CN/common.php 或 lang/zh_CN/common.php)就会静默失败——不报错、不提示、也不加载语言项。
常见错误现象:lang('hello') 始终返回英文或空字符串,但检查文件存在、内容也正确。
- Linux 服务器上尤其敏感,
zh-cn≠ZH-CN≠zh_CN - 语言包必须放在项目根目录下的
lang/子目录里,不能放在config/lang.php或application/lang/下(除非你明确启用了模块语言包自动加载) - 每个语言子目录下至少要有
common.php,内容必须是return ['key' => 'value'];,不能是 JSON、YAML 或带 BOM 的 UTF-8 文件
Lang::setLang() 必须在请求最开始调用
调用 Lang::setLang('en-us') 却没生效?大概率是执行太晚了。验证器、系统提示、模板渲染等模块在中间件之后就已触发语言加载,此时再设语言只影响后续新调用的 lang(),对已载入内容无效。
正确做法是在中间件的 handle() 方法开头就设置:
立即学习“PHP免费学习笔记(深入)”;
public function handle($request, \Closure $next)
{
$lang = $request->cookie('think_lang', 'zh-cn');
\think\Lang::setLang($lang);
return $next($request);
}
- 不要在控制器构造函数或方法里调用
Lang::setLang() - 确保
think\middleware\Lang已注册到app/middleware.php全局中间件栈中 - 如果用了 Session,记得在
SessionInit中间件之后执行该语言中间件,否则读不到 Cookie
Cookie 键名和路径必须严格匹配
ThinkPHP 默认只识别名为 think_lang 的 Cookie,不是 lang、language 或 think-lang。前端用 document.cookie = "lang=zh-cn" 写的,后端根本不会读取。
正确写法只有两种:
- 后端写:
cookie('think_lang', 'en-us') - 前端写(需保证域名和路径一致):
document.cookie = "think_lang=en-us; path=/; domain=.example.com";
注意:Lang::setLang() 不会自动写 Cookie,它只改当前请求上下文;要持久化切换,必须手动调 cookie()。
URL 参数 lang=xxx 与 Cookie 冲突时优先级问题
默认开启 allow_url_lang 时,?lang=en-us 会覆盖 Cookie 设置的语言,导致用户切完语言又点个链接就回退到 URL 指定语言。
如果你依赖 Cookie 或 Session 控制语言,建议直接关闭 URL 参数识别:
'allow_url_lang' => false
放在 config/app.php 中。否则就得重写 Lang::detect() 逻辑,让 Cookie 优先于 GET 参数——但这容易引入边界问题,比如调试时 URL 临时切语言反而失效。
多语言真正的复杂点不在配置,而在“谁先读、谁后设、谁已加载”。一次请求中语言上下文只能设一次,且必须在任何 lang()、验证规则、模板输出之前完成。漏掉这个前提,后面所有操作都是补救,不是正解。



















