直接在中间件中调用app::setlocale()即可实现语言切换,但必须确保其注册在web中间件组中且位于startsession之后,按请求头→查询参数→session→配置默认值的优先级安全获取并校验语言代码,否则翻译函数和验证消息将无法正确本地化。

直接在中间件里调用 App::setLocale() 就行,但必须确保它在 session 启动之后、路由匹配之前执行,否则语言设置无效。
中间件必须放在 StartSession 之后
如果你把语言中间件注册在全局 $middleware 数组里(比如 Kernel.php 的 protected $middleware),它会在 session 初始化前运行,session()->get('locale') 永远是 null。
- 正确位置:放进
protected $middlewareGroups['web'],且排在\Illuminate\Session\Middleware\StartSession::class之后 - 错误写法:
App\Http\Middleware\SetLocale::class放在全局中间件数组最前面 → session 还没启动,读不到值 - 验证方法:在中间件里加
dd(session()->all(), App::getLocale()),看 session 是否有数据
handle() 里怎么安全取语言值
别只依赖 session;用户可能通过 URL 参数、请求头或表单提交临时切语言,中间件应该按优先级兜底处理。
- 推荐顺序:请求头
lang→ 查询参数lang→ session 值 → config 的app.locale(fallback) - 必须校验语言代码是否合法:
in_array($lang, ['en', 'zh_CN', 'ja'], true),避免被恶意传入非法目录名导致文件加载失败 - 设置后建议立刻验证:
if (! in_array(App::getLocale(), ['en', 'zh_CN'])) { App::setLocale(config('app.fallback_locale')); }
为什么 __('xxx') 在中间件里不生效
因为翻译函数依赖当前 locale,而中间件里调用 App::setLocale() 只影响后续执行流程——但 __('xxx') 如果在中间件之前(比如路由闭包、控制器构造函数)就执行了,它用的还是旧 locale。
- 典型坑:在
Route::get('/', function () { return __('welcome'); });里写翻译,但中间件在该闭包执行后才运行 → 永远显示默认语言 - 解决办法:所有需要翻译的内容,必须在中间件执行完之后才触发(即放在控制器方法体、Blade 视图、响应生成阶段)
- 调试技巧:在控制器里加
Log::info('Current locale: ' . App::getLocale());,确认是否真的设上了
最易被忽略的一点:Laravel 的验证错误消息(如 required)走的是独立的 validation.php 文件,它只响应当前 App::getLocale(),但不会自动重载——所以中间件里设完 locale 后,表单验证消息才能正确本地化;如果没设,哪怕视图里 __('xxx') 显示对了,错误提示仍是英文。











