必须将语言码置于url路径最前端,php启动时用正则preg_match('@^/([a-z]{2}|zh-cn|en-us|ja-jp)(?=/|$)@i', $_server['request_uri'], $m)安全提取,避免parse_url()和explode()导致的错位。

多语言路由必须把语言码放在 URL 路径最前端,且 PHP 启动时就要提取,不能靠 $_GET['lang'] 或路由解析后才判断——否则所有语言共用同一 URL,SEO 直接判重复内容。
怎么安全提取语言前缀
别用 parse_url() + explode('/', ...),遇到双斜杠、URL 编码或空格会错位。正则才是唯一靠谱方式:
if (preg_match('@^/([a-z]{2}|zh-CN|en-US|ja-JP)(?=/|$)@i', $_SERVER['REQUEST_URI'], $m)) {
$lang = strtolower($m[1]);
} else {
$lang = config('lang.default_lang', 'zh');
}
- 必须用
@作分隔符,避免斜杠转义混乱 -
(?=/|$)确保只匹配完整路径段,防止https://www.php.cn/link/05d723194b85deefdb219a577ea1f6bftry被误认成en - 区域变体(如
zh-TW)要加进正则,但 Google 对区域码识别有限,主推zh、en这类两字母码
ThinkPHP 5+ 路由怎么配多语言前缀
不能只写 Route::get('zh/hello', ...) 这种静态规则——它不支持自动 fallback,也不处理带参数的动态路径。正确做法是用变量路由 + 闭包提取:
use think\facade\Route;
Route::rule(':lang/[:controller/:action]', function ($lang, $controller = 'index', $action = 'index') {
// 提前加载对应语言包
\think\Lang::load(APP_PATH . 'common/lang/' . $lang . '.php');
// 设置当前语言上下文(供模板和逻辑使用)
\think\App::instance()->lang = $lang;
return \think\facade\Route::dispatch($controller . '/' . $action);
})->pattern(['lang' => '[a-z]{2}|zh-CN|en-US']);
-
:lang必须是第一个变量,且pattern要严格限制合法值,防注入 - 不要在闭包里调
$this->fetch(),TP5 的dispatch已接管控制器执行流程 - 如果用模块分组(如
admin),需额外加Route::group('admin', [...])并重复配置:lang前缀
生成多语言跳转链接时容易踩的坑
手动拼 "@#@#@#@#@#@#@#@#@#@0" 是高危操作:当前 URL 是 /zh/news/2,硬拼会变成 https://www.php.cn/link/05d723194b85deefdb219a577ea1f6bf/zh/news/2。必须先剥离原始语言段:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
function lang_url($target_lang, $path = null) {
$current_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$segments = array_filter(explode('/', trim($current_path, '/')));
if (!empty($segments) && in_array(strtolower($segments[0]), ['zh', 'en', 'ja'])) {
array_shift($segments);
}
$actual_path = $path ?? implode('/', $segments);
return '/' . $target_lang . '/' . ltrim($actual_path, '/');
}
- 这个函数必须在视图层或公共函数库中全局可用,不能只写在某个控制器里
-
hreflang标签必须同步输出,例如:<link rel="alternate" hreflang="en" href="<?php%20echo%20lang_url('en');%20?>"> - 分页链接也要走这个函数,比如第 3 页英文版:
lang_url('en', 'news/3')→https://www.php.cn/link/05d723194b85deefdb219a577ea1f6bf/news/3
Apache/Nginx 重写规则必须提前捕获语言段
Web 服务器层不识别 TP 的路由规则。如果没在 Apache 或 Nginx 配置里显式放行 :lang 段,请求根本到不了 PHP —— 会直接 404 或 500。
Apache(.htaccess):
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-z]{2}|zh-CN|en-US)/(.*)$ index.php/$2?lang=$1 [QSA,L]
Nginx(server 块内):
location ~ ^/(zh|en|ja|zh-CN|en-US)/(.*)$ {
set $lang $1;
set $path $2;
try_files $uri $uri/ /index.php?lang=$lang&path=$path&$query_string;
}
- Apache 的
[QSA]和 Nginx 的&$query_string必须带上,否则原有查询参数丢失 - TP5 默认不读
$_GET['lang'],所以你得在app\common.php或基础控制器_initialize()里主动取:$lang = input('lang', config('lang.default_lang')); - 注意:Nginx 的
set指令在某些旧版本不支持,若报错请改用rewrite指令并确保nginx -V输出含http_rewrite_module
最常被忽略的一点:语言切换链接必须同时更新 hreflang 标签和当前页面的 rel="canonical",否则 Google 会认为你在用同一 URL 做多语言欺骗。这两者不是可选项,是多语言 SEO 的硬性前提。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










