应通过中间件在请求早期调用 App::setLocale() 动态设置语言环境,确保翻译服务使用新 locale;不可在控制器中设置,因 translator 单例已初始化并缓存旧 locale。

如何通过请求参数动态设置 app.locale
Laravel 默认从 config/app.php 的 locale 键读取语言环境,但这个值是启动时加载的静态配置。要实现「按请求参数切换」,不能直接改 config('app.locale'),而必须在请求生命周期早期调用 App::setLocale() —— 否则翻译函数(如 __('xxx'))仍会使用原始 locale。
推荐在中间件中处理,确保在路由匹配后、控制器执行前完成切换:
class SetLocaleFromRequest
{
public function handle($request, Closure $next)
{
$locale = $request->input('lang', $request->input('locale'));
if ($locale && in_array($locale, config('app.available_locales', ['en', 'zh-CN']))) {
\App::setLocale($locale);
}
return $next($request);
}
}
注意:in_array() 校验必不可少,否则攻击者可传入任意字符串导致 Lang::get() 加载失败或触发异常。
为什么不能在控制器里调用 App::setLocale()?
因为 Laravel 的翻译服务(translator)在容器中是单例且**初始化时就绑定了当前 locale**。即使你在控制器里调用 App::setLocale('zh-CN'),已实例化的 translator 实例仍缓存着旧 locale 的加载器和消息包。
- 视图中
__('Welcome')仍返回英文(除非你手动刷新 translator) -
trans('auth.failed')不生效 - 表单验证错误信息也不会变
正确做法:要么在中间件中设 locale(推荐),要么在 boot() 中监听 localeChanged 事件并重置 translator(复杂且不必要)。
如何让 URL 路由支持多语言前缀(如 /zh-CN/login)?
如果希望把语言作为 URL 一部分(而非 query 参数),需配合路由分组 + 正则约束,并将 locale 传递给中间件:
Route::middleware(['set_locale'])->prefix('{locale}')->where('locale', '[a-zA-Z\-]+')->group(function () {
Route::get('/login', [LoginController::class, 'showLoginForm'])->name('login');
});
对应中间件接收参数:
public function handle($request, Closure $next)
{
$locale = $request->route('locale');
if ($locale && in_array($locale, config('app.available_locales'))) {
\App::setLocale($locale);
// 可选:把 locale 存入 session 或 cookie,避免后续请求重复传
session(['locale' => $locale]);
}
return $next($request);
}
关键点:where('locale', '[a-zA-Z\-]+') 防止路径注入;session(['locale' => ...]) 是提升体验的补充,不是必须,但能减少客户端反复传参。
本地化资源加载失败的常见报错和排查点
切换后出现 Translation not found 或空字符串,通常不是代码逻辑问题,而是文件结构或命名不匹配:
- 语言包路径必须是
resources/lang/{locale}/{file}.php,例如resources/lang/zh-CN/auth.php -
{locale}名称必须与App::setLocale()传入的**完全一致**(区分大小写、含连字符) - 运行
php artisan config:clear和php artisan view:clear,避免缓存干扰 - 检查
config/app.php中是否设置了fallback_locale,它会在当前 locale 缺失键时兜底,容易掩盖路径错误
最易忽略的是:Laravel 9+ 默认启用 lang 目录下 JSON 文件支持,若混用 PHP 数组和 JSON,且未禁用 json loader,可能加载错格式。










