NegotiateLocale.php 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. <?php
  2. namespace App\Http\Middleware\V3;
  3. use App\Http\Middleware\SetLocale;
  4. use Closure;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Support\Facades\App;
  7. use Symfony\Component\HttpFoundation\Response;
  8. /**
  9. * v3 的语言协商:按 `Accept-Language` 决定 `__()` 用哪种语言。
  10. *
  11. * 只挂在 v3 路由组上(routes/api.php)。之前 api 组没有任何语言协商,`__()` 永远取
  12. * en,中文界面里显示英文错误(dashboard-test BUG-5)。
  13. *
  14. * 继承 web 组的 {@see SetLocale},复用它的标签解析(q 值排序、大小写不敏感、
  15. * zh-CN/zh-TW 归简繁、th-TH → th),但**只读请求头**:
  16. *
  17. * - 不读 `?lang=` / cookie:API 的调用方(v6、mobile)明确知道界面语言,直接放请求头里;
  18. * `?lang=` 在不少 v3 端点上已有业务含义(如译文语言),不能拿来当界面语言。
  19. * - 不读写 cookie / session:API 无状态,api 组也没有 session。尤其不能读 web 组写的
  20. * `language` cookie——Laravel 页面与 dashboard-v6 同域,这个 cookie 会跟着 v6 的请求
  21. * 一起发来,若它优先,用户在 Laravel 页面选过的语言就会盖过 v6 明确声明的界面语言。
  22. * Laravel 页面将来要调 v3 并显示错误文案,也应像 v6 一样自己带 Accept-Language
  23. * (取 `<html lang>`,即 `app()->getLocale()`)。
  24. *
  25. * 匹配不上就用 `config('mint.default_language')`。Problem Details 的 `title` 按 RFC 9457
  26. * 保持稳定英文,不受这里影响;本地化的是 `detail` 与 `errors`。
  27. */
  28. class NegotiateLocale extends SetLocale
  29. {
  30. public function handle(Request $request, Closure $next): Response
  31. {
  32. $supported = array_keys(config('mint.languages'));
  33. // 兜底用 mint.default_language 而不是 app.locale:App::setLocale() 会顺带改写
  34. // config('app.locale'),常驻进程(Octane、队列)里上一个请求的语言会串到下一个
  35. App::setLocale($this->getBrowserLocale($request, $supported) ?? config('mint.default_language', 'en'));
  36. $response = $next($request);
  37. // 响应随 Accept-Language 而变,告诉缓存别混用
  38. $response->headers->set('Vary', trim($response->headers->get('Vary').', Accept-Language', ', '));
  39. $response->headers->set('Content-Language', App::getLocale());
  40. return $response;
  41. }
  42. }