ThinkPHP多语言常量必须通过lang()、L()或{:lang('key')}调用,不可直接使用;lang()为TP6+推荐函数,L()兼容TP3.2;键名严格匹配语言文件且默认区分大小写;语言包按层级路径加载,路径错误或BOM头会导致静默失败;模板中需用{:lang('key')}安全输出。

ThinkPHP 多语言常量不能直接当 PHP 常量用,必须走 lang()、L() 或模板里的 {:lang('key')},否则输出为空或报错。
lang() 和 L() 函数的区别与选择
两者功能完全一致,L() 是旧版 ThinkPHP(如 3.2)的写法,lang() 是 TP6+ 的推荐助手函数。它们都从当前激活的语言包中取值,不区分大小写,但键名必须严格匹配语言文件中的 key。
- TP6 中优先用
lang('welcome'),更语义化,且与框架其他助手函数风格统一 - TP3.2 项目里仍常见
L('WELCOME_MSG'),注意它不自动转小写,L('welcome_msg')可能取不到值 - 如果语言包里定义的是
'user_login' => '请登录',调用时必须写lang('user_login'),不能写lang('USER_LOGIN')(除非你开了大小写不敏感模式,但默认不开) - 在模型验证规则里用多语言,得写成
['username', 'require', lang('username_required'), 1],而不是字符串字面量
语言包路径和加载顺序容易出错
ThinkPHP 按固定顺序加载语言包,漏掉某一层或路径写错,就会静默 fallback 到默认语言或空值,很难排查。
- 框架层:thinkphp/lang/
zh-cn.php - 应用层:app/lang/
zh-cn.php - 模块层:app/
index/lang/zh-cn.php(模块名小写) - 控制器层:app/index/lang/
zh-cn/user.php(控制器名小写) - 路径中任何一级目录名写错(比如把
zh-cn写成zh_CN或zhcn),该层语言包就完全不会加载 - Windows 下注意大小写不敏感,Linux 下严格区分——开发在 Windows 测试通过,上线 Linux 报空,大概率是路径大小写问题
URL 切换语言后页面没变?检查这几个点
加 ?lang=en-us 不生效,不是配置没开,而是被缓存、Cookie 或行为顺序卡住了。
立即学习“PHP免费学习笔记(深入)”;
- 确认
lang_switch_on设为true,且lang_list包含'en-us'(不是'en') - 检查是否启用了
CheckLangBehavior行为:TP3.2 要在tags.php的app_begin里加,TP6 不需要手动配,但需确保没有禁用内置行为 - 浏览器已有旧语言 Cookie,会覆盖 URL 参数;可临时加
setcookie('think_language', '', time()-3600)清除再试 - TP6 中
lang_detect_var默认是'lang',但如果你改过这个配置项(比如设成'l'),URL 就得用?l=en-us,否则无效 - 开启调试模式后,在日志里搜
Lang::detect,能看到实际检测到的语言和 fallback 过程
模板里怎么安全输出多语言文本
模板引擎不执行 PHP 函数,所以不能写 <?php echo lang('xxx'); ?>,也不能用三元表达式套常量。
- 正确写法是
{:lang('submit_btn')}或{__('submit_btn')}(TP6 支持) - 不要试图在模板里做判断,比如
{:lang(APP_DEBUG ? 'dev_mode' : 'prod_mode')}—— 模板不解析 PHP 表达式,这行会原样输出或报错 - 如果某个文本要带变量插值(如“欢迎
{$name}”),语言包里定义为'welcome_user' => '欢迎 %s',模板里写{:sprintf(lang('welcome_user'), $name)};TP6 也支持{:lang('welcome_user', [$name])} - 注意 BOM 头:语言文件保存为 UTF-8 无 BOM,否则可能触发 headers already sent 错误
最常被忽略的是语言包文件的返回值必须是数组,且不能有任何输出(包括空格、换行、BOM),哪怕一个空行都会让整个语言包加载失败,而框架通常不报错,只默默用默认语言兜底。



















