lang::get()返回null或原key说明语言包未加载成功;须启用loadlangpack中间件、路径严格为app/lang/zh-cn.php、文件内容为扁平数组且无bom,lang::setlocale()必须在任何lang()调用前执行。

Lang::get() 返回 null 或原 key 字符串,基本可以断定语言包根本没加载成功——不是配置错,是压根没进加载流程。
确认 LoadLangPack 中间件是否启用
ThinkPHP6 的语言包加载完全依赖 think\middleware\LoadLangPack 中间件。它不注册,lang() 就永远只返回键名,不会报错也不会警告。
- 打开
app/middleware.php,检查全局中间件数组是否包含'think\middleware\LoadLangPack'(注意不是Lang类,也不是注释状态) - 多应用模式下,每个应用的
middleware.php都得单独加,不能只在根目录配 - 若用路由级中间件(如
->middleware(Lang::class)),必须确保该路由确实命中,且未被前置中间件(如 CORS、Auth)提前终止
检查语言包路径与文件命名是否严格合规
ThinkPHP6 只认一种格式:app/lang/zh-cn.php。任何偏差都会静默失败,连 warning 都不抛。
- 路径必须是
app/lang/{lang}.php(单应用)或app/{app_name}/lang/{lang}.php(多应用),不能是lang/、runtime/lang/或带子目录如app/lang/zh-cn/common.php - 文件名必须小写 + 短横线,
zh-cn.php✅,zh_CN.php❌,zh.php❌,ZH-CN.php❌(Linux 区分大小写) - 文件内容必须以
return [ 'key' => 'value' ];开头,不能有 BOM、空格、echo、var_dump或任何输出
验证 lang() 调用前语言环境是否已就绪
lang('welcome') 的结果取决于调用时刻的当前语言设置。如果控制器里才设语言,而模板或验证器已先执行,就会拿到 null 或默认英文。
-
Lang::setLocale('zh-cn')必须在任何lang()调用之前执行,最佳位置是中间件handle()开头 - 不要在控制器构造函数里调用
Lang::setLocale()—— 此时中间件尚未运行,Lang实例仍是初始状态 - 验证器静态方法
Validate::check()不触发自动语言加载,需手动Lang::load(app()->getAppPath() . 'lang/zh-cn/validate.php') - CLI 场景无自动侦测,必须显式
Lang::setLocale('zh-cn')并Lang::load()
排查 lang 参数是否触发了不安全加载导致静默中断
旧版 ThinkPHP(6.0.13 及更早)中,恶意 lang 值(如 ?lang=../../../etc/passwd)会令 Lang::load() 因路径校验失败而直接退出,后续 lang() 无语言包可用,自然返回 null 或 key 本身。
- 检查
config/lang.php中'switch' => true是否开启;若非必要,设为false彻底禁用动态切换 - 若需支持切换,务必限制白名单:
'allow_lang_list' => ['zh-cn', 'en-us'],并在中间件中手动校验$request->param('lang')是否在此列表 - 升级到 6.0.14+,框架已内置
realpath()校验,但前提是你的项目没重写过Lang::load()逻辑
最常被忽略的点:语言包文件存在且路径对,但里面用了 return ['zh-cn' => ['hello' => '你好']] 这种嵌套结构——ThinkPHP 要的是扁平数组,嵌套一层就等于没定义,lang('hello') 必然返回 null 或原 key。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











