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 パラメータを明示的に設定します。これにより、ユーザーは同じ論理ページにとどまりながら言語を変更することができます。