yii中需在urlmanager.rules中显式配置带语言参数的路由规则,如'//post/' => 'post/view',并按顺序排列;控制器中通过beforeaction统一设置语言并存入session;生成url时须显式传入lang参数或封装lurl()函数;布局中动态输出hreflang和canonical标签。

URL 中的 language 参数怎么塞进路由规则
Yii 默认不自动把语言代码塞进 URL,得靠 urlManager 的 rules 显式声明。常见错误是只配了 'enablePrettyUrl' => true 就以为万事大吉,结果所有 URL 还是 /post/123 这种无语言前缀的格式。
正确做法是在 config/web.php 的 urlManager.rules 里加带语言变量的 pattern,比如:
'/<lang:>/post/<id:>' => 'post/view', '/<lang:>/about' => 'site/about', '' => 'site/index', // 默认语言入口,不带 lang </lang:></id:></lang:>
注意三点:
-
<lang:></lang:>是正则捕获组,匹配两位语言码(如zh、en),不能写成<lang></lang>这种裸占位符 - 规则顺序很重要:带
<lang></lang>的必须放前面,否则/en/about会先被'about' => 'site/about'吃掉,lang参数根本收不到 - 默认语言(比如中文)的首页要单独留一条空规则,否则
/会 404
如何让控制器自动识别并切换语言
路由解析出 lang 参数后,得手动把它喂给 Yii 的 i18n 组件,否则 Yii::t() 还是用默认语言。不能只靠 Yii::$app->language = $lang 简单赋值——这只能改当前请求的语言,但 session、cookie、重定向等后续动作会断掉上下文。
推荐在 SiteController 或基类 Controller 的 beforeAction() 里统一处理:
public function beforeAction($action)
{
$lang = $this->routeParams['lang'] ?? null;
if ($lang && in_array($lang, ['zh', 'en', 'ja'])) {
Yii::$app->language = $lang;
Yii::$app->session->set('language', $lang);
} else {
Yii::$app->language = Yii::$app->session->get('language', 'zh');
}
return parent::beforeAction($action);
}
关键点:
- 别直接读
$_GET['lang'],要用$this->routeParms(来自 URL 解析结果),避免和查询参数冲突 - 语言白名单必须做,否则攻击者能传任意字符串污染
messages/目录路径 - session 存语言是为了后续跳转或刷新时保持一致,光设
Yii::$app->language不够
生成带语言前缀的 URL 为什么总漏掉 lang 参数
Url::to() 默认不会自动补 lang,哪怕当前请求里有这个参数。这是最常踩的坑:页面上点了 Url::to(['site/about']),结果跳到 /about 而不是 /en/about,SEO 友好性直接归零。
解决方法只有两个:
- 每次调用都显式传
lang:Url::to(['site/about', 'lang' => Yii::$app->language]) - 封装一个全局函数,比如
lurl(),内部自动注入当前语言:return Url::to(array_merge($params, ['lang' => Yii::$app->language]));
额外提醒:
-
Url::current()会保留当前 URL 的所有参数,包括lang,适合分页、筛选等场景 - 如果用了
urlManager.suffix(比如'.html'),确保lang规则也带上后缀,否则/en/about.html匹配失败 - Gii 生成的 CRUD 链接默认不含
lang,必须手动改视图里的Url::to()调用
hreflang 标签和 canonical 怎么动态输出
搜索引擎靠 <link rel="alternate" hreflang="x"> 和 <link rel="canonical"> 判断多语言页面关系。Yii 不自动输出这些,得自己在布局文件(views/layouts/main.php)里补。
示例写法:
<link rel="alternate" hreflang="zh" href="<?=%20Url::to(['site/index',%20'lang'%20=>%20'zh'],%20true)%20?>"><link rel="alternate" hreflang="en" href="<?=%20Url::to(['site/index',%20'lang'%20=>%20'en'],%20true)%20?>"><link rel="canonical" href="<?=%20Url::to(['site/index',%20'lang'%20=>%20Yii::%24app->language],%20true)%20?>">
注意:
- 必须用
true参数生成绝对 URL,相对地址对 hreflang 无效 - 不要硬编码语言列表,从配置或数据库读取支持的语言数组,避免新增语言时漏改
- canonical 必须指向当前语言版本,不能固定写死成
/zh/
真正难的不是写这几行 HTML,而是保证所有控制器动作都走同一套语言解析逻辑,且每个 Url::to() 调用都携带上下文语言——漏一处,SEO 就断一节。











