Как связать альтернативные языковые версии в React Router v7
Свяжите языковые альтернативы для поисковых систем
Проблема
Когда веб-сайт предоставляет один и тот же контент на нескольких языках, поисковые системы сталкиваются с проблемой. Без явных сигналов они рассматривают каждую языковую версию как отдельную, несвязанную страницу. Французский пользователь, выполняющий поиск на французском языке, может увидеть английскую версию выше в результатах, чем французскую, даже если обе версии существуют. Аналогично, английский пользователь может попасть на немецкую страницу. Это происходит потому, что поисковые системы не могут автоматически определить, что /en/about и /fr/about являются переводами друг друга, а не конкурирующими страницами с дублированным контентом.
Эта путаница разделяет авторитет ранжирования между языковыми версиями и ухудшает пользовательский опыт. Поисковым системам необходимы явные метаданные, чтобы понять, какие страницы являются языковыми альтернативами одного и того же контента, чтобы они могли предоставлять соответствующую версию на основе предпочтений языка и местоположения пользователя.
Решение
Добавьте теги ссылки hreflang на каждую страницу, которые перечисляют все доступные языковые версии этого контента. Эти теги используют атрибут rel="alternate", чтобы указать, что связанные страницы являются переводами, а не дубликатами. Каждый тег указывает код языка и ссылается на URL для этой языковой версии.
Объявляя эти отношения в метаданных страницы, вы помогаете поисковым системам понять структуру вашего сайта и предоставлять правильную языковую версию каждому пользователю. Это улучшает релевантность результатов поиска и предотвращает штрафы за дублированный контент.
Шаги
1. Создайте вспомогательную функцию для генерации ссылок hreflang
Атрибут hreflang использует языковые коды ISO 639-1, которые могут быть дополнены региональными кодами ISO 3166-1 Alpha 2. Создайте утилиту, которая генерирует дескрипторы ссылок для всех языковых версий страницы.
type HreflangLink = {
tagName: "link";
rel: "alternate";
hrefLang: string;
href: string;
};
export function buildHreflangLinks(
pathname: string,
locales: string[],
baseUrl: string,
): HreflangLink[] {
return locales.map((locale) => ({
tagName: "link",
rel: "alternate",
hrefLang: locale,
href: `${baseUrl}/${locale}${pathname}`,
}));
}
Эта функция принимает текущий путь, список поддерживаемых локалей и базовый URL вашего сайта, а затем возвращает массив дескрипторов ссылок, которые мета-функция может отобразить.
2. Добавьте ссылку x-default в качестве резервной
Значение hreflang x-default указывает на страницу по умолчанию, если ни одна другая страница не подходит лучше и не нацелена на определённый язык или регион. Добавьте это, чтобы направлять пользователей, чей язык вы не поддерживаете.
export function buildHreflangLinks(
pathname: string,
locales: string[],
baseUrl: string,
defaultLocale: string,
): HreflangLink[] {
const links = locales.map((locale) => ({
tagName: "link",
rel: "alternate",
hrefLang: locale,
href: `${baseUrl}/${locale}${pathname}`,
}));
links.push({
tagName: "link",
rel: "alternate",
hrefLang: "x-default",
href: `${baseUrl}/${defaultLocale}${pathname}`,
});
return links;
}
Ссылка x-default обычно указывает на версию вашего сайта на основном языке и служит резервной для пользователей, чьи языковые предпочтения не совпадают ни с одной из ваших конкретных языковых версий.
3. Экспортируйте ссылки hreflang из вашей функции meta
Функция meta может устанавливать теги link на основе данных. Используйте её для возврата ссылок hreflang для каждого маршрута.
import type { Route } from "./+types/about";
import { buildHreflangLinks } from "~/utils/hreflang";
const SUPPORTED_LOCALES = ["en", "fr", "de", "es"];
const BASE_URL = "https://example.com";
const DEFAULT_LOCALE = "en";
export function meta({ location }: Route.MetaArgs) {
const hreflangLinks = buildHreflangLinks(
location.pathname,
SUPPORTED_LOCALES,
BASE_URL,
DEFAULT_LOCALE,
);
return [
{ title: "О нас" },
{ name: "description", content: "Узнайте о нашей компании" },
...hreflangLinks,
];
}
Функция meta возвращает массив дескрипторов, которые могут включать объекты с tagName, установленным в "link". React Router рендерит их как элементы link в заголовке документа.
4. Убедитесь, что компонент Meta находится в вашем корневом макете
Компонент Meta рендерит все мета-теги, созданные экспортом meta модуля маршрута, и должен находиться внутри заголовка вашего документа. Убедитесь, что ваш корневой макет включает его.
import { Links, Meta, Outlet, Scripts } from "react-router";
export default function Root() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
);
}
Компонент Meta агрегирует и рендерит все мета-дескрипторы из совпадающего маршрута, включая теги ссылок hreflang, которые вы определили на шаге 3.
5. Адаптируйте путь для маршрутов с префиксом локали
Если ваши маршруты включают локаль в пути (например, /en/about), удалите её перед созданием hreflang-ссылок, чтобы все языковые версии указывали на одну и ту же логическую страницу.
export function meta({ location }: Route.MetaArgs) {
const pathWithoutLocale = location.pathname.replace(/^\/[a-z]{2}(\/|$)/, "/");
const hreflangLinks = buildHreflangLinks(
pathWithoutLocale,
SUPPORTED_LOCALES,
BASE_URL,
DEFAULT_LOCALE,
);
return [{ title: "About Us" }, ...hreflangLinks];
}
Это гарантирует, что /en/about, /fr/about и /de/about создают hreflang-ссылки, указывающие на правильные URL-адреса для соответствующего языка для одного и того же содержимого.