Laravel × Next.jsで作る完璧な多言語化(i18n)アーキテクチャ!非同期処理の罠と解決策

未分類

こんにちは、Trade Agency編集長のKです。現役のシステムアーキテクト兼トレーダーとして、日々トレードシステムの開発と市場の波に向き合っています。

昨今のグローバルなWebアプリケーション開発において、多言語対応(i18n)は避けて通れない要件です。しかし、フロントエンドの見た目を多言語化するのは簡単でも、バックエンドの非同期処理(メール送信など)まで完璧に言語を同期させるのは至難の業です。

今回は、LaravelとNext.jsを用いたモダンな構成において、ユーザー体験(UX)を極限まで損なわない【完全な多言語化アーキテクチャ】を構築する実践的なノウハウを公開します。

Trade Agency 編集長
Trade Agency 編集長

「画面は英語なのに、届いた二段階認証(OTP)メールが日本語だった」なんて経験ありませんか?開発側からすると「非同期だから仕方ない」と言い訳したくなりますが、ユーザーにとっては最悪の体験です。これを徹底的に潰していきますよ。

スポンサーリンク
OANDA証券 MT4

多言語化における最大の罠「非同期ワーカーでのコンテキスト喪失」

通常、多言語対応はHTTPリクエストのヘッダー(Accept-Languageなど)をミドルウェアでキャッチし、システム言語を切り替えることで実現します。しかし、メール送信などの重い処理をキュー(Queue)に入れて非同期化すると、重大な問題が発生します。

非同期ジョブを処理する裏側のプロセス(ワーカー)には、ブラウザからのHTTPリクエスト情報が存在しません。そのため、ミドルウェアで設定したはずの言語情報が引き継がれず、システムデフォルトの言語(英語など)でメールが送信されてしまうというバグが発生します。

フロントエンドとバックエンドの言語を同期させる

まずは入り口の対策です。Next.js側の言語スイッチャーコンポーネントで、URLのロケールを切り替えるだけでなく、バックエンド(DB)のユーザー情報も同時に更新する仕組みを作ります。

// LanguageSwitcher.tsx (抜粋)
const switchLocale = async (nextLocale: string) => {
    // ログイン中のユーザーであれば、バックエンドの言語設定も更新する
    if (user) {
        try {
            await api.put('/user/locale', { locale: nextLocale });
        } catch (error) {
            console.error('Failed to update locale:', error);
        }
    }

    // フロントエンドの言語切り替え(URLの書き換え等)
    startTransition(() => {
        router.replace(pathname, { locale: nextLocale });
    });
};
Trade Agency 編集長
Trade Agency 編集長

ログイン中のユーザーなら、この一手間を入れるだけで、次回以降どの端末からアクセスしても自分の好みの言語が維持されるようになります。UX向上の第一歩ですね。

Laravelの神機能「HasLocalePreference」の活用

バックエンド側では、Laravelが用意している強力なインターフェースを活用します。Userモデルに【HasLocalePreference】を実装し、preferredLocaleメソッドでDBのlocaleカラムを返すように設定します。

// app/Models/User.php
use Illuminate\Contracts\Translation\HasLocalePreference;

class User extends Authenticatable implements HasLocalePreference
{
    /**
     * 通知を送信する際にLaravelが自動参照する言語設定
     */
    public function preferredLocale(): ?string
    {
        return $this->locale; // DBに保存された 'ja' や 'en' を返す
    }
}

このインターフェースを実装しておくと、Laravelは非同期ワーカーで通知メールを構築する直前に、対象ユーザーのDB設定を参照し、ワーカーの実行言語を一時的にその言語へ【自動で切り替えて】くれます。

しかし、これだけでは完璧ではありません。新規登録時、ログイン前のOTP送信、あるいは未認証ゲストからのお問い合わせなど、【ユーザーレコードが存在しない、またはDBの言語と現在画面で見ている言語が異なる】ケースが存在します。

例えば、普段は日本語設定のユーザーが、出先のPCで英語UIからログインしようとした場合、DBの言語を正としてしまうと「画面は英語、OTPメールは日本語」というチグハグな状態になります。

