Cómo traducir metadatos de página en TanStack Start v1

Traducir metadatos para búsqueda y redes sociales

Problema

Los metadatos de la página—títulos y descripciones—aparecen en las pestañas del navegador, marcadores, resultados de búsqueda y vistas previas en redes sociales. Cuando el idioma de los metadatos no coincide con el idioma del contenido de la página, los usuarios experimentan una inconsistencia desconcertante incluso antes de ver la página. Una página en español con un título en inglés en los resultados de búsqueda indica una mala calidad de localización. Los motores de búsqueda pueden interpretar esta discrepancia como una señal de clasificación, potencialmente reduciendo la visibilidad en los resultados de búsqueda específicos del idioma.

Esta desconexión socava la experiencia del usuario en la etapa de descubrimiento. Los usuarios que buscan en su idioma preferido esperan que los metadatos coincidan, y la inconsistencia erosiona la confianza antes del primer clic.

Solución

Traduce los metadatos de la página utilizando los mismos recursos de traducción que el contenido de la página. Define una función head en tu configuración de ruta que acceda a las cadenas traducidas y devuelva metadatos de título y descripción localizados. Esto asegura la consistencia entre lo que aparece en la interfaz del navegador, los resultados de búsqueda y la página renderizada.

Al utilizar el formato de mensajes de react-intl dentro de la función head, los metadatos se mantienen sincronizados con tu flujo de trabajo de traducción y se actualizan automáticamente cuando cambia el idioma.

Pasos

1. Crear un helper para formatear mensajes fuera de los componentes React

La función head se ejecuta fuera del árbol de componentes de React donde los hooks no están disponibles. Crea una utilidad que formatee mensajes usando createIntl de 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);
}

Este helper crea una instancia de intl bajo demanda para usar en contextos que no son de React, como la función head.

2. Define las claves de traducción de metadatos

Añade claves de título y descripción a tus archivos de traducción para cada página que necesite metadatos localizados.

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",
};

Estas claves siguen el mismo patrón que tus traducciones de componentes, manteniendo todo el contenido localizado en un solo lugar.

3. Añade una función head a tu ruta

Utiliza la opción head en createFileRoute para devolver metadatos traducidos. Accede al idioma actual desde los parámetros de ruta o datos del cargador, luego formatea los mensajes utilizando tu helper.

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>;
}

La función head se ejecuta durante la coincidencia de rutas y devuelve objetos de metadatos que TanStack Start renderiza en el head del documento.

4. Utiliza datos del loader para metadatos dinámicos

Cuando los metadatos dependen de datos obtenidos, accede a loaderData en la función head para combinar contenido dinámico con plantillas traducidas.

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>;
}

El loader obtiene datos antes de que se ejecute la función head, permitiéndote interpolar valores dinámicos en plantillas de metadatos traducidas.