Как создать мультиязычные карты сайта в Next.js (Pages Router) v16
Организуйте карты сайта по языкам для масштабирования
Проблема
Карты сайта помогают поисковым системам находить и индексировать страницы на сайте. Для многоязычного сайта с сотнями или тысячами страниц на каждом языке одна карта сайта, содержащая все URL-адреса для всех локалей, быстро становится неудобной. Большие монолитные карты сайта могут превышать ограничения протокола карты сайта в 50 000 URL-адресов или 50 МБ, что делает их недействительными. Даже если они остаются в пределах ограничений, повторное создание и проверка огромного файла каждый раз, когда контент меняется на одном языке, неэффективны. По мере роста сайта и добавления новых языков или страниц этот подход не масштабируется.
Решение
Организуйте карты сайта в иерархию, используя индексный файл карты сайта. Индексный файл перечисляет отдельные карты сайта для каждого языка, каждая из которых содержит URL-адреса для одной локали. Эта структура позволяет поддерживать отдельные файлы карты сайта в управляемом состоянии и в пределах ограничений протокола. Когда контент меняется на одном языке, требуется обновить только карту сайта для этого языка. Этот подход естественно масштабируется с добавлением новых языков — каждый получает свою собственную карту сайта, указанную в индексе. Поисковые системы сначала сканируют индекс, а затем переходят по ссылкам на отдельные карты сайта для каждого языка.
Шаги
1. Создайте индексную страницу карты сайта
Создайте страницу в каталоге pages, чтобы динамически генерировать индекс карты сайта, используя 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() {}
Индекс использует корневой элемент <sitemapindex> с записями <sitemap>, каждая из которых содержит дочерний элемент <loc>, указывающий на карту сайта для конкретного языка. Функция getServerSideProps устанавливает заголовок Content-Type в text/xml и записывает XML-ответ напрямую.
2. Создайте страницы карты сайта для каждого языка
Создайте динамическую страницу маршрута для генерации индивидуальных карт сайта для каждого языка.
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() {}
Динамический маршрут извлекает локаль из параметров URL и генерирует XML, содержащий только URL-адреса для этого языка. Каждая карта сайта сосредоточена только на содержимом одной локали.
3. Получите контент, специфичный для локали
Замените функцию-заглушку getPagesByLocale на ваш реальный источник данных.
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,
}));
}
Эта функция запрашивает ваш CMS, базу данных или API для получения страниц для указанной локали. Она возвращает структурированные данные, которые генератор карты сайта преобразует в записи XML.
4. Добавьте статические страницы в каждую карту сайта
Добавьте статические маршруты, которые существуют на всех языках, вместе с динамическим контентом.
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>`;
}
Объединение статических и динамических страниц гарантирует, что каждая карта сайта для языка будет полной. Для статических страниц используется текущая временная метка в качестве даты последнего изменения.
5. Укажите индекс в файле robots.txt
Добавьте расположение индекса карты сайта в файл robots.txt, чтобы поисковые системы могли его обнаружить.
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
Вам нужно указать только файл индекса; поисковые системы автоматически перейдут по ссылкам на карты сайта для отдельных языков. Разместите этот файл в директории public.