😎

Laravel+Next(Subpath locale)で多言語化対応

2022/09/10に公開

nextjsはすでに記事がいくつかあるように、かなり手軽に多言語化対応ができる。ドメインで指定したりサブパスを切って対応したり、Cookieでもできるらしい。
今回はサブパスでフロントエンドの多言語か対応したので、それに応じてバックエンドからのエラーやレスポンスの多言語か対応もしたので備忘録として残す。

前提

  • ユーザーは任意の言語にいつでも切り替えられる
  • ロケール判定はサブパス
  • httpクライアントはaxios
  • axiosはcreateしてから使ってる

方向性としては生成タイミングの問題で煩わしい思いをしたくないので、Interceptor使ってHttpヘッダーに選択されているロケールを入れてしまって、セッションごとにLaravel側でsetLocaleする形にする。

セッションが発生しない処理で多言語化対応が必要なのであればDB等にユーザーの選好言語を保存してあげないと通知、メールの多言語化ができなそうだが、現状そこは要件ではないのでスルー。

frontend

サブパスロケール判定処理

getCurrentClientLocale
import isClient from "@utils/isClient";

const getCurrentClientLocale = () => {
  const path = isClient() ? window.location.pathname : "";
  if (path.startsWith("/en")) {
    return "en";
  }
  return "ja";
};

export default getCurrentClientLocale;

axiosの生成

Repository.ts

import axios from "axios";

import isClient from "@utils/isClient";
import getCurrentClientLocale from "@utils/getCurrentClientLocale";
import HttpRequestHeaderKeys from "@constants/HttpRequestHeaderKeys";

const Repository = axios.create({
  baseURL: isClient()
    ? process.env.NEXT_PUBLIC_API_ENDPOINT
    : process.env.API_ENDPOINT,
  responseType: "json",
  withCredentials: true,
  headers: {
    "Content-Type": "application/json",
  },
});
Repository.interceptors.request.use((request) => {
  request.headers = {
    ...request.headers,
    [HttpRequestHeaderKeys.PREFERRED_LOCALE]: getCurrentClientLocale(),
  };
  return request;
});

export default Repository;

backend

Laravel Middlewar

SetPreferredLocale.php

<?php

namespace App\Http\Middleware;

use App\Constants\HttpRequestLocaleHeader;
use App\Constants\SelectableLocales;
use Closure;
use Illuminate\Http\Request;

class SetPreferredLocale
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {
        $preferred_locale = $request->headers->get(HttpRequestLocaleHeader::PREFERRED_LOCALE->key());
        if ($preferred_locale === SelectableLocales::EN->value) {
            app()->setLocale(SelectableLocales::EN->value);
        } else {
            app()->setLocale(config('app.locale'));
        }
        return $next($request);
    }
}

全てのルートに適応する必要があったのでそのように配置。

Kernel.php
    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        //custom middleware
        SetPreferredLocale::class
    ];

laravel側で例外メッセージとか、abortとかうっかりハードコーディングしてると、多言語化されないぞ?ってなってしまうのでお気をつけて。基本__()で参照する。

Discussion