Как перевести метаданные страницы в React Router v7

Перевод метаданных для поиска и социальных сетей

Проблема

Метаданные страницы — заголовки и описания — отображаются за пределами самой страницы: в вкладках браузера, закладках, результатах поиска и предварительных просмотрах в социальных сетях. Когда эти метаданные не соответствуют языку страницы, это создает резкий диссонанс. Испанская страница с английским заголовком сбивает пользователей с толку еще до того, как они увидят содержимое. Поисковые системы интерпретируют это несоответствие как сигнал о том, что страница плохо локализована или низкого качества, что может снизить ее рейтинг в языковых результатах поиска. Пользователи могут покинуть страницу еще до ее загрузки, предполагая, что она не на их языке.

Решение

Переведите метаданные страницы, чтобы они соответствовали текущему языку, экспортируя функцию meta из вашего модуля маршрута. Используйте API formatMessage из react-intl с Message Descriptors для перевода строк заголовков и описаний, чтобы метаданные использовали те же ресурсы перевода, что и содержимое страницы. Это обеспечивает согласованность между тем, что отображается во вкладках браузера, результатах поиска и на самой странице.

Шаги

1. Создайте вспомогательную функцию для доступа к intl за пределами компонентов

Объект intl предоставляет функцию formatMessage и может быть доступен через хук useIntl в компонентах или создан напрямую с помощью createIntl в средах вне React. Поскольку функция meta выполняется за пределами дерева компонентов React, создайте вспомогательную функцию, которая создает экземпляр intl из ваших сообщений.

import { createIntl, createIntlCache } from "react-intl";

const cache = createIntlCache();

export function createIntlForLocale(
  locale: string,
  messages: Record<string, string>,
) {
  return createIntl(
    {
      locale,
      messages,
    },
    cache,
  );
}

Этот вспомогательный инструмент создает экземпляр intl, который может форматировать сообщения в любой функции, а не только в компонентах React.

2. Загрузите сообщения в загрузчике родительского маршрута

Загрузчики маршрутов возвращают данные, к которым компоненты получают доступ через свойства loaderData. Загружайте сообщения перевода в родительском маршруте, чтобы они были доступны дочерним маршрутам.

import type { Route } from "./+types/root";

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const locale = url.pathname.split("/")[1] || "en";

  const messages = await import(`../translations/${locale}.json`);

  return {
    locale,
    messages: messages.default,
  };
}

Функция meta получает параметр matches, содержащий данные загрузчика из всех совпадающих маршрутов, что делает данные родительского загрузчика доступными для функций meta дочерних маршрутов.

3. Экспортируйте функцию meta для перевода метаданных

Экспортируйте функцию meta из вашего модуля маршрута, которая возвращает массив объектов описания метаданных. Получите данные родительского загрузчика из matches и используйте ваш intl-хелпер для перевода строк.

import type { Route } from "./+types/product";
import { createIntlForLocale } from "~/utils/intl";

export function meta({ matches }: Route.MetaArgs) {
  const rootMatch = matches.find((match) => match.id === "root");
  const { locale, messages } = rootMatch?.data || {
    locale: "en",
    messages: {},
  };

  const intl = createIntlForLocale(locale, messages);

  return [
    {
      title: intl.formatMessage({
        id: "product.meta.title",
        defaultMessage: "Product Details",
      }),
    },
    {
      name: "description",
      content: intl.formatMessage({
        id: "product.meta.description",
        defaultMessage: "View detailed information about this product",
      }),
    },
  ];
}

Функция formatMessage принимает дескриптор сообщения с id и defaultMessage, возвращая переведенную строку для текущей локали.

4. Добавьте переведенные строки метаданных в файлы сообщений

Добавьте ключи перевода метаданных в файл сообщений каждой локали, чтобы formatMessage мог их найти.

{
  "product.meta.title": "Детали продукта",
  "product.meta.description": "Просмотр подробной информации об этом продукте"
}

Когда пользователи переходят на этот маршрут, компонент Meta в вашем корневом макете рендерит все мета-теги, созданные экспортами meta маршрутов, отображая переведенные заголовки и описания, соответствующие языку страницы.