Как переводить метаданные страницы в 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".