TanStack Start v1で言語切り替えコンポーネントを構築する方法

同じページに留まりながら言語を切り替える

問題

多言語アプリケーションでユーザーが言語を切り替える際、同じページで同じコンテンツを新しい言語で表示し続けることを期待します。不適切に実装された言語切り替え機能は、言語選択を別の目的地へのナビゲーションとして扱い、ユーザーをホームページにリダイレクトして現在位置を完全に失わせることがよくあります。これはユーザーフローを中断させ、特にユーザーがワークフローの深い部分にいる場合や特定のコンテンツを閲覧している場合にフラストレーションを生み出します。課題は、URLの言語セグメントのみを変更し、残りのパス、検索パラメータ、ハッシュを保持する切り替え機能を構築することです。

解決策

現在のURLパス名を読み取り、パス内のロケールセグメントを置き換えることで利用可能な各言語のリンクを構築する言語切り替えコンポーネントを構築します。URL構造から現在のロケールを抽出し、他のすべてのURLセグメントをそのまま保持しながらロケール部分を置換することで、サポートされている他の言語の新しいパスを生成します。これらのパスを使用して、ユーザーが現在のページコンテキストを失うことなく言語を切り替えることができるリンクをレンダリングします。

手順

1. サポートされているロケールを定義する

アプリケーションがサポートするすべての言語をリストし、デフォルトのロケールを識別する設定ファイルを作成します。

export const locales = ["en", "fr", "es", "de"] as const;

export type Locale = (typeof locales)[number];

export const defaultLocale: Locale = "en";

この設定は、利用可能な言語の唯一の信頼できる情報源として機能し、切り替えリンクの生成に使用されます。

2. パス名から現在のロケールを抽出するヘルパーを作成する

パス名を解析し、存在する場合はロケールセグメントを抽出するユーティリティ関数を記述します。

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocaleFromPathname(pathname: string): Locale {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  if (firstSegment && locales.includes(firstSegment as Locale)) {
    return firstSegment as Locale;
  }

  return defaultLocale;
}

この関数は最初のパスセグメントを検査し、サポートされているロケールと一致する場合はそれを返し、それ以外の場合はデフォルトにフォールバックします。

3. ローカライズされたパスを構築するヘルパーを作成する

現在のパス名とターゲットロケールを受け取り、ロケールセグメントを置き換えた新しいパスを構築する関数を記述します。

import { defaultLocale, locales, type Locale } from "./locales";

export function getLocalizedPath(
  pathname: string,
  targetLocale: Locale,
): string {
  const segments = pathname.split("/").filter(Boolean);
  const firstSegment = segments[0];

  const hasLocalePrefix =
    firstSegment && locales.includes(firstSegment as Locale);

  if (hasLocalePrefix) {
    segments[0] = targetLocale;
  } else {
    segments.unshift(targetLocale);
  }

  return "/" + segments.join("/");
}

この関数は既存のロケールセグメントを置き換えるか、ターゲットロケールをパスの先頭に追加し、新しいURLが異なる言語で同じページを指すようにします。

4. 言語切り替えコンポーネントを構築する

現在の場所を使用して、サポートされているすべての言語のリンクを生成するコンポーネントを作成します。

import { Link } from "@tanstack/react-router";
import { useLocation } from "@tanstack/react-router";
import { locales, type Locale } from "./locales";
import { getLocaleFromPathname, getLocalizedPath } from "./locale-helpers";

export function LanguageSwitcher() {
  const location = useLocation();
  const currentLocale = getLocaleFromPathname(location.pathname);

  return (
    <nav>
      <ul>
        {locales.map((locale) => {
          const isActive = locale === currentLocale;
          const localizedPath = getLocalizedPath(location.pathname, locale);

          return (
            <li key={locale}>
              <Link
                to={localizedPath}
                search={location.search}
                hash={location.hash}
                aria-current={isActive ? "page" : undefined}
              >
                {locale.toUpperCase()}
              </Link>
            </li>
          );
        })}
      </ul>
    </nav>
  );
}

このコンポーネントは現在のパス名を読み取り、アクティブなロケールを判定し、現在のページ構造、検索パラメータ、ハッシュフラグメントを保持する各サポート言語のリンクをレンダリングします。ユーザーは同じ論理ページに留まりながら言語を切り替えることができます。