如何在 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",
  },
});
}

该路由作为 API 端点,通过返回 XML 响应实现。索引会为每种语言列出一个站点地图,方便通过更新 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 提供。每种语言的 sitemap 只包含该特定语言的 URL。

3. 为每种语言获取页面

实现辅助函数,从您的数据源中检索指定 locale 的所有页面。

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. 注册 sitemap 路由

将 sitemap 路由添加到您的路由配置文件中。

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 中添加 sitemap 索引

在 public 目录根下的 robots.txt 文件中,指向您的 sitemap 索引以便搜索引擎发现。

User-agent: *
Allow: /

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

一个列出所有 sitemap 的 sitemap 索引可以让搜索引擎通过单一入口发现所有语言的 sitemap。搜索引擎会抓取索引并自动跟踪到每个语言的 sitemap。