ThinkPHP子域名语言映射需在全局中间件最早阶段用parse_url提取域名,查配置数组映射为语言码(如en.site.com→en-us),调用Lang::setLang();必须关闭allow_url_lang或重写Detect类,语言包路径须全小写(如lang/en-us/),且session共享不自动同步语言。

子域名怎么映射到对应语言包
ThinkPHP 不会自动把 en.site.com 解析成 en-us 语言,必须手动在请求最早阶段完成映射。关键不是“路由”,而是“语言加载时机”——Lang 类一旦初始化,后续再改 Lang::setLang() 就只影响新加载的语言项,验证器提示、系统默认文案等早已固化。
推荐做法是在全局中间件(如 app\common\middleware\Lang.php)的 handle() 开头就解析域名:
- 用
parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST)提取纯净域名,避免端口干扰(如localhost:8000) - 查配置数组,例如
['zh.site.com' => 'zh-cn', 'en.site.com' => 'en-us'],注意键名必须全小写、带点号 - 调用
Lang::setLang($lang),且确保它在任何lang()调用前执行
为什么 lang=xx 参数和子域名会冲突
默认开启的 URL 语言参数(allow_url_lang => true)优先级高于域名判断,导致访问 en.site.com?lang=zh-cn 时仍切回中文,而你本意是靠域名定语言。
解决方法只有两个:
立即学习“PHP免费学习笔记(深入)”;
- 直接关闭:在
config/app.php中设'allow_url_lang' => false,彻底禁用 GET 参数干预 - 若必须保留 URL 切换能力,需重写
think\lang\Detect类,在detect()方法里把域名匹配逻辑提到 GET 参数之前
不关掉的话,子域名切换永远不稳定,尤其用户从分享链接带参进来时会意外覆盖。
lang/ 目录结构和命名必须严格小写
语言包路径必须是 lang/en-us/common.php,不能是 lang/en_US/common.php 或 lang/EN-US/common.php。Linux 服务器区分大小写,错一个字母就加载失败,且 ThinkPHP 默认静默忽略——lang('hello') 会原样返回字符串 'hello',而不是报错。
常见踩坑点:
- Windows 下开发测试正常,上线 Linux 后多语言失效(因目录名写成
EN-US) - 语言包文件用了
return ['hello' => 'Hello'];但漏了return关键字,PHP 解析失败,整个文件被跳过 - 想用分组文件(如
user.php),却没在lang()中显式指定分组:lang('login', [], 'user')
子域名间 session 共享不等于语言自动同步
即使你已配好 session.cookie_domain => '.site.com' 让登录态跨域共享,语言选择仍要单独处理。Session 里存的 lang 值(比如 $request->session('lang'))只是用户偏好,不是当前请求语言。
真正起效的是每次请求开始时的 Lang::setLang() 调用。所以即使用户在 zh.site.com 设置了语言为英文,跳转到 en.site.com 时,仍要靠域名映射逻辑主动设一次 en-us,不能依赖 Session 读取后手动 set —— 因为中间件执行早于控制器,Session 还没初始化。
最稳妥的路径:域名 → 映射语言码 → Lang::setLang() → 加载对应 lang/{lang}/ 下全部文件。别省这一步,也别指望框架自动猜。



















