Next.js(Pages Router)v16でナビゲーションリンクのロケールを維持する方法

内部ナビゲーション全体でロケールを維持

問題

Webアプリケーションが言語設定をURLパスにエンコードする場合、すべての内部リンクは、ユーザーのセッション全体を通じてその言語コンテキストを保持する必要があります。明示的なロケール処理がない場合、単純なパスとして記述されたリンクは、誤って言語プレフィックスを削除してしまい、ユーザーがナビゲーション中に選択した言語を失う原因となります。これにより、ローカライズされた体験が中断され、ユーザーは希望する言語を繰り返し選択することを余儀なくされます。

解決策

すべての内部ナビゲーションリンクが、ルーターコンテキストから現在のロケールを読み取ることで、自動的に現在のロケールを含むようにします。Next.js Pages Routerは、ルーティングシステムを通じて組み込みのロケールサポートを提供しており、アクティブなロケールはuseRouterフックを介して利用できます。デフォルトでは、Linkコンポーネントは、クライアント側の遷移中に現在のロケールを保持しますが、この動作を理解し、アプリケーション全体で一貫して適用することで、シームレスなロケール対応ナビゲーションが保証されます。

手順

1. ルーターから現在のロケールにアクセスする

useRouterフックは、locale(現在アクティブなロケール)、locales(設定されたすべてのロケール)、およびdefaultLocale(設定されたデフォルトロケール)を提供します。

import { useRouter } from "next/router";

export default function Navigation() {
  const router = useRouter();
  const currentLocale = router.locale;

  return (
    <nav>
      <p>Current locale: {currentLocale}</p>
    </nav>
  );
}

このコンポーネントは、ルーターからアクティブなロケールを読み取り、ナビゲーションロジックで使用できるようにします。

2. 明示的なlocale propsなしでLinkコンポーネントを使用する

Linkにlocale propが提供されていない場合、クライアント遷移中に現在アクティブなロケールが使用されます。

import Link from "next/link";

export default function Navigation() {
  return (
    <nav>
      <Link href="/about">About</Link>
      <Link href="/products">Products</Link>
      <Link href="/contact">Contact</Link>
    </nav>
  );
}

これらのリンクは、現在のロケールを自動的に維持します。ユーザーが/fr/productsを表示している場合、「About」をクリックすると/fr/aboutに移動します。

3. 明示的な制御のためのロケール対応リンクヘルパーを作成する

明示的な制御が必要な場合や、ロケール処理をより明確にしたい場合は、現在のロケールを明示的に渡すラッパーコンポーネントを作成します。

import Link from "next/link";
import { useRouter } from "next/router";
import { ReactNode } from "react";

interface LocaleLinkProps {
  href: string;
  children: ReactNode;
}

export function LocaleLink({ href, children }: LocaleLinkProps) {
  const { locale } = useRouter();

  return (
    <Link href={href} locale={locale}>
      {children}
    </Link>
  );
}

このコンポーネントはロケールの保持を明示的にし、必要に応じて追加のロケール固有のロジックで拡張できます。

4. ロケールを保持したプログラマティックナビゲーションを処理する

ルーターメソッドを直接使用する場合、トランジションオプションを介してロケールを指定するか、hrefパラメータをオブジェクトとして渡すことで、ロケールを含むすべてのルーティング情報を保持できます。

import { useRouter } from "next/router";

export default function NavigationButton() {
  const router = useRouter();
  const { pathname, asPath, query, locale } = router;

  const handleNavigate = () => {
    router.push({ pathname: "/dashboard", query }, asPath, { locale });
  };

  return <button onClick={handleNavigate}>Go to Dashboard</button>;
}

このパターンにより、プログラマティックナビゲーションが現在のロケールと既存のクエリパラメータを維持することが保証されます。

5. アプリケーション全体で一貫したリンクパターンを適用する

アプリケーション全体で標準のLinkコンポーネントを使用し、その組み込みのロケール保持動作に依存します。

import Link from "next/link";

export default function ProductCard({ productId }: { productId: string }) {
  return (
    <article>
      <h2>Product {productId}</h2>
      <Link href={`/products/${productId}`}>View Details</Link>
      <Link href="/products">Back to Products</Link>
    </article>
  );
}

静的ルートと動的ルートの両方が自動的に現在のロケールプレフィックスを含むため、ユーザーはアプリケーション内を移動する際に選択した言語を維持できます。