TanStack Start v1でページメタデータを翻訳する方法

検索とソーシャルメディア向けにメタデータを翻訳

問題

ページメタデータ(タイトルと説明)は、ブラウザのタブ、ブックマーク、検索結果、ソーシャルメディアのプレビューに表示されます。メタデータの言語がページコンテンツの言語と一致しない場合、ユーザーはページを閲覧する前に不快な不一致を経験します。スペイン語のページに英語のタイトルが検索結果に表示されることは、ローカライゼーションの品質が低いことを示します。検索エンジンはこの不一致をランキングシグナルとして解釈し、言語固有の検索結果での可視性を低下させる可能性があります。

この不一致は、発見段階でのユーザーエクスペリエンスを損ないます。ユーザーは好みの言語で検索する際、メタデータが一致することを期待しており、不一致は最初のクリック前に信頼を損ないます。

解決策

ページコンテンツと同じ翻訳リソースを使用してページメタデータを翻訳します。ルート設定でhead関数を定義し、翻訳された文字列にアクセスして、ローカライズされたタイトルと説明のメタデータを返します。これにより、ブラウザのクローム、検索結果、レンダリングされたページに表示される内容の一貫性が保証されます。

head関数内でreact-intlのメッセージフォーマットを使用することで、メタデータは翻訳ワークフローと同期し、ロケールが変更されると自動的に更新されます。

手順

1. Reactコンポーネント外でメッセージをフォーマットするヘルパーを作成

head関数は、フックが使用できないReactコンポーネントツリーの外で実行されます。react-intlのcreateIntlを使用してメッセージをフォーマットするユーティリティを作成します。

import { createIntl, createIntlCache } from "react-intl";

const cache = createIntlCache();

export function formatMetadataMessage(
  locale: string,
  messages: Record<string, string>,
  id: string,
  values?: Record<string, string | number>,
): string {
  const intl = createIntl({ locale, messages }, cache);
  return intl.formatMessage({ id }, values);
}

このヘルパーは、head関数などのReact以外のコンテキストで使用するために、オンデマンドでintlインスタンスを作成します。

2. メタデータ翻訳キーを定義

ローカライズされたメタデータが必要な各ページの翻訳ファイルに、タイトルと説明のキーを追加します。

export const enMessages = {
  "page.home.title": "Welcome to Our Site",
  "page.home.description": "Discover amazing content in your language",
  "page.about.title": "About Us",
  "page.about.description": "Learn more about our mission and team",
};

export const esMessages = {
  "page.home.title": "Bienvenido a Nuestro Sitio",
  "page.home.description": "Descubre contenido increíble en tu idioma",
  "page.about.title": "Acerca de Nosotros",
  "page.about.description": "Conoce más sobre nuestra misión y equipo",
};

これらのキーは、コンポーネントの翻訳と同じパターンに従い、すべてのローカライズされたコンテンツを1か所にまとめます。

3. ルートにhead関数を追加する

createFileRouteheadオプションを使用して、翻訳されたメタデータを返します。ルートパラメータまたはローダーデータから現在のロケールにアクセスし、ヘルパーを使用してメッセージをフォーマットします。

import { createFileRoute } from "@tanstack/react-router";
import { formatMetadataMessage } from "../utils/formatMetadataMessage";
import { enMessages, esMessages } from "../i18n/messages";

const messagesByLocale = {
  en: enMessages,
  es: esMessages,
};

export const Route = createFileRoute("/$locale/about")({
  head: ({ params }) => {
    const locale = params.locale || "en";
    const messages = messagesByLocale[locale] || messagesByLocale.en;

    return {
      meta: [
        {
          title: formatMetadataMessage(locale, messages, "page.about.title"),
        },
        {
          name: "description",
          content: formatMetadataMessage(
            locale,
            messages,
            "page.about.description",
          ),
        },
      ],
    };
  },
  component: AboutPage,
});

function AboutPage() {
  return <div>About content</div>;
}

head関数は、ルートマッチング中に実行され、TanStack Startがドキュメントヘッドにレンダリングするメタデータオブジェクトを返します。

4. 動的メタデータにローダーデータを使用する

メタデータが取得したデータに依存する場合、head関数内でloaderDataにアクセスし、動的コンテンツと翻訳されたテンプレートを組み合わせます。

import { createFileRoute } from "@tanstack/react-router";
import { formatMetadataMessage } from "../utils/formatMetadataMessage";
import { enMessages, esMessages } from "../i18n/messages";

const messagesByLocale = {
  en: enMessages,
  es: esMessages,
};

export const Route = createFileRoute("/$locale/posts/$postId")({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId);
    return { post };
  },
  head: ({ params, loaderData }) => {
    const locale = params.locale || "en";
    const messages = messagesByLocale[locale] || messagesByLocale.en;
    const { post } = loaderData;

    return {
      meta: [
        {
          title: formatMetadataMessage(locale, messages, "page.post.title", {
            title: post.title,
          }),
        },
        {
          name: "description",
          content: post.excerpt,
        },
      ],
    };
  },
  component: PostPage,
});

function PostPage() {
  const { post } = Route.useLoaderData();
  return <article>{post.content}</article>;
}

ローダーはhead関数が実行される前にデータを取得するため、翻訳されたメタデータテンプレートに動的な値を補間できます。