Как переводить метаданные страницы в TanStack Start v1
Переводим метаданные для поиска и соцсетей
Проблема
Метаданные страницы — заголовки и описания — отображаются во вкладках браузера, закладках, результатах поиска и превью в соцсетях. Если язык метаданных не совпадает с языком содержимого страницы, пользователь сталкивается с резким несоответствием ещё до просмотра самой страницы. Например, испанская страница с английским заголовком в поиске выглядит как плохая локализация. Поисковые системы могут расценить это как негативный сигнал и понизить видимость в языковых результатах поиска.
Такое несоответствие портит пользовательский опыт уже на этапе поиска. Пользователи, ищущие на своём языке, ожидают совпадения метаданных, и несогласованность подрывает доверие ещё до первого клика.
Решение
Переводите метаданные страницы с помощью тех же ресурсов, что и основной контент. Определите функцию head в конфигурации маршрута, чтобы получать переведённые строки и возвращать локализованные заголовок и описание. Это обеспечит согласованность между тем, что видит пользователь во вкладке браузера, в поиске и на самой странице.
Используя форматирование сообщений react-intl внутри функции head, вы синхронизируете метаданные с вашим процессом перевода, и они будут автоматически обновляться при смене локали.
Шаги
1. Создайте хелпер для форматирования сообщений вне компонентов React
Функция head работает вне дерева компонентов React, где хуки недоступны. Создайте утилиту, которая форматирует сообщения с помощью createIntl из react-intl.
import { createIntl, createIntlCache } from "react-intl";
const cache = createIntlCache();
export function formatMetadataMessage(
locale: string,
messages: Record<string, string>,
id: string,
values?: Record<string, string | number>,
): string {
const intl = createIntl({ locale, messages }, cache);
return intl.formatMessage({ id }, values);
}
Этот хелпер создаёт экземпляр intl по требованию для использования вне React, например, в функции head.
2. Задайте ключи для перевода метаданных
Добавьте ключи для заголовка и описания в ваши файлы переводов для каждой страницы, где нужны локализованные метаданные.
export const enMessages = {
"page.home.title": "Welcome to Our Site",
"page.home.description": "Discover amazing content in your language",
"page.about.title": "About Us",
"page.about.description": "Learn more about our mission and team",
};
export const esMessages = {
"page.home.title": "Bienvenido a Nuestro Sitio",
"page.home.description": "Descubre contenido increíble en tu idioma",
"page.about.title": "Acerca de Nosotros",
"page.about.description": "Conoce más sobre nuestra misión y equipo",
};
Эти ключи используют тот же шаблон, что и переводы ваших компонентов, чтобы всё локализованное содержимое было в одном месте.
3. Добавьте функцию head в ваш маршрут
Используйте опцию head в createFileRoute, чтобы возвращать переведённые метаданные. Получите текущую локаль из параметров маршрута или данных загрузчика, затем отформатируйте сообщения с помощью вашего помощника.
import { createFileRoute } from "@tanstack/react-router";
import { formatMetadataMessage } from "../utils/formatMetadataMessage";
import { enMessages, esMessages } from "../i18n/messages";
const messagesByLocale = {
en: enMessages,
es: esMessages,
};
export const Route = createFileRoute("/$locale/about")({
head: ({ params }) => {
const locale = params.locale || "en";
const messages = messagesByLocale[locale] || messagesByLocale.en;
return {
meta: [
{
title: formatMetadataMessage(locale, messages, "page.about.title"),
},
{
name: "description",
content: formatMetadataMessage(
locale,
messages,
"page.about.description",
),
},
],
};
},
component: AboutPage,
});
function AboutPage() {
return <div>About content</div>;
}
Функция head выполняется при сопоставлении маршрута и возвращает объекты метаданных, которые TanStack Start вставляет в head документа.
4. Используйте данные загрузчика для динамических метаданных
Когда метаданные зависят от полученных данных, получите loaderData в функции head, чтобы объединить динамический контент с переведёнными шаблонами.
import { createFileRoute } from "@tanstack/react-router";
import { formatMetadataMessage } from "../utils/formatMetadataMessage";
import { enMessages, esMessages } from "../i18n/messages";
const messagesByLocale = {
en: enMessages,
es: esMessages,
};
export const Route = createFileRoute("/$locale/posts/$postId")({
loader: async ({ params }) => {
const post = await fetchPost(params.postId);
return { post };
},
head: ({ params, loaderData }) => {
const locale = params.locale || "en";
const messages = messagesByLocale[locale] || messagesByLocale.en;
const { post } = loaderData;
return {
meta: [
{
title: formatMetadataMessage(locale, messages, "page.post.title", {
title: post.title,
}),
},
{
name: "description",
content: post.excerpt,
},
],
};
},
component: PostPage,
});
function PostPage() {
const { post } = Route.useLoaderData();
return <article>{post.content}</article>;
}
Загрузчик получает данные до запуска функции head, что позволяет подставлять динамические значения в шаблоны переведённых метаданных.