TanStack Start v1でナビゲーションリンクのロケールを維持する方法

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

問題

ロケール情報がURLパスにエンコードされている場合、すべてのナビゲーションリンクは一貫したユーザー体験を維持するためにそのロケールを保持する必要があります。ユーザーが/fr/productsでサイトのフランス語版を閲覧している場合、/aboutへのリンクをクリックすると、デフォルト言語に切り替わり、セッションコンテキストが失われます。すべてのリンクにロケールパラメータをハードコーディングすることは反復的でエラーが発生しやすく、特にアプリケーションが成長し、コンポーネント全体により多くのリンクが追加されるにつれて顕著になります。

ロケールを考慮したナビゲーションへの体系的なアプローチがなければ、開発者はすべてのLinkコンポーネントを通じて手動でロケールを渡すか、ユーザーがセッション中に予期せずデフォルト言語に切り替わることを受け入れるかの選択を迫られます。どちらの結果もユーザー体験を低下させ、メンテナンスの負担を増加させます。

解決策

フレームワークのLinkの周りにラッパーコンポーネントを作成し、URLから現在のロケールを自動的に読み取り、すべてのナビゲーション先のparamsプロパティに注入します。この論理を単一の再利用可能なコンポーネントに集中させることで、すべての内部リンクは、開発者がすべての呼び出し箇所で手動でロケールパラメータを渡す必要なく、ロケールを考慮した動作を継承します。

このアプローチは、ルーターのフックを使用してルートパラメータからアクティブなロケールを読み取り、そのロケールを宛先リンクのパラメータにマージすることで機能します。ラッパーは、ナビゲーション全体でロケールの継続性を確保しながら、他のすべてのLink機能を保持します。

手順

1. ロケールを考慮したLinkラッパーコンポーネントを作成する

現在のロケールを読み取り、ナビゲーションパラメータに自動的に含めるコンポーネントを構築します。

import { Link, LinkProps, useParams } from "@tanstack/react-router";

type LocaleLinkProps = LinkProps & {
  to: string;
};

export function LocaleLink(props: LocaleLinkProps) {
  const params = useParams({ strict: false });
  const currentLocale = params.locale;

  const enhancedParams = {
    ...props.params,
    ...(currentLocale && { locale: currentLocale }),
  };

  return <Link {...props} params={enhancedParams} />;
}

このコンポーネントはuseParamsstrict: falseを使用してアプリケーション内の任意のルートからパラメータにアクセスし、現在のlocaleを抽出して、基盤となるLinkに渡されるparamsプロパティにマージします。スプレッド演算子により、明示的に提供されたパラメータが優先されます。

アプリケーション全体で標準のLinkコンポーネントをLocaleLinkに置き換えます。

import { createFileRoute } from "@tanstack/react-router";
import { LocaleLink } from "../components/LocaleLink";

export const Route = createFileRoute("/{-$locale}/products")({
  component: ProductsPage,
});

function ProductsPage() {
  return (
    <div>
      <h1>Products</h1>
      <nav>
        <LocaleLink to="/{-$locale}/about">About</LocaleLink>
        <LocaleLink to="/{-$locale}/contact">Contact</LocaleLink>
        <LocaleLink to="/{-$locale}/products/$id" params={{ id: "123" }}>
          Product 123
        </LocaleLink>
      </nav>
    </div>
  );
}

ユーザーが/fr/productsにアクセスすると、すべてのLocaleLinkコンポーネントは自動的に/fr/about/fr/contact/fr/products/123に解決されます。ロケールパラメータは各リンク箇所で手動介入することなく保持されます。

3. 必要に応じてロケール切り替えを明示的に処理する

言語切り替えコンポーネントの場合は、標準のLinkを直接使用して自動ロケール注入をバイパスします。

import { Link, useParams } from "@tanstack/react-router";

export function LanguageSwitcher() {
  const params = useParams({ strict: false });
  const currentPath = window.location.pathname.replace(/^\/(en|fr|es)/, "");

  return (
    <div>
      <Link to={`/{-$locale}${currentPath}`} params={{ locale: "en" }}>
        English
      </Link>
      <Link to={`/{-$locale}${currentPath}`} params={{ locale: "fr" }}>
        Français
      </Link>
      <Link to={`/{-$locale}${currentPath}`} params={{ locale: "es" }}>
        Español
      </Link>
    </div>
  );
}

言語切り替えにはロケールパラメータの明示的な制御が必要なため、標準のLinkコンポーネントを使用し、localeパラメータを明示的に設定します。これにより、ユーザーは同じ論理ページに留まりながら言語を変更できます。