ここでのベストプラクティスは、コントローラー内で現在の実行言語を取得し、通知インスタンスに【直接言語を注入】することです。

// app/Http/Controllers/AuthController.php (ログイン時のOTP送信例)

// 1. ミドルウェアが設定した「現在の画面の言語」を取得
$currentLocale = \Illuminate\Support\Facades\App::getLocale();

// 2. 通知インスタンスに locale() をチェーンして非同期キューへ投げる
$user->notify(
    (new TwoFactorCode($code))->locale($currentLocale)
);
  • App::getLocale() で現在画面の言語をキャッチする
  • ->locale() メソッドで言語を強制指定する
  • キューのペイロードに言語指定がシリアライズされ、ワーカーが確実に対象言語でメールを生成する

APIレスポンスと例外メッセージの多言語化

最後に、APIが返すJSONレスポンスや、内部でスローする例外メッセージから【ハードコーディングされた文字列を完全に排除】します。

// lang/ja/messages.php
return [
    'internal_server_error' => 'サーバーエラーが発生しました。時間をおいて再度お試しください。',
    'throttle_lockout'      => '失敗回数が上限に達しました。約:minutes分後に再度お試しください。',
];

// Controller内での呼び出し例
return response()->json([
    'message' => __('messages.internal_server_error')
], 500);

// 動的な変数を渡す場合
throw ValidationException::withMessages([
    'login_id' => [__('messages.throttle_lockout', ['minutes' => $minutes])],
]);
Trade Agency 編集長
Trade Agency 編集長

「ユーザーが見つかりません」といった文字列をコントローラーに直接書くのはアーキテクチャの匂い(Code Smell)です。RateLimiter(スロットリング)などの変数もプレースホルダー(:minutes)を活用して、必ず言語ファイルに切り出しましょう。

よくある質問(FAQ)

Q
Next.js側で言語を切り替えたのに、メールがデフォルト言語で届いてしまいます。
A

非同期キューを利用している場合、ワーカープロセス内でHTTPリクエストのヘッダー情報が失われるためです。UserモデルにHasLocalePreferenceを実装するか、通知送信時に->locale()メソッドで言語を明示的に渡す必要があります。

Q
新規登録のメール認証(VerifyEmail)を非同期化するにはどうすればよいですか?
A

Laravel標準のVerifyEmailは同期処理です。UserモデルのsendEmailVerificationNotificationメソッドをオーバーライドし、ShouldQueueを実装したカスタム通知クラスを呼び出すように変更してください。

Q
言語ファイル(lang)の管理が多くて大変です。良い管理方法はありますか?
A

機能ごと(auth.php, messages.php, emails.phpなど)にファイルを分割し、フロントエンド(next-intlなど)とキー構造をある程度統一しておくと、開発体験が圧倒的に向上します。

まとめ

グローバル対応を見据えたシステムアーキテクチャにおいて、考慮すべきポイントを整理します。

  • フロントエンドの言語切り替え時は、バックエンドのDBも同期させる
  • 認証済みユーザーの非同期通知は、HasLocalePreferenceに任せる
  • 未認証や認証の入り口(OTP等)では、現在画面の言語を明示的に通知に注入する
  • APIのレスポンスや例外メッセージからハードコーディングを完全に排除する

神は細部に宿ります。システムのエッジケースを徹底的に潰し、世界中のユーザーに「違和感のないUX」を提供できるアーキテクチャを目指していきましょう。

この記事を書いた人
Trade Agency編集長K

■ 投資歴: 10年以上
■ スタイル: Fintokei(プロップファーム)× 株式投資
■ 職業: ITコンサルタント、アプリ開発

【詳細】
USD/JPYの裁量トレードでプロップファーム攻略に挑むエンジニアトレーダー。
Fintokeiで稼いだ利益を、堅実なバリュー・グロース株へ投資して資産を最大化する「資金循環スタイル」を実践中。

本業のWeb開発スキルを活かし、FXに役立つツール(Lot計算機など)を自作・無料公開しています。

Trade Agency編集長Kをフォローする
未分類
スポンサーリンク
Trade Agency編集長Kをフォローする

コメント

タイトルとURLをコピーしました