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. ロケールパラメータを検証するミドルウェアを作成する

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

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 - Page Not Found</h1>
      <p>The page you are looking for does not exist.</p>
    </div>
  );
}

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