中间件未执行的主因是缓存未清除,需运行php think clear:all和php think clear:template;其次检查LoadLangPack是否在SessionInit之后、路径格式是否为app/lang/zh-cn.php、文件是否以return []开头且无BOM。

中间件注册了但没真正执行
改完 app/middleware.php 后逻辑没变,大概率是中间件根本没进 pipeline。框架启动时会把中间件列表缓存到 runtime/container/ 下,哪怕你改了 PHP 文件,旧的中间件链仍可能从缓存里加载出来。
必须执行:php think clear:all(清空全部缓存)+ php think clear:template(特别清模板缓存,它常偷偷固化中间件顺序)。
验证是否真执行了:在中间件的 handle() 方法开头加一行 file_put_contents(runtime/log/mw_debug.log, 'LoadLangPack run', FILE_APPEND | LOCK_EX);,访问页面后检查日志有没有写入——没写入就说明中间件压根没跑。
中间件位置放错了导致被跳过
ThinkPHP 的中间件是洋葱模型,顺序决定执行时机。比如 think\middleware\LoadLangPack 默认优先级是 10,如果你把它放在 SessionInit 之前,语言包加载时 session 还没初始化,$request->cookie('think_lang') 就读不到;如果放在路由之后、控制器之前,那 $request->lang() 在中间件里还是 null。
立即学习“PHP免费学习笔记(深入)”;
- 确保
think\middleware\LoadLangPack在app/middleware.php全局数组中,且排在think\middleware\SessionInit之后 - 别只在某个路由里加中间件——全站多语言必须全局注册,否则控制器里
Lang::get()可能提前执行,语言包还没加载 - 自定义语言切换中间件(如
LangSwitchMiddleware)要设更低优先级(比如protected $priority = 5;),确保它在LoadLangPack前拿到原始请求参数
中间件里调用了 lang() 却返回键名
这不是中间件没生效,而是语言包压根没加载成功。ThinkPHP6 查语言包路径极其严格,错一个字符就静默失败,不报错也不 fallback。
- 路径必须是
app/lang/zh-cn.php,不能是app/lang/zh_CN.php、app/lang/zh-cn/common.php或lang/zh-cn.php -
config/lang.php中的default_lang和allow_lang_list必须小写短横线格式,比如'zh-cn',不能是'zh'或'ZH-CN' - 语言包文件必须以
return [];开头,前面不能有 BOM、空行、echo、var_dump等任何输出 - 多应用模式下路径是
app/应用名/lang/zh-cn.php,不是app/lang/应用名/zh-cn.php
中间件里 setLocale() 没触发重载
Lang::setLocale('en-us') 只改当前请求的语言标识,不会自动重载语言包。如果 LoadLangPack 已经执行完毕,再调这个方法,lang('hello') 依然读的是旧语言包内容。
正确做法是:在中间件 handle() 开头立即调用 Lang::setLang($lang)(注意是 setLang,不是 setLocale),并确保该中间件在 LoadLangPack 之前执行;或者手动触发重载:Lang::load(app()->getAppPath() . 'lang/' . $lang . '.php', $lang);。
更稳妥的方式是彻底绕过中间件顺序依赖:把语言解析逻辑收口到一个靠前的中间件里,在 $next($request) 前完成 Lang::setLang() + Lang::load(),后面所有 lang() 调用才真正受控。



















