Как связать альтернативные языковые версии в Next.js (Pages Router) v16
Связывайте языковые альтернативы для поисковых систем
Проблема
Когда сайт предоставляет один и тот же контент на нескольких языках, поисковые системы видят отдельные URL для каждой версии, но не понимают их взаимосвязь. Например, французский пользователь может увидеть англоязычную версию выше в поиске, даже если есть и французская. Аналогично, английская страница и её французский перевод могут восприниматься как конкурирующие дубликаты, а не как согласованные альтернативы. Без явных сигналов, связывающих языковые версии, поисковые системы не могут уверенно показывать наиболее подходящую версию пользователям с учётом их языковых предпочтений, что приводит к размыванию рейтинга и плохому пользовательскому опыту.
Решение
Добавьте элементы link с атрибутами hreflang в head каждой страницы, указав все языковые версии, включая саму страницу. Каждый вариант страницы должен содержать одинаковый набор ссылок на все доступные языковые версии. Такое двустороннее связывание помогает поисковым системам распознавать страницы как переводы, а не дубликаты, и позволяет показывать пользователям нужную языковую версию в зависимости от настроек браузера и поискового запроса.
Шаги
1. Создайте компонент для генерации ссылок на альтернативные языки
Импортируйте компонент Head из next/head, чтобы изменять метаданные страницы. Получите информацию о локали через хук useRouter, чтобы строить ссылки для всех доступных языковых версий.
import Head from "next/head";
import { useRouter } from "next/router";
interface AlternateLinksProps {
path?: string;
}
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, locale: currentLocale, asPath } = router;
const canonicalPath = path || asPath;
if (!locales) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
</Head>
);
}
Компонент перебирает доступные локали из роутера и динамически генерирует элементы link для всех страниц.
2. Добавьте компонент на страницы с переводами
Включите компонент AlternateLinks в каждую страницу, которая существует на нескольких языках.
import AlternateLinks from "@/components/AlternateLinks";
export default function AboutPage() {
return (
<>
<AlternateLinks />
<main>
<h1>About Us</h1>
</main>
</>
);
}
Компонент гарантирует, что на каждой странице есть полный набор альтернативных ссылок, что соответствует требованию, чтобы все варианты ссылались друг на друга.
3. Добавьте самоссылку для текущей локали
На каждой странице должен быть hreflang-тег, указывающий на её собственный язык. Компонент уже делает это, проходя по всем локалям, включая текущую.
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, asPath } = router;
const canonicalPath = path || asPath;
if (!locales) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
</Head>
);
}
Теперь на каждой странице есть hreflang-теги, указывающие как на саму себя, так и на другие версии.
4. Добавьте резервную ссылку x-default
Добавьте ссылку x-default, чтобы указать, какую версию показывать пользователям, если их язык недоступен.
export default function AlternateLinks({ path }: AlternateLinksProps) {
const router = useRouter();
const { locales, defaultLocale, asPath } = router;
const canonicalPath = path || asPath;
if (!locales || !defaultLocale) {
return null;
}
return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${locale}${canonicalPath}`}
/>
))}
<link
rel="alternate"
hrefLang="x-default"
href={`${process.env.NEXT_PUBLIC_SITE_URL}/${defaultLocale}${canonicalPath}`}
/>
</Head>
);
}
Ссылка x-default направляет пользователей, чьи языковые предпочтения не совпадают, на локаль по умолчанию.