Как переводить метаданные страницы в Next.js (Pages Router) v16
Перевод метаданных для поиска и соцсетей
Проблема
Метаданные страницы — заголовки и описания — отображаются вне основного контента. Они видны во вкладках браузера, закладках, результатах поиска и превью в соцсетях. Если метаданные не совпадают с языком страницы, пользователь сталкивается с резким несоответствием ещё до просмотра контента. Например, испанская страница с английским заголовком сбивает с толку и говорит о низком качестве локализации.
Поисковые системы воспринимают несоответствие языков как признак плохой локализации, что может понизить позиции в результатах поиска на нужном языке. Социальные сети показывают неправильный язык в превью ссылок, что снижает вовлечённость международной аудитории. Последовательный перевод на всех этапах, включая метаданные, — основа профессионального мультиязычного опыта.
Решение
Переводите метаданные страницы с помощью тех же ресурсов, что и основной контент. Используйте хук react-intl useIntl, чтобы получать переведённые строки и вставлять их в компонент <Head> страницы. Так заголовки и описания будут соответствовать текущей локали во вкладках, поиске и соцсетях.
Если брать метаданные из тех же каталогов сообщений, что и видимый контент, вы сохраняете единообразие и избегаете дублирования переводов. Этот подход работает и для статических страниц, и для динамических маршрутов, где метаданные зависят от данных страницы.
Шаги
1. Добавьте сообщения для метаданных в ваши файлы переводов
Определите дескрипторы сообщений для заголовков и описаний страниц в ваших каталогах переводов, используя ту же структуру, что и для остального контента.
{
"home.title": "Welcome to Our Store",
"home.description": "Discover amazing products at great prices",
"products.title": "Our Products",
"products.description": "Browse our full catalog of items"
}
Для каждой страницы, где нужны переведённые метаданные, должны быть соответствующие идентификаторы сообщений для заголовка и описания.
2. Создайте компонент для перевода метаданных
Используйте хук useIntl, чтобы получить доступ к функции formatMessage, а затем выводите переведённые строки внутри компонента Head.
import Head from "next/head";
import { useIntl } from "react-intl";
export default function HomePage() {
const intl = useIntl();
return (
<>
<Head>
<title>{intl.formatMessage({ id: "home.title" })}</title>
<meta
name="description"
content={intl.formatMessage({ id: "home.description" })}
/>
</Head>
<main>
<h1>{intl.formatMessage({ id: "home.title" })}</h1>
</main>
</>
);
}
Компонент Head — это React-компонент, встроенный в Next.js, который позволяет изменять <head> страницы. Функция formatMessage возвращает переведённую строку для текущей локали.
3. Добавьте Open Graph и социальные метаданные
Расширьте шаблон, чтобы включить метаданные Open Graph и Twitter Card для предпросмотра в соцсетях.
import Head from "next/head";
import { useIntl } from "react-intl";
export default function ProductsPage() {
const intl = useIntl();
const title = intl.formatMessage({ id: "products.title" });
const description = intl.formatMessage({ id: "products.description" });
return (
<>
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
</Head>
<main>
<h1>{title}</h1>
</main>
</>
);
}
Сохраняя отформатированные сообщения в переменных, вы избегаете многократных вызовов formatMessage для одного и того же перевода и делаете JSX чище.
4. Обработка динамических метаданных для параметрических страниц
Для страниц с динамическими маршрутами объединяйте параметры маршрута с переведёнными строками для создания контекстных метаданных.
import Head from "next/head";
import { useIntl } from "react-intl";
import { useRouter } from "next/router";
export default function ProductDetailPage() {
const intl = useIntl();
const router = useRouter();
const { id } = router.query;
const title = intl.formatMessage(
{ id: "product.detail.title" },
{ productId: id },
);
return (
<>
<Head>
<title>{title}</title>
</Head>
<main>
<h1>{title}</h1>
</main>
</>
);
}
Message descriptors поддерживают интерполяцию переменных, что позволяет подставлять динамические значения, такие как ID или имена продуктов, в переведённые строки метаданных. Соответствующее сообщение может быть "Product {productId} - Our Store".