Comment créer des sitemaps multilingues dans Next.js (Pages Router) v16
Organiser les sitemaps par langue pour la scalabilité
Problème
Les sitemaps aident les moteurs de recherche à découvrir et indexer les pages d'un site web. Pour un site multilingue avec des centaines ou des milliers de pages par langue, un seul sitemap listant chaque URL pour chaque locale devient rapidement ingérable. Les sitemaps monolithiques volumineux peuvent dépasser les limites de 50 000 URL ou 50 Mo définies par le protocole sitemap, les rendant invalides. Même lorsqu'ils restent dans les limites, régénérer et valider un fichier massif à chaque modification de contenu dans une langue est inefficace. À mesure que le site se développe et ajoute plus de langues ou de pages, cette approche ne passe pas à l'échelle.
Solution
Organiser les sitemaps en hiérarchie en utilisant un fichier d'index de sitemap. Le fichier d'index liste des sitemaps spécifiques à chaque langue, chacun contenant les URL d'une seule locale. Cette structure maintient les fichiers sitemap individuels gérables et dans les limites du protocole. Lorsque le contenu change dans une langue, seul le sitemap de cette langue nécessite une régénération. L'approche évolue naturellement à mesure que de nouvelles langues sont ajoutées — chacune obtient son propre sitemap référencé dans l'index. Les moteurs de recherche explorent d'abord l'index, puis suivent les liens vers les sitemaps de langues individuelles.
Étapes
1. Créer une page d'index de sitemap
Créer une page dans le répertoire pages pour générer l'index de sitemap dynamiquement en utilisant getServerSideProps.
import { GetServerSideProps } from "next";
const SITE_URL = "https://example.com";
const LOCALES = ["en", "es", "fr", "de"];
function generateSitemapIndex(locales: string[]): string {
const sitemapEntries = locales
.map((locale) => {
return `
<sitemap>
<loc>${SITE_URL}/sitemap-${locale}.xml</loc>
<lastmod>${new Date().toISOString()}</lastmod>
</sitemap>`;
})
.join("");
return `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${sitemapEntries}
</sitemapindex>`;
}
export const getServerSideProps: GetServerSideProps = async ({ res }) => {
const sitemap = generateSitemapIndex(LOCALES);
res.setHeader("Content-Type", "text/xml");
res.write(sitemap);
res.end();
return {
props: {},
};
};
export default function SitemapIndex() {}
L'index utilise l'élément racine <sitemapindex> avec des entrées <sitemap>, chacune contenant un enfant <loc> pointant vers un sitemap spécifique à une langue. La fonction getServerSideProps définit l'en-tête Content-Type sur text/xml et écrit la réponse XML directement.
2. Créer des pages de sitemap spécifiques à chaque langue
Créez une page de route dynamique pour générer des sitemaps individuels pour chaque langue.
import { GetServerSideProps } from "next";
const SITE_URL = "https://example.com";
interface PageData {
slug: string;
lastModified: string;
}
async function getPagesByLocale(locale: string): Promise<PageData[]> {
return [
{ slug: "about", lastModified: "2024-01-15" },
{ slug: "contact", lastModified: "2024-01-20" },
];
}
function generateSitemap(locale: string, pages: PageData[]): string {
const urlEntries = pages
.map((page) => {
return `
<url>
<loc>${SITE_URL}/${locale}/${page.slug}</loc>
<lastmod>${page.lastModified}</lastmod>
</url>`;
})
.join("");
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urlEntries}
</urlset>`;
}
export const getServerSideProps: GetServerSideProps = async ({
params,
res,
}) => {
const locale = params?.locale as string;
const pages = await getPagesByLocale(locale);
const sitemap = generateSitemap(locale, pages);
res.setHeader("Content-Type", "text/xml");
res.write(sitemap);
res.end();
return {
props: {},
};
};
export default function LocaleSitemap() {}
La route dynamique extrait la locale depuis les paramètres d'URL et génère du XML contenant uniquement les URL pour cette langue. Chaque sitemap reste concentré sur le contenu d'une seule locale.
3. Récupérer le contenu spécifique à la locale
Remplacez la fonction placeholder getPagesByLocale par votre source de données réelle.
async function getPagesByLocale(locale: string): Promise<PageData[]> {
const response = await fetch(
`https://api.example.com/pages?locale=${locale}`,
);
const data = await response.json();
return data.pages.map((page: any) => ({
slug: page.slug,
lastModified: page.updatedAt,
}));
}
Cette fonction interroge votre CMS, base de données ou API pour récupérer les pages de la locale spécifiée. Elle renvoie des données structurées que le générateur de sitemap transforme en entrées XML.
4. Ajouter des pages statiques à chaque sitemap
Incluez les routes statiques qui existent dans chaque langue aux côtés du contenu dynamique.
function generateSitemap(locale: string, pages: PageData[]): string {
const staticPages = [
{ slug: "", lastModified: new Date().toISOString() },
{ slug: "about", lastModified: new Date().toISOString() },
];
const allPages = [...staticPages, ...pages];
const urlEntries = allPages
.map((page) => {
const path = page.slug ? `/${locale}/${page.slug}` : `/${locale}`;
return `
<url>
<loc>${SITE_URL}${path}</loc>
<lastmod>${page.lastModified}</lastmod>
</url>`;
})
.join("");
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urlEntries}
</urlset>`;
}
La combinaison de pages statiques et dynamiques garantit que chaque sitemap de langue est complet. Les pages statiques utilisent l'horodatage actuel comme date de dernière modification.
5. Référencer l'index dans robots.txt
Ajoutez l'emplacement de l'index du sitemap à votre fichier robots.txt afin que les moteurs de recherche le découvrent.
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
Vous devez uniquement lister le fichier d'index ; les moteurs de recherche suivront automatiquement les liens vers les sitemaps de langues individuels. Placez ce fichier dans le répertoire public.