laravel 多语言切换核心是早期调用 app::setlocale() 或 config('app.locale'),最佳位置是全局中间件(如 setlocale.php),需在 startsession 后、路由绑定前执行,并优先从 session→cookie→accept-language 读取 locale,切换后同步存入 session 和 cookie。

如何用 App::setLocale() 动态切换语言
核心就一句话:Laravel 的多语言切换不靠 URL 重写或中间件自动识别,而是靠手动调用 App::setLocale() 或设置 config('app.locale')。它必须在请求生命周期早期执行,否则已加载的翻译内容不会刷新。
常见错误是把它放在控制器方法末尾、或者在视图里调用——这时候 __('welcome') 已经解析完了,切换无效。
- 最佳位置是全局中间件(如
app/Http/Middleware/SetLocale.php),且需排在StartSession之后、路由绑定之前 - 读取语言偏好时,优先从 session(
session('locale'))取, fallback 到 cookie(request()->cookie('locale')),最后才看Accept-Language头(注意:浏览器头不可靠,别直接信) - 切换后记得保存到 session 和 cookie,否则刷新就回退:
session(['locale' => 'zh']); Cookie::queue('locale', 'zh', 60*24*30)
为什么 __() 不生效?检查翻译文件路径和命名
Laravel 按 resources/lang/{locale}/{file}.php 查找翻译,比如 __('auth.failed') 会去找 resources/lang/zh/auth.php 里的 'failed' => '登录失败'。路径错一个字母、文件名大小写不对、PHP 数组键名拼错,都会静默失败(返回原始键名)。
- 确保 locale 目录名全小写(
zh不是ZH,en-us不是en_US) - 翻译文件必须返回数组,不能有 echo/print、不能包含 BOM 头(Windows 记事本易埋雷)
- 嵌套键名如
auth.throttle要求auth.php里有'throttle' => '太频繁了',不能写成'auth' => ['throttle' => '...']再套一层——除非你用trans('auth::throttle')加命名空间
Lang::get() 和 __() 有什么区别?
两者底层都走 Translator,但行为不同:__() 是辅助函数,支持占位符自动转义(__('Hello :name', ['name' => '<script>alert(1)</script>']) 会输出转义后的 HTML);Lang::get() 不处理转义,更底层,适合需要原样插入 JS 或属性值的场景。
- 模板中一律用
__(),安全省心 - JS 中动态取翻译(比如 Vue 组件),别直接
Lang::get()输出到 script 标签——容易 XSS,应通过 API 返回 JSON 或预渲染到data-属性 -
Lang::has('auth.failed')可用来判断当前 locale 是否有该翻译,避免 fallback 到英文键名
如何让 URL 带语言前缀(如 /zh/login)又不爆 404?
不能只改路由前缀,必须配合路由模型绑定和语言初始化。Laravel 默认不识别 /zh/login 这类路径,需在 routes/web.php 顶层加 locale 参数,并把所有路由包进 Route::prefix('{locale}')->group(...),同时在 group 内部用 where('locale', 'zh|en|ja') 限制合法值。
- 必须在 group 内第一行调用
App::setLocale($locale),否则后续route()生成的 URL 仍用默认 locale - 用
route('login', ['locale' => 'zh'])生成带前缀的链接,别硬拼/zh/login—— 否则换语言时要手动改所有 href - 注意:如果你用了
fallback_locale,App::isLocale('zh')在非 zh 请求下会返回 false,别用它做条件跳转逻辑
最麻烦的其实是 SEO 友好跳转:用户访问 /login 应 302 到 /zh/login(根据其偏好),这个逻辑得在中间件里做,而且得避开 API 路由、资源文件等非 HTML 请求。









