Next.js(Pages Router)v16でURLのロケールパラメータを検証する方法

サポートされていないロケールコードを適切に処理する

問題

ロケールコードがURL構造の一部になると、それはバリデーションが必要なユーザー入力に変わります。訪問者は有効なロケール(例:/en/about)と同じように、/xx/about/gibberish/contactなどと入力することができます。バリデーションがなければ、アプリケーションがクラッシュしたり、壊れたコンテンツを表示したり、混乱するエラーメッセージを表示したりする可能性があります。無効なロケールURLに遭遇したユーザーには、有効なロケールへのリダイレクトか、何が問題だったかを理解するための適切な「Not Found」レスポンスを通じて、明確な進路が必要です。

解決策

リクエストがページコンポーネントに到達する前に、受信するロケールパラメータをサポートされているロケールのリストと照合してバリデーションを行います。Next.jsのミドルウェアを使用してリクエストをインターセプトし、URLのロケールがサポートされている値と一致するかどうかを確認し、適切に対応します。無効なロケールの場合、ユーザーをデフォルトロケールにリダイレクトするか、リクエストを書き換えて404ページを表示します。これにより、有効なロケールコードのみがコンテンツをレンダリングし、無効なコードはユーザーエクスペリエンスを損なうことなく適切に処理されます。

ステップ

1. Next.js設定でサポートされるロケールを定義する

next.config.jsでi18n設定を構成し、アプリケーションがサポートするロケールとデフォルトとして機能するロケールを宣言します。

module.exports = {
  i18n: {
    locales: ["en", "fr", "de"],
    defaultLocale: "en",
    localeDetection: false,
  },
};

localeDetectionをfalseに設定すると、ブラウザの設定に基づく自動リダイレクトが防止され、ロケール処理を完全に制御できます。

2. ロケールパラメータを検証するミドルウェアを作成する

プロジェクトのルートまたはsrcフォルダを使用している場合はその中にmiddleware.tsファイルを作成します。

import { NextRequest, NextResponse } from "next/server";

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

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  const pathnameHasLocale = SUPPORTED_LOCALES.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );

  if (!pathnameHasLocale) {
    return;
  }

  const localeInPath = pathname.split("/")[1];

  if (!SUPPORTED_LOCALES.includes(localeInPath)) {
    const url = request.nextUrl.clone();
    url.pathname = pathname.replace(`/${localeInPath}`, `/${DEFAULT_LOCALE}`);
    return NextResponse.redirect(url);
  }
}

export const config = {
  matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};

このミドルウェアはURLパスからロケールを抽出し、サポートされているロケール配列と照合し、無効なロケールをパスの残りの部分を保持しながらデフォルトロケールにリダイレクトします。

3. 無効なロケールを404レスポンスで処理する

リダイレクトする代わりに404ページを表示したい場合は、リクエストをカスタム404ページに書き換えます。

import { NextRequest, NextResponse } from "next/server";

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

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  const pathnameHasLocale = SUPPORTED_LOCALES.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );

  if (!pathnameHasLocale) {
    return;
  }

  const localeInPath = pathname.split("/")[1];

  if (!SUPPORTED_LOCALES.includes(localeInPath)) {
    const url = request.nextUrl.clone();
    url.pathname = "/404";
    return NextResponse.rewrite(url);
  }
}

export const config = {
  matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};

ビルド時に静的に生成されるpages/404.jsにカスタム404ページを作成します。

4. カスタム404ページを作成する

export default function Custom404() {
  return (
    <div>
      <h1>404 - ページが見つかりません</h1>
      <p>お探しのページは存在しません。</p>
    </div>
  );
}

このページは、ミドルウェアが無効なロケールリクエストを書き換えるときに表示され、リクエストされたURLが利用できないことをユーザーに明確に伝えます。