TanStack Start v1でURLのロケールパラメータを検証する方法

サポートされていないロケールコードを適切に処理する

問題

言語コードがURL構造の一部になると、それはバリデーションが必要なユーザー入力に変わります。ユーザーは手動でロケールセグメントに任意の文字列を入力できます—/xx/about/gibberish/contact、または/typo123/products—これは/en/about/fr/contactのような有効なコードと同じくらい簡単です。バリデーションがなければ、アプリケーションは存在しないロケールの翻訳を読み込もうとしたり、壊れたコンテンツを表示したり、クラッシュしたりする可能性があります。無効なロケールはそれぞれ、ユーザーが回復できない、または機能するページにナビゲートできない潜在的な行き止まりを表します。

バリデーションされていないロケールパラメータは予測不可能な動作を引き起こします。アプリケーションは翻訳の読み込みに静かに失敗したり、フォールバックと欠落したコンテンツが混在して表示されたり、翻訳キーにアクセスする際にランタイムエラーをスローしたりする可能性があります。壊れたリンクをたどったり、URLを誤入力したりするユーザーは、何が間違っているのか、どうやって修正するのかについての明確なフィードバックがないまま取り残されます。

解決策

ルートのbeforeLoad関数内で、URLからのロケールパラメータをサポートされているロケールのリストと照合してバリデーションします。ロケールが無効または欠落している場合、デフォルトロケールを持つ有効なURLにユーザーをリダイレクトするか、役立つエラーページを表示するためにnot-foundエラーをスローします。これにより、サポートされているロケールのみが処理され、ユーザーは常に有効で翻訳可能なページに到達することが保証されます。

beforeLoad関数はルートが読み込まれる前に実行されるため、ロケールをチェックするのに理想的な場所です。redirect()またはnotFound()エラーをスローすることで、無効なロケールでルートがレンダリングされるのを防ぎ、ユーザーを機能する状態に導きます。

ステップ

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

有効なロケールコードの定数配列と型安全なバリデーション関数を作成します。

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

type Locale = (typeof SUPPORTED_LOCALES)[number];

function isValidLocale(locale: string | undefined): locale is Locale {
  return SUPPORTED_LOCALES.includes(locale as Locale);
}

これにより、サポートされているロケールの単一の信頼できる情報源と、TypeScriptが理解できる再利用可能なバリデーション関数が提供されます。

2. ロケール検証を含むレイアウトルートを作成する

オプションのロケールパラメータを使用し、beforeLoadで検証します。

import { createFileRoute, redirect } from "@tanstack/react-router";

const DEFAULT_LOCALE: Locale = "en";

export const Route = createFileRoute("/{-$locale}")({
  beforeLoad: ({ params }) => {
    const { locale } = params;

    if (locale && !isValidLocale(locale)) {
      throw redirect({
        to: "/{-$locale}",
        params: { locale: undefined },
        replace: true,
      });
    }

    return {
      locale: (locale as Locale) || DEFAULT_LOCALE,
    };
  },
});

beforeLoad関数はロケールパラメータをチェックします。存在するが無効な場合、ユーザーはロケールプレフィックスなしの同じパスにリダイレクトされ、デフォルトロケールに解決されます。検証されたロケールは子ルートが使用するためにコンテキストで返されます。

3. ネストされたルートでロケールを検証する

ロケールを必要とするルートでは、それを検証し、無効な場合はnotFound()をスローします。

import { createFileRoute, notFound } from "@tanstack/react-router";

export const Route = createFileRoute("/{-$locale}/products")({
  beforeLoad: ({ params }) => {
    const { locale } = params;

    if (locale && !isValidLocale(locale)) {
      throw notFound();
    }

    return {
      locale: (locale as Locale) || DEFAULT_LOCALE,
    };
  },
  component: ProductsPage,
});

function ProductsPage() {
  const { locale } = Route.useRouteContext();
  return <div>Products in {locale}</div>;
}

このアプローチでは、無効なロケールに対してリダイレクトする代わりにnot-foundページを表示します。これはURLが不正形式であることを示したい場合に便利で、静かに修正するのではなく明示的に通知します。

4. 無効なロケールに対するnot-foundコンポーネントを追加する

無効なロケールエラーを適切に処理するために、ルートルートにnotFoundComponentを定義します。

import { createRootRoute } from "@tanstack/react-router";

export const Route = createRootRoute({
  notFoundComponent: () => {
    return (
      <div>
        <h1>Page Not Found</h1>
        <p>The language or page you requested does not exist.</p>
        <a href="/">Go to home page</a>
      </div>
    );
  },
});

このコンポーネントはbeforeLoadからnotFound()がスローされたときにレンダリングされ、ユーザーに明確なメッセージと有効なページに戻る方法を提供します。