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;
  }
}

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