So erstellen Sie mehrsprachige Sitemaps in TanStack Start v1
Sitemaps nach Sprache für Skalierbarkeit organisieren
Problem
Sitemaps helfen Suchmaschinen, alle Seiten einer Website zu entdecken und zu crawlen. Eine mehrsprachige Website mit Hunderten oder Tausenden von Seiten pro Sprache kann schnell enorme Sitemaps generieren. Eine einzelne Datei, die jede URL für jede Sprache auflistet, wird unhandlich und kann die im Sitemap-Protokoll definierten Grenzwerte von 50.000 URLs oder 50 MB Dateigröße überschreiten. Wenn Sie die Struktur einer Sprache aktualisieren, müssen Sie die gesamte Datei neu generieren und validieren. Große monolithische Sitemaps sind schwer zu pflegen, langsam zu verarbeiten und skalieren nicht, wenn Sie Sprachen oder Inhalte hinzufügen.
Lösung
Teilen Sie Ihre Sitemap in mehrere Dateien auf und verwenden Sie eine Sitemap-Index-Datei, um viele Sitemaps auf einmal einzureichen. Erstellen Sie einen Sitemap-Index auf oberster Ebene unter /sitemap.xml, der auf separate, sprachspezifische Sitemaps wie /sitemap-en.xml und /sitemap-es.xml verweist. Das XML-Format einer Sitemap-Index-Datei ähnelt einer regulären Sitemap und ist durch das Sitemap-Protokoll definiert. Dies hält einzelne Dateien überschaubar, ermöglicht es Ihnen, jede Sprache unabhängig zu aktualisieren, und skaliert gut, wenn Sie neue Sprachen oder Seiten hinzufügen.
Schritte
1. Erstellen Sie eine Hilfsfunktion zur Generierung von Sitemap-XML
Erstellen Sie eine Hilfsfunktion, die aus einem Array von URL-Einträgen gültiges Sitemap-XML generiert.
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>`
}
Diese Funktion akzeptiert ein Array von URL-Objekten und gibt einen korrekt formatierten XML-String mit dem erforderlichen Namespace und der erforderlichen Struktur zurück.
2. Erstellen Sie eine Hilfsfunktion zur Generierung von Sitemap-Index-XML
Erstellen Sie eine zweite Hilfsfunktion, die einen Sitemap-Index generiert, der auf mehrere untergeordnete Sitemaps verweist.
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>`
}
Der Sitemap-Index verwendet ein <sitemapindex> Root-Tag und enthält einen <sitemap> Eintrag für jede Sitemap, mit einem <loc> Child-Eintrag für jedes Parent-Tag.
3. Definieren Sie eine Server-Route für den Haupt-Sitemap-Index
Erstellen Sie eine Server-Route unter /sitemap.xml, die den Sitemap-Index zurückgibt, der alle sprachspezifischen Sitemaps auflistet.
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",
},
});
},
},
},
});
Diese Route generiert einen Index, der auf eine Sitemap pro Sprache verweist, und stellt ihn als XML mit entsprechenden Caching-Headern bereit.
4. Definieren Sie Server-Routen für sprachspezifische Sitemaps
Erstellen Sie eine dynamische Server-Route, die eine Sitemap für jede Sprache basierend auf dem Locale-Parameter generiert.
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",
},
});
},
},
},
});
Server-Routen unterstützen dynamische Pfadparameter auf die gleiche Weise wie TanStack Router, sodass eine Datei mit dem Namen $locale eine Route erstellt, die einen dynamischen Locale-Parameter akzeptiert. Jede sprachspezifische Sitemap wird unabhängig generiert und kann aktualisiert werden, ohne andere Sprachen zu beeinflussen.
5. Rufen Sie URLs aus Ihrer Datenquelle ab
Ersetzen Sie die Platzhalter-Funktion getUrlsForLocale durch Logik, die tatsächliche URLs aus Ihrer Datenbank, Ihrem CMS oder Ihren Routendefinitionen abruft.
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,
}));
}
Dieses Beispiel fragt eine Datenbank nach veröffentlichten Seiten in der angegebenen Locale ab und ordnet sie Sitemap-Einträgen mit Metadaten zu. Passen Sie die Abfrage- und Mapping-Logik an Ihr Datenmodell und Ihre URL-Struktur an.