Lang中间件不生效主因是未注册到全局中间件栈,需在app/middleware.php中显式添加\think\middleware\Lang::class,并确保路径、locale格式(如zh-cn)、加载时机等全链路正确。

Lang中间件不生效,90%是因为没注册到全局中间件栈,不是配置写错了,而是根本没加载。
Lang中间件必须显式注册到 app/middleware.php
ThinkPHP 不会自动启用 Lang 或 LoadLangPack 中间件,哪怕你配好了 lang.php、放对了语言包路径,中间件没注册就等于没写。它不会报错,Lang::get() 只会静默返回键名或空字符串,排查起来非常隐蔽。
- 打开
app/middleware.php,确认它存在且返回一个数组(不能是null、不能有echo) - 把
\think\middleware\Lang::class或\think\middleware\LoadLangPack::class加进返回数组里(V6.0.3+ 推荐用Lang,功能更全) - 多应用模式下,得去对应应用目录(如
app/index/middleware.php)单独配,全局文件不生效 - 中间件顺序有影响:它应排在
SessionInit之后、路由调度之前,否则读不到 Cookie 或input()数据
语言包路径和命名必须严格匹配短横线格式
ThinkPHP 按固定规则查找语言包:app/lang/{lang}/xx.php,其中 {lang} 必须是小写、短横线分隔、无下划线的 locale 标识,比如 zh-cn,不是 zh_CN、zhcn 或 ZH-CN。系统内部会强制转成小写连字符,但路径本身必须手动写对。
- 路径必须是
app/lang/zh-cn/common.php,不能是app/lang/zh_CN/common.php或app/lang/zh-cn.php - 文件名必须小写,如
zh-cn.php、en-us.php;return ['hello' => '你好'];,不要嵌套['zh-cn' => [...]] - 常见错误现象:
Language file not exists或Lang::get('hello')始终返回hello - 第三方扩展的语言包不会被自动扫描,需手动调用
Lang::load($absPath)
切换语言不能只靠 GET 参数,得用 Lang::setLocale() + 白名单校验
默认情况下,Lang 中间件只从 Accept-Language 请求头、Cookie 或配置项读取语言,它不解析 $_GET['lang']。想通过链接参数(如 ?lang=ja-jp)切换,必须自己取值、校验、设 locale。
立即学习“PHP免费学习笔记(深入)”;
- 在中间件或控制器中调用
Lang::setLocale(input('lang')),但务必先校验是否在allow_lang_list白名单内 - 设完后建议显式调一次
Lang::load(),避免因加载时机问题漏载 -
Lang::setLocale()不会自动写 Cookie,要持久化得配合cookie('think_lang', $lang)(注意键名必须是think_lang) - 别在控制器构造函数里调
Lang::detect()——中间件已做这事,重复调可能覆盖检测结果
Lang::get() 返回空或键名?重点查加载时机和作用域
最常被忽略的是「模块级语言包」未启用,或中间件执行太晚,导致控制器里调用 Lang::get() 时语言包还没加载。另外,Lang::get('user.name') 要求语言包里是嵌套数组 ['user' => ['name' => '用户名']],平铺写法 'user_name' => '用户名' 会失效。
- 调试优先用
Lang::range()查看当前已加载的所有键值,比猜更直接 - 确保
lang_switch_on配置为true,default_lang是合法值(如'zh-cn') - 如果用了路由分组(如
lang/:lang),记得把:lang值传给Lang::setLocale(),别只存不设 - 动态数据字段(如数据库里的
title_zh/title_en)不属于语言包范畴,得在模型 accessor 里按Lang::getLangSet()取值
真正麻烦的点不在怎么写,而在于所有环节都得对上:中间件注册了、路径写对了、locale 格式合规、切换逻辑在正确时机调用、白名单锁死了非法输入——漏掉任意一环,Lang::get() 就会安静地失效。



















