PAYATHON 2026

Laravel 12 中 session 语言已切换,Blade 的 locale 为何仍是 en?

数据小许

session 中保存语言后,Laravel 不会自动将它同步为当前请求的应用 locale。每次页面请求都需要在 session 启动后、Blade 渲染前调用 App::setLocale()。

给出的 SetLocale 已经包含这个调用。如果它在 dashboard 的同一次请求中确实读取到 ja 并执行成功,而 Blade 仍读到 en,应继续检查后续代码是否重新设置了语言。现有材料还不足以确定唯一根因。

为什么 session 与应用 locale 可以不同?

session('locale') 读取 session 数据,app()->getLocale() 读取当前应用的 app.locale 配置。Laravel 12 的 App::setLocale() 会同时更新该配置和 translator 的 locale。因此,写入 session 保存的是用户的选择,页面请求仍需执行设置语言的逻辑。Laravel 12 源码

切换过程通常是:

  1. 请求 /lang/ja,路由把 ja 写入 session。
  2. 浏览器收到重定向,再次请求 dashboard。
  3. dashboard 请求中的中间件读取 ja,调用 App::setLocale('ja')。
  4. Blade 使用当前请求的语言渲染。

切换路由执行前,中间件可能读取到旧语言,这是正常的。应观察重定向后的 dashboard 请求。

先确认 Laravel 12 实际使用的注册入口

采用 Laravel 12 默认项目结构时,中间件在 bootstrap/app.php 中配置。仅在自行创建或未被使用的 app/Http/Kernel.php 中添加配置,不会使配置自动生效。如果项目从旧版本升级,并保留了自定义 Kernel,应检查实际的 Kernel 绑定,不能只根据版本判断。

在默认结构中,将下面的注册合并到现有 bootstrap/app.php 的 withMiddleware 回调中:

use App\Http\Middleware\SetLocale;
use Illuminate\Foundation\Configuration\Middleware;

// 合并到现有应用配置链中
->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [
        SetLocale::class,
    ]);
})

默认 web 组包含 StartSession,追加 SetLocale 可以让它在 session 启动之后执行。这个注册覆盖使用 web 组的请求;如果 dashboard 来自其他路由文件或自定义路由组,需要单独核对配置。Laravel 12 中间件文档

原问题展示的顺序已经把 SetLocale 放在 StartSession 之后。如果这份配置确实生效,顺序本身没有明显问题。

可以查看实际路由及展开后的中间件组:

php artisan route:list -vv

检查语言切换路由和 dashboard 路由是否都包含 web 组及 SetLocale。Laravel 12 路由文档

在 dashboard 请求中确认读取值和设置结果

“中间件执行过”还不足以证明它在出问题的请求中读取到了正确语言。可以暂时把 handle() 改为以下形式:

public function handle($request, Closure $next)
{
    $locale = $request->session()->get('locale', 'en');

    if (!in_array($locale, ['en', 'ja'], true)) {
        $locale = 'en';
    }

    App::setLocale($locale);

    logger()->debug('SetLocale applied', [
        'path' => $request->path(),
        'session_locale' => $request->session()->get('locale'),
        'app_locale' => app()->getLocale(),
        'translator_locale' => app('translator')->getLocale(),
    ]);

    return $next($request);
}

切换到日语后,完整刷新 dashboard,检查对应请求的日志。三个语言值预期都为 ja,具体结果需要在项目中验证。

如果日志中的 session_locale 仍是 en,应检查日志是否来自正确的请求,以及 session 是否在之后才被修改。如果日志显示三个值均为 ja,但 Blade 显示 en,可以检查设置语言之后、视图输出之前的代码。

如果语言随后变回 en,检查谁覆盖了它

在项目代码中搜索这些调用和配置写入:

App::setLocale(...);
app()->setLocale(...);
app('translator')->setLocale(...);
config(['app.locale' => ...]);
Config::set('app.locale', ...);

优先检查 dashboard 控制器、后续中间件、View Composer,以及根据用户资料设置语言的代码。尤其留意直接写入 app.locale 或只修改 translator 的情况,它们可能让应用 locale 与 translator locale 不一致。

在 dashboard 视图中临时增加:

SESSION: {{ session('locale') }} <br>
APP: {{ app()->getLocale() }} <br>
TRANSLATOR: {{ app('translator')->getLocale() }}

如果菜单和 dashboard 确实在同一次服务端渲染中都使用普通 __() 调用,且期间没有修改 locale,它们应使用同一套语言状态。菜单能翻译可以作为排查线索,但还需要确认菜单是否传入了显式语言参数,或来自独立请求、预先生成的文本或缓存内容。

翻译目录需要核对,但不能解释应用 locale 仍为 en

Laravel 12 文档使用项目根目录下的 lang 目录。框架仍兼容已存在的 resources/lang 目录,因此不能仅凭路径认定翻译文件放错了。本地化文档、语言路径选择源码

在实际页面请求中查看:

dump(lang_path());

确认返回目录中存在 en/messages.php 和 ja/messages.php,并确保实际 PHP 文件以 <?php 开头。

还可以临时显式指定日语,单独验证翻译加载:

{{ __('messages.dashboard', [], 'ja') }}

如果这样能显示日语,而普通 __('messages.dashboard') 不能,优先检查当前请求的语言设置。如果显式指定也失败,再检查实际语言目录、文件内容和键名。这种写法用于定位问题,正常页面仍应由统一的语言中间件控制。

翻译文件缺失或触发 fallback 可以解释文字为什么没有变化,但不能直接解释 app()->getLocale() 为什么仍是 en。先确认 dashboard 请求的中间件注册和设置结果,再追踪语言覆盖,最后检查翻译加载。重复清缓存无法替代这些检查。

备注:内容仅供参考。