React Router v7で多言語サイトマップを作成する方法

スケールのために言語別にサイトマップを整理する

問題

サイトマップは検索エンジンがサイト上のすべてのページを発見してクロールするのに役立ちます。言語ごとに数百または数千ページを持つ多言語サイトでは、すぐに巨大なサイトマップが生成されます。すべての言語のすべてのURLを一つのファイルにリストすると扱いにくくなり、50,000 URLまたは50MBのサイズ制限を超える可能性があり、メンテナンスも困難になります。ある言語の構造を更新すると、ファイル全体を再生成して再検証する必要があります。このアプローチはサイトの成長に伴ってスケールしません。

解決策

サイトマップを階層に分割し、言語ごとの個別のサイトマップを指すトップレベルのサイトマップインデックスを作成します。各言語はその言語のURLのみを含む独自のサイトマップファイルを持ちます。インデックスファイルはディレクトリとして機能し、各言語サイトマップの場所をリストします。これにより個々のファイルは管理しやすくなり、各言語を独立して更新でき、新しい言語やページを追加する際にもうまくスケールします。検索エンジンは最初にインデックスをクロールし、その後各言語サイトマップへのリンクをたどります。

ステップ

1. サイトマップインデックスルートを作成する

すべての言語固有のサイトマップをリストするXMLレスポンスを返すリソースルートを作成します。

import type { Route } from "./+types/sitemap";

const LOCALES = ["en", "es", "fr", "de"];

export function loader({ request }: Route.LoaderArgs) {
const { origin } = new URL(request.url);

const sitemapIndex = `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${LOCALES.map(locale => `  <sitemap>
  <loc>${origin}/sitemap-${locale}.xml</loc>
</sitemap>`).join("
")}
</sitemapindex>`;

return new Response(sitemapIndex, {
  headers: {
    "Content-Type": "application/xml",
  },
});
}

このルートはXMLレスポンスを返すことでAPIエンドポイントとして機能します。インデックスは言語ごとに1つのサイトマップをリストし、LOCALESの配列を更新することで言語の追加や削除が簡単にできます。

2. 言語固有のサイトマップルートを作成する

各言語の個別サイトマップを生成するために、動的セグメントを持つリソースルートを定義します。

import type { Route } from "./+types/sitemap.$locale";

export async function loader({ params }: Route.LoaderArgs) {
const locale = params.locale;
const urls = await getUrlsForLocale(locale);

const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.map(url => `  <url>
  <loc>${url}</loc>
</url>`).join("
")}
</urlset>`;

return new Response(sitemap, {
  headers: {
    "Content-Type": "application/xml",
  },
});
}

async function getUrlsForLocale(locale: string): Promise<string[]> {
return [];
}

ルートパスの動的セグメントは解析され、params.localeとして提供されます。各言語のサイトマップには、その特定のロケールのURLのみが含まれます。

3. 各言語のページを取得する

データソースから特定のロケールのすべてのページを取得するヘルパー関数を実装します。

async function getUrlsForLocale(locale: string): Promise<string[]> {
  const pages = await db.pages.findMany({
    where: { locale, published: true },
    select: { slug: true },
  });

  return pages.map((page) => `https://example.com/${locale}/${page.slug}`);
}

この関数は、リクエストされた言語で公開されているページをデータベースまたはコンテンツソースからクエリし、完全なURLを構築します。クエリとURL構造をアプリケーションに合わせて調整してください。

4. サイトマップルートを登録する

サイトマップルートをルート設定ファイルに追加します。

import { type RouteConfig, route } from "@react-router/dev/routes";

export default [
  route("sitemap.xml", "./routes/sitemap.tsx"),
  route("sitemap-:locale.xml", "./routes/sitemap.$locale.tsx"),
] satisfies RouteConfig;

各ルートには、一致するURLパターンとルートモジュールへのファイルパスがあります。インデックスルートは/sitemap.xmlで応答し、言語ルートは/sitemap-en.xml/sitemap-es.xmlなどで応答します。

5. robots.txtにサイトマップインデックスを追加する

公開ディレクトリのルートにあるrobots.txtファイルで検索エンジンにサイトマップインデックスを指示します。

User-agent: *
Allow: /

Sitemap: https://example.com/sitemap.xml

それぞれのサイトマップを一覧表示するサイトマップインデックスにより、検索エンジンは単一のエントリポイントからすべての言語固有のサイトマップを発見できます。検索エンジンはインデックスをクロールし、各言語サイトマップへのリンクを自動的に辿ります。