ThinkPHP多语言文件必须用UTF-8无BOM编码,否则Lang::get()会因BOM导致解析失败而返回空字符串;语言变量须全大写加下划线,路径大小写敏感,且Lang::set()必须在Lang::get()前调用。

ThinkPHP 多语言文件必须用 UTF-8 无 BOM 编码,否则 Lang::get() 读取时会解析失败,返回空字符串或乱码——这是最常被忽略的硬性前提。
多语言文件编码必须为 UTF-8 无 BOM
即使编辑器显示“UTF-8”,也极可能带 BOM(\xEF\xBB\xBF)。BOM 会被 PHP 当作普通字符读入,导致 parse_ini_file() 或框架内置解析器跳过第一行、键名错位、值截断。
- VS Code:右下角点击编码 → “Save with Encoding” → 选
UTF-8(确认未勾选 “Include BOM”) - Sublime Text:File → Save with Encoding →
UTF-8(不是 “UTF-8 with BOM”) - Linux/macOS 批量修复:
find app/lang -name "*.php" -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;(仅清除已有 BOM) - 禁止用 Windows 记事本保存语言文件——它默认 ANSI,BOM 检测不可靠
语言变量命名必须全大写 + 下划线
ThinkPHP 的 Lang::get() 和模板 {:lang('USER_NAME')} 均依赖严格的大写命名。小写或驼峰会导致查不到,且不报错,静默失效。
- 正确:
USER_NAME、LOGIN_FAILED_TIP、ORDER_STATUS_PENDING - 错误:
userName、login_failed_tip、OrderStatusPending - 系统级语言变量以
_开头(如_VALIDATE_REQUIRED),自定义变量避免用此前缀,防止冲突 - 中文键名不推荐;若必须用,确保文件编码和运行时
default_charset一致,且模板中显式声明charset=utf-8
语言包目录结构与加载顺序要匹配配置
ThinkPHP 按 app/lang/{lang}/{module}.php 路径自动加载,路径大小写敏感,且模块名必须与控制器命名空间对齐。错一个字母或大小写,整个包就失效。
立即学习“PHP免费学习笔记(深入)”;
- 例如
app/lang/zh-cn/user.php对应app/controller/UserController.php中调用的lang('USER_LOGIN') - 全局语言包放
app/lang/zh-cn.php(无模块后缀),会被所有模块加载 - 切换语言时,
Lang::set('zh-cn')必须在任何Lang::get()调用之前执行,否则仍用默认语言 - 调试技巧:
dump(Lang::range())可查看当前已加载的所有语言变量键值对
模板中使用 {:lang()} 时要注意上下文编码
模板输出本身受响应头 Content-Type 控制。即使语言变量是 UTF-8,若响应头没声明 charset,浏览器可能用 ISO-8859-1 解析,中文变方块。
- 确保入口或中间件中已设置:
header('Content-Type: text/html; charset=utf-8'); - ThinkPHP 6+ 推荐在
config/app.php中设'default_charset' => 'utf-8' - 避免在语言变量值里拼接 HTML 标签——
lang()不做转义,直接输出,XSS 风险高;需 HTML 内容请改用htmlspecialchars(lang('...'), ENT_QUOTES, 'UTF-8') - JS 中使用语言变量?别直接
var tip = "{:lang('SAVE_SUCCESS')}";,应先 JSON 编码:var tip = {:json_encode(lang('SAVE_SUCCESS'))};
真正卡住人的从来不是语法,而是语言文件保存时那个看不见的 BOM,还有路径里一个不该大写的字母——它们不会报错,只会让 lang() 返回空,然后你花两小时翻文档、查缓存、重装语言包,最后发现是 Notepad++ 保存时多勾了一个选项。



















