React Router v7でユーザー言語設定を検出する方法

ブラウザ設定に基づく自動リダイレクト

問題

すべてのブラウザは、HTTPリクエストごとにAccept-Languageヘッダーを送信し、ユーザーの優先言語を優先順に示します。このヘッダーには、ユーザーがどの言語で表示されることを期待しているかという貴重な情報が含まれていますが、ほとんどのアプリケーションはこれを完全に無視しています。代わりに、すべての訪問者にデフォルトの言語(通常は英語)を表示し、ブラウザがすでに彼らの好みを伝えているにもかかわらず、ユーザーに言語切り替えを手動で探すよう強制しています。これにより、特に国際的なユーザーにとって不必要な摩擦と悪い第一印象を生み出します。

解決策

ルートパスのローダーを作成し、受信リクエストからAccept-Languageヘッダーを読み取ります。ヘッダーを解析してユーザーの優先言語とその品質値を抽出します。優先言語をアプリケーションのサポートされているロケールと比較します。一致するものが見つかった場合、ユーザーをそのロケールのパスにリダイレクトします。サポートされている言語が一致しない場合は、デフォルトのロケールにリダイレクトします。これにより、ユーザーは自分のブラウザですでに設定した設定に基づいて、自動的にサイトのローカライズされたバージョンにアクセスできます。

ステップ

1. Accept-Languageヘッダーを解析するライブラリをインストールする

Accept-Languageヘッダーには、慎重な解析が必要な品質値を持つ特定のフォーマットがあります。これを正しく処理するために専用のライブラリを使用してください。

npm install accept-language-parser

このライブラリは、HTTP仕様に従って品質値とエッジケースを処理し、ヘッダー文字列を順序付けられた言語設定のリストに解析します。

2. サポートするロケールを定義する

アプリケーションがサポートするロケールをリストし、デフォルトのフォールバックを指定するヘルパーファイルを作成します。

export const supportedLocales = ["en", "fr", "de", "es", "ja"] as const;

export const defaultLocale = "en";

export type Locale = (typeof supportedLocales)[number];

これにより、アプリケーションが提供できる言語の単一の情報源が提供され、コードベース全体での型安全性が確保されます。

3. ロケール検出ヘルパーを作成する

Accept-Languageヘッダー値を受け取り、最適にマッチするサポート対象ロケールを返す関数を構築します。

import parser from "accept-language-parser";
import { supportedLocales, defaultLocale, type Locale } from "./locales";

export function detectLocale(acceptLanguageHeader: string | null): Locale {
  if (!acceptLanguageHeader) {
    return defaultLocale;
  }

  const languages = parser.parse(acceptLanguageHeader);

  for (const lang of languages) {
    const code = lang.code.toLowerCase();
    if (supportedLocales.includes(code as Locale)) {
      return code as Locale;
    }
  }

  return defaultLocale;
}

この関数はヘッダーを解析し、品質順にユーザーの言語設定を反復処理し、サポート対象ロケールと最初にマッチするものを返すか、デフォルトにフォールバックします。

4. ルートインデックスルートを設定する

ルート設定にインデックスルートを追加して、ルートパスへのリクエストを処理します。

import { type RouteConfig, index, route } from "@react-router/dev/routes";

export default [
  index("routes/index.tsx"),
  route(":locale", "routes/locale-root.tsx", []),
] satisfies RouteConfig;

インデックスルートは他のルートがマッチする前にルートパスリクエストをインターセプトし、言語検出とリダイレクトを実行できるようにします。

5. 言語検出機能を持つインデックスルートローダーを実装する

Accept-Languageヘッダーを読み取り、適切なロケールパスにリダイレクトするインデックスルートモジュールを作成します。

import { redirect } from "react-router";
import type { Route } from "./+types/index";
import { detectLocale } from "~/utils/detect-locale";

export async function loader({ request }: Route.LoaderArgs) {
  const acceptLanguage = request.headers.get("Accept-Language");
  const locale = detectLocale(acceptLanguage);
  return redirect(`/${locale}`);
}

ユーザーがルートパスにアクセスすると、このローダーはリクエストからAccept-Languageヘッダーを抽出し、最適なロケールを決定して、そのロケールのルートパスにリダイレクトします。これにより、ユーザーは最初のページ読み込みから好みの言語でコンテンツを見ることができます。