При разработке многоязычных веб-приложений недостаточно просто
перевести интерфейс через I18next. Поисковые системы должны понимать,
какая языковая версия страницы предназначена для конкретной аудитории.
Для решения этой задачи используется атрибут hreflang.
hreflang сообщает поисковым роботам:
Корректная настройка позволяет избежать проблем с дублированием контента и помогает поисковым системам показывать пользователям нужную локализованную версию сайта.
I18next отвечает за локализацию приложения:
Однако сама библиотека не занимается SEO-настройками. После
определения текущего языка необходимо дополнительно формировать
соответствующие метаданные страницы, включая атрибуты
hreflang.
Типичная схема работы выглядит следующим образом:
<head> страницы генерируются ссылки с
атрибутами hreflang.Базовый вариант:
<link rel="alternate" hreflang="en" href="https://example.com/en/" />
<link rel="alternate" hreflang="ru" href="https://example.com/ru/" />
Здесь указываются две версии одной страницы:
Если требуется разделение по регионам:
<link rel="alternate" hreflang="en-US" href="https://example.com/us/" />
<link rel="alternate" hreflang="en-GB" href="https://example.com/uk/" />
В этом случае:
en-US — английский язык для США;en-GB — английский язык для Великобритании.I18next использует языковые идентификаторы на основе стандарта BCP
47, поэтому они хорошо сочетаются с hreflang.
Примеры:
| Код | Описание |
|---|---|
| ru | Русский |
| en | Английский |
| de | Немецкий |
| fr | Французский |
| es | Испанский |
| kk | Казахский |
Региональные варианты:
| Код | Описание |
|---|---|
| en-US | США |
| en-GB | Великобритания |
| fr-CA | Канадский французский |
| pt-BR | Бразильский португальский |
| zh-CN | Китай |
| zh-TW | Тайвань |
Если в конфигурации I18next используются такие локали:
i18next.init({
supportedLngs: [
'en-US',
'en-GB',
'ru',
'kk'
]
});
те же значения могут использоваться и в hreflang.
Для страницы по умолчанию рекомендуется использовать специальное
значение x-default.
Пример:
<link rel="alternate" hreflang="en" href="https://example.com/en/" />
<link rel="alternate" hreflang="ru" href="https://example.com/ru/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/" />
Такая страница может:
Во многих проектах список локалей уже хранится в настройках I18next.
Например:
const supportedLanguages = [
'en',
'ru',
'de'
];
На основе этого массива можно автоматически формировать теги:
function generateHreflangTags(languages, currentPath) {
return languages.map(lang => ({
hreflang: lang,
href: `https://example.com/${lang}${currentPath}`
}));
}
Результат:
[
{
hreflang: 'en',
href: 'https://example.com/en/products'
},
{
hreflang: 'ru',
href: 'https://example.com/ru/products'
},
{
hreflang: 'de',
href: 'https://example.com/de/products'
}
]
Такой подход позволяет избежать ручного сопровождения большого количества страниц.
В React-проектах часто применяется библиотека React Helmet или её современные аналоги.
Пример:
import { Helmet } from 'react-helmet';
function SeoLinks() {
return (
<Helmet>
<link
rel="alternate"
hreflang="en"
href="https://example.com/en/"
/>
<link
rel="alternate"
hreflang="ru"
href="https://example.com/ru/"
/>
</Helmet>
);
}
При интеграции с I18next:
import i18next from 'i18next';
const currentLanguage = i18next.language;
можно динамически строить URL для альтернативных языков.
Конфигурация:
const supportedLngs = [
'en',
'ru',
'de'
];
Генерация ссылок:
<Helmet>
{supportedLngs.map(lang => (
<link
key={lang}
rel="alternate"
hreflang={lang}
href={`https://example.com/${lang}/about`}
/>
))}
</Helmet>
После рендеринга получится:
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="ru" href="https://example.com/ru/about" />
<link rel="alternate" hreflang="de" href="https://example.com/de/about" />
В приложениях Next.js локализация обычно описывается следующим образом:
module.exports = {
i18n: {
locales: ['en', 'ru', 'de'],
defaultLocale: 'en'
}
};
На основе этих настроек легко сформировать набор ссылок:
import Head from 'next/head';
function HreflangLinks() {
const locales = ['en', 'ru', 'de'];
return (
<Head>
{locales.map(locale => (
<link
key={locale}
rel="alternate"
hrefLang={locale}
href={`https://example.com/${locale}`}
/>
))}
</Head>
);
}
Так как локали уже используются системой маршрутизации Next.js, риск расхождения URL и переводов существенно снижается.
Допустим, существуют страницы товаров:
/en/products/123
/ru/products/123
/de/products/123
Для каждой версии необходимо указывать полный набор альтернатив:
<link rel="alternate"
hreflang="en"
href="https://example.com/en/products/123" />
<link rel="alternate"
hreflang="ru"
href="https://example.com/ru/products/123" />
<link rel="alternate"
hreflang="de"
href="https://example.com/de/products/123" />
Важно, чтобы все версии ссылались друг на друга.
Поисковые системы ожидают двустороннюю связь между локализованными страницами.
Если английская версия содержит:
<link rel="alternate"
hreflang="ru"
href="https://example.com/ru/about" />
то русская версия должна содержать:
<link rel="alternate"
hreflang="en"
href="https://example.com/en/about" />
Отсутствие взаимности может привести к игнорированию части атрибутов.
Частая ошибка — попытка сделать все локализованные страницы каноническими относительно одной версии.
Неправильно:
<link rel="canonical"
href="https://example.com/en/about" />
на русской странице.
Правильный вариант:
<link rel="canonical"
href="https://example.com/ru/about" />
Каждая локализованная страница должна иметь собственный canonical URL
и одновременно набор альтернативных hreflang.
Пример:
<link rel="canonical"
href="https://example.com/ru/about" />
<link rel="alternate"
hreflang="en"
href="https://example.com/en/about" />
<link rel="alternate"
hreflang="ru"
href="https://example.com/ru/about" />
При наличии единой схемы URL можно использовать функцию:
function getAlternateLinks(path, locales) {
return locales.map(locale => ({
locale,
url: `https://example.com/${locale}${path}`
}));
}
Использование:
const links = getAlternateLinks(
'/contacts',
['en', 'ru', 'de']
);
Результат:
[
{
locale: 'en',
url: 'https://example.com/en/contacts'
},
{
locale: 'ru',
url: 'https://example.com/ru/contacts'
},
{
locale: 'de',
url: 'https://example.com/de/contacts'
}
]
Подобная автоматизация особенно важна для крупных проектов с тысячами страниц.
Поисковые системы позволяют размещать информацию о языковых версиях не только в HTML, но и в sitemap.
Пример:
<url>
<loc>https://example.com/en/about</loc>
<xhtml:link
rel="alternate"
hreflang="ru"
href="https://example.com/ru/about" />
<xhtml:link
rel="alternate"
hreflang="de"
href="https://example.com/de/about" />
</url>
Для больших сайтов такой подход часто оказывается удобнее, чем генерация большого количества тегов в каждой странице.
Неверно:
<link rel="alternate"
hreflang="russian"
href="https://example.com/ru/" />
Правильно:
<link rel="alternate"
hreflang="ru"
href="https://example.com/ru/" />
Ошибка:
<link rel="alternate"
hreflang="de"
href="https://example.com/de/about" />
если страница фактически отсутствует и возвращает ошибку 404.
Все альтернативные URL должны быть доступны для индексации.
Например:
supportedLngs: [
'en-US',
'ru-RU'
]
но в hreflang указано:
hreflang="en"
hreflang="ru"
Технически это допустимо, однако рекомендуется использовать единообразную схему локалей во всём приложении.
Неверно:
<link rel="alternate"
hreflang="en"
href="https://example.com/en/about" />
на английской странице отсутствует ссылка на саму себя.
Правильно:
<link rel="alternate"
hreflang="en"
href="https://example.com/en/about" />
<link rel="alternate"
hreflang="ru"
href="https://example.com/ru/about" />
Каждая версия должна участвовать в общем наборе альтернатив.
Распространённая структура:
const locales = [
'en',
'ru',
'de',
'kk'
];
const baseUrl = 'https://example.com';
Функция генерации:
function buildHreflangs(path) {
return locales.map(locale => ({
hreflang: locale,
href: `${baseUrl}/${locale}${path}`
}));
}
Использование:
buildHreflangs('/blog/article-1');
Результат:
[
{
hreflang: 'en',
href: 'https://example.com/en/blog/article-1'
},
{
hreflang: 'ru',
href: 'https://example.com/ru/blog/article-1'
},
{
hreflang: 'de',
href: 'https://example.com/de/blog/article-1'
},
{
hreflang: 'kk',
href: 'https://example.com/kk/blog/article-1'
}
]
Такой подход обеспечивает синхронизацию между:
При грамотной интеграции I18next и hreflang многоязычное
приложение становится понятным как пользователям, так и поисковым
роботам, обеспечивая корректное отображение локализованных страниц в
результатах поиска для разных стран и языковых аудиторий.