Как создать многоязычные карты сайта в TanStack Start v1
Организация карт сайта по языкам для масштабирования
Проблема
Карты сайта помогают поисковым системам находить и сканировать все страницы на сайте. Многоязычный сайт с сотнями или тысячами страниц на каждом языке может быстро создавать огромные карты сайта. Один файл, содержащий все URL-адреса для каждого языка, становится громоздким и может превышать ограничения протокола карты сайта в 50 000 URL-адресов или 50 МБ. Когда вы обновляете структуру одного языка, вам нужно заново генерировать и проверять весь файл. Большие монолитные карты сайта сложно поддерживать, они медленно обрабатываются и не масштабируются при добавлении новых языков или контента.
Решение
Разделите вашу карту сайта на несколько файлов и используйте индексный файл карты сайта для отправки множества карт сайта одновременно. Создайте индексный файл карты сайта верхнего уровня по адресу /sitemap.xml, который указывает на отдельные карты сайта для каждого языка, такие как /sitemap-en.xml и /sitemap-es.xml. Формат XML для индексного файла карты сайта похож на обычную карту сайта и определяется протоколом Sitemap. Это позволяет сделать отдельные файлы более управляемыми, обновлять каждый язык независимо и эффективно масштабироваться при добавлении новых языков или страниц.
Шаги
1. Создайте помощник для генерации XML карты сайта
Создайте утилиту, которая генерирует корректный XML карты сайта из массива записей URL.
export function generateSitemapXML(urls: Array<{ loc: string; lastmod?: string; changefreq?: string; priority?: number }>): string {
const entries = urls.map(url => {
let entry = ` <url>
<loc>${url.loc}</loc>`
if (url.lastmod) entry += `
<lastmod>${url.lastmod}</lastmod>`
if (url.changefreq) entry += `
<changefreq>${url.changefreq}</changefreq>`
if (url.priority !== undefined) entry += `
<priority>${url.priority}</priority>`
entry += `
</url>`
return entry
}).join('
')
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${entries}
</urlset>`
}
Эта функция принимает массив объектов URL и возвращает правильно отформатированную строку XML с необходимым пространством имен и структурой.
2. Создайте вспомогательную функцию для генерации XML индекса карты сайта
Создайте вторую утилитарную функцию, которая генерирует индекс карты сайта, указывающий на несколько дочерних карт сайта.
export function generateSitemapIndexXML(sitemaps: Array<{ loc: string; lastmod?: string }>): string {
const entries = sitemaps.map(sitemap => {
let entry = ` <sitemap>
<loc>${sitemap.loc}</loc>`
if (sitemap.lastmod) entry += `
<lastmod>${sitemap.lastmod}</lastmod>`
entry += `
</sitemap>`
return entry
}).join('
')
return `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${entries}
</sitemapindex>`
}
Индекс карты сайта использует корневой тег <sitemapindex> и включает запись <sitemap> для каждой карты сайта с дочерним элементом <loc> для каждого родительского тега.
3. Определите серверный маршрут для основного индекса карты сайта
Создайте серверный маршрут по адресу /sitemap.xml, который возвращает индекс карты сайта, перечисляющий все карты сайта для конкретных языков.
import { createFileRoute } from "@tanstack/react-router";
import { generateSitemapIndexXML } from "~/utils/sitemap";
const SUPPORTED_LOCALES = ["en", "es", "fr", "de"];
const BASE_URL = "https://example.com";
export const Route = createFileRoute("/sitemap")({
server: {
handlers: {
GET: async () => {
const sitemaps = SUPPORTED_LOCALES.map((locale) => ({
loc: `${BASE_URL}/sitemap-${locale}.xml`,
lastmod: new Date().toISOString().split("T")[0],
}));
const xml = generateSitemapIndexXML(sitemaps);
return new Response(xml, {
headers: {
"Content-Type": "application/xml",
"Cache-Control": "public, max-age=3600",
},
});
},
},
},
});
Этот маршрут генерирует индекс, который указывает на одну карту сайта для каждого языка, и предоставляет его в формате XML с соответствующими заголовками кэширования.
4. Определите серверные маршруты для карт сайта, специфичных для языка
Создайте динамический серверный маршрут, который генерирует карту сайта для каждого языка на основе параметра локали.
import { createFileRoute } from "@tanstack/react-router";
import { generateSitemapXML } from "~/utils/sitemap";
const BASE_URL = "https://example.com";
async function getUrlsForLocale(locale: string) {
return [
{ loc: `${BASE_URL}/${locale}`, changefreq: "daily", priority: 1.0 },
{
loc: `${BASE_URL}/${locale}/about`,
changefreq: "monthly",
priority: 0.8,
},
{
loc: `${BASE_URL}/${locale}/contact`,
changefreq: "monthly",
priority: 0.8,
},
];
}
export const Route = createFileRoute("/sitemap-$locale")({
server: {
handlers: {
GET: async ({ params }) => {
const { locale } = params;
const urls = await getUrlsForLocale(locale);
const xml = generateSitemapXML(urls);
return new Response(xml, {
headers: {
"Content-Type": "application/xml",
"Cache-Control": "public, max-age=3600",
},
});
},
},
},
});
Серверные маршруты поддерживают динамические параметры пути так же, как и TanStack Router, поэтому файл с именем $locale создает маршрут, который принимает динамический параметр локали. Каждая карта сайта для конкретного языка генерируется независимо и может быть обновлена без влияния на другие языки.
5. Получение URL-адресов из вашего источника данных
Замените функцию-заглушку getUrlsForLocale на логику, которая извлекает реальные URL-адреса из вашей базы данных, CMS или определений маршрутов.
async function getUrlsForLocale(locale: string) {
const pages = await db.page.findMany({
where: { locale, published: true },
select: { slug: true, updatedAt: true },
});
return pages.map((page) => ({
loc: `${BASE_URL}/${locale}/${page.slug}`,
lastmod: page.updatedAt.toISOString().split("T")[0],
changefreq: "weekly",
priority: 0.7,
}));
}
Этот пример выполняет запрос к базе данных для опубликованных страниц на указанном языке и преобразует их в записи карты сайта с метаданными. Настройте запрос и логику преобразования в соответствии с вашей моделью данных и структурой URL-адресов.