Next.js(Pages Router)v16でユーザーの言語設定を検出する方法

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

課題

すべてのブラウザは、HTTPリクエストごとにAccept-Languageヘッダーを送信し、ユーザーの優先言語を優先順位順に示します。ほとんどのアプリケーションはこの貴重な情報を無視し、すべての訪問者にデフォルト言語を提供するため、アプリケーションがすでにユーザーの設定を把握しているにもかかわらず、ユーザーは言語切り替え機能を手動で探す必要があります。これにより、初回訪問時に不要な摩擦が生じ、ユーザーが自分の言語でコンテンツを見つける前にサイトを離脱する可能性があります。

ユーザーがアプリケーションのルートパスにアクセスした際、言語設定を確認し、理解できる言語のコンテンツに即座に誘導する機会があります。この検出機能がない場合、国際的なユーザーはブラウザの設定に関係なく英語優先の体験を強いられ、歓迎的でローカライズされた第一印象を提供する機会を逃すことになります。

解決策

ルートパスへのすべてのリクエストに対してサーバーサイドロジックを実行するルートページを作成します。受信リクエストからAccept-Language HTTPヘッダーを読み取り、解析してユーザーの最優先言語を抽出します。この言語をアプリケーションがサポートする言語リストと比較します。一致する言語が見つかった場合、ユーザーをその言語のルートパスにリダイレクトします。サポートされている言語が一致しない場合は、デフォルト言語パスにリダイレクトします。

このアプローチは、Next.jsのgetServerSideProps関数を活用します。この関数は各リクエストに対してサーバー上で実行され、リダイレクトレスポンスを返すことができます。ルートパスで検出を処理することで、アプリケーションはインテリジェントなデフォルトを提供しながら、必要に応じてユーザーが後で手動で言語を切り替えることも可能にします。

手順

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

Accept-Languageヘッダーには、オプションの品質値を持つ言語コードのカンマ区切りリストが含まれており、解析する必要があります。この形式を処理するためのパーサーライブラリをインストールします。

npm install accept-language-parser

このライブラリは、ヘッダー文字列から言語コードと品質スコアを抽出し、優先順位順に返します。

2. サーバーサイドリダイレクトロジックを持つルートページを作成する

ルートディレクトリに自動的にルーティングされるpages/index.tsxファイルを作成します。getServerSidePropsを使用して、宛先と永続フラグを持つリダイレクトオブジェクトを返します。

import { GetServerSideProps } from "next";
import parser from "accept-language-parser";

const SUPPORTED_LOCALES = ["en", "fr", "es", "de"];
const DEFAULT_LOCALE = "en";

export default function RootPage() {
  return null;
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  const acceptLanguageHeader = context.req.headers["accept-language"];

  let targetLocale = DEFAULT_LOCALE;

  if (acceptLanguageHeader) {
    const languages = parser.parse(acceptLanguageHeader);

    const matchedLanguage = languages.find((lang) =>
      SUPPORTED_LOCALES.includes(lang.code),
    );

    if (matchedLanguage) {
      targetLocale = matchedLanguage.code;
    }
  }

  return {
    redirect: {
      destination: `/${targetLocale}`,
      permanent: false,
    },
  };
};

getServerSideProps関数は各リクエストで実行され、Accept-Languageヘッダーを読み取り、ページコンテンツがレンダリングされる前に適切なロケールパスにリダイレクトします。

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

SUPPORTED_LOCALES配列を、アプリケーションがサポートする言語に合わせて更新します。パーサーは品質順に言語を返し、コードは最初に一致したものを選択します。

const SUPPORTED_LOCALES = ["en", "fr", "es", "de", "ja", "zh"];
const DEFAULT_LOCALE = "en";

パーサーは品質の高いものから低いものへと並べ替えられた言語を返すため、最初に見つかったサポート対象言語は、アプリケーションが満たすことができるユーザーの最も強い優先設定を表します。

4. Accept-Languageヘッダーがない場合を処理する

一部のリクエストにはAccept-Languageヘッダーが含まれていない場合があります。コードはヘッダーの存在を確認し、欠落している場合はデフォルトのロケールにフォールバックします。

if (acceptLanguageHeader) {
  const languages = parser.parse(acceptLanguageHeader);

  const matchedLanguage = languages.find((lang) =>
    SUPPORTED_LOCALES.includes(lang.code),
  );

  if (matchedLanguage) {
    targetLocale = matchedLanguage.code;
  }
}

これにより、ブラウザの言語情報が利用できない場合でも、アプリケーションは常に有効なロケールパスにリダイレクトされます。