路由定义必须显式包含{_locale}占位符,否则generate()会因缺少必需参数报错;正确写法是path中含/{_locale}并配requirements如'(zh|en)?'以支持空值,默认locale仅作fallback。

路由定义必须显式包含 {_locale} 占位符
Symfony 不会自动把语言塞进 URL,哪怕你配置了 default_locale 或启用了翻译组件。要生成像 /zh/blog 或 /en/about 这样的链接,路径里就得写死 {_locale} —— 它不是可选的“魔法字段”,而是普通占位符,必须出现在 path 中。
常见错误是只在 defaults 里设 "_locale": "zh",却不把 {_locale} 放进路径,结果 path('blog_show', ['slug' => 'hello']) 会报错:「Some mandatory parameters are missing ("_locale")」。
- 正确写法(注解):
#[Route('/{_locale}/{slug}', name: 'blog_show', requirements: ['_locale' => 'zh|en|fr'])] - 错误写法:
#[Route('/{slug}', defaults: ['_locale' => 'zh'], ...)]—— 路径没它,generate()就找不到参数来源 - 如果想支持无前缀访问(如
/blog默认为 zh),得靠两条路由或用空字符串匹配,但后者需配合requirements放宽校验(见下一条)
requirements 决定 {_locale} 是否可为空
默认情况下,requirements={"_locale": "zh|en"} 会拒绝空值,所以 /blog 匹配失败。要让它同时接受 /zh/blog 和 /blog,得把正则改成允许空字符串,或者拆成两条路由。
- 推荐方案(单条路由 + 空值兼容):
requirements={"_locale": "(zh|en)?"},再配defaults={"_locale": "zh"}。注意括号和问号是正则语法,不是 Symfony 特有写法 - 首页场景更简单:
#[Route('/{_locale}', name: 'home', defaults: ['_locale' => 'zh'])],不加requirements就能匹配/和/zh,因为 Symfony 允许可选段末尾为空 - 别用
requirements={"_locale": ".*"}—— 太宽泛,可能匹配到非法 locale,也影响路由匹配顺序和性能
生成带 _locale 的 URL 必须显式传参
Twig 的 path() 或 PHP 的 $urlGenerator->generate() 不会读取 session、cookie 或请求里的当前 locale 来补全 _locale。它只看 route 定义和你传进去的数组参数。
- 正确调用:
{{ path('blog_show', {'_locale': 'en', 'slug': 'hello'}) }}→ 输出/en/hello - 错误调用:
{{ path('blog_show', {'slug': 'hello'}) }}→ 报错,即使用户当前在/en/下浏览 - 若想动态传当前 locale,模板中可用
{{ app.request.locale }}:{{ path('blog_show', {'_locale': app.request.locale, 'slug': 'hello'}) }} - 控制器里同理:
$this->generateUrl('blog_show', ['_locale' => $request->getLocale(), 'slug' => 'hello'])
子域名方案下 {_locale} 仍需参与路由定义
用 zh.example.com 做语言区分时,很多人以为可以去掉 URL 路径里的 {_locale}。其实不行 —— 路由匹配发生在 host 解析之后,host 模板只是增加一层约束,{_locale} 如果还在路径里,就必须提供值。
- 若走子域名,建议路由定义同时含
host和{_locale}:#[Route('/{slug}', host: '{_locale}.example.com', requirements: ['_locale' => 'zh|en'])] - 此时生成 URL 仍要传
_locale:path('blog_show', ['_locale' => 'zh', 'slug' => 'hello']),否则无法确定用哪个子域名 - 如果不希望路径里出现
{_locale},那就彻底移除它,改用监听器从 host 提取 locale 并设置到 request,但所有控制器逻辑和翻译仍依赖该值,URL 生成也不再需要传_locale参数
最容易被忽略的是:路由定义、URL 生成、请求匹配这三处的 _locale 必须严格一致 —— 少一个占位符、漏一次传参、正则写错范围,都会导致 404 或生成失败。它不像配置项那样“全局生效”,而是一个实打实的路径参数。











