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ヘッダーを抽出し、最適なロケールを決定し、そのロケールのルートパスにリダイレクトすることで、最初のページ読み込みから希望する言語でコンテンツが表示されるようにします。