Cómo vincular versiones de idiomas alternativos en TanStack Start v1

Vincula alternativas de idioma para motores de búsqueda

Problema

Cuando un sitio web ofrece el mismo contenido en varios idiomas, los motores de búsqueda tratan cada versión de idioma como una página separada por defecto. Sin señales explícitas que conecten estas versiones, los motores de búsqueda no pueden entender que /en/about y /fr/about son traducciones del mismo contenido en lugar de duplicados que compiten. Esta fragmentación divide la autoridad de clasificación entre las versiones de idioma y crea problemas de servicio: un usuario francófono podría ver la versión en inglés clasificada más alto en los resultados de búsqueda, aunque exista una traducción al francés. Los motores de búsqueda necesitan metadatos explícitos para comprender la relación entre las variantes de idioma y servir la versión apropiada según las preferencias de idioma y ubicación del usuario.

Solución

Añade elementos de enlace hreflang al encabezado del documento que declaren todas las versiones de idioma disponibles de cada página. Estos enlaces indican a los motores de búsqueda qué URLs contienen el mismo contenido en diferentes idiomas, permitiéndoles consolidar las señales de clasificación y servir la versión correcta a los usuarios. Cada página enumera todas sus alternativas de idioma, incluyendo una referencia a sí misma, creando una relación bidireccional que los motores de búsqueda utilizan para comprender la estructura de traducción. Estos metadatos se añaden por ruta utilizando el sistema de gestión de encabezados del framework, que tiene acceso a los parámetros de ruta actuales necesarios para construir URLs para todas las variantes de idioma.

Pasos

1. Crea una función auxiliar para construir URLs alternativas de idioma

El método buildLocation del enrutador construye URLs completas a partir de parámetros de ruta, que utilizarás para generar URLs para cada versión de idioma.

import { AnyRouter } from "@tanstack/react-router";

export function buildLanguageAlternates(
  router: AnyRouter,
  currentPath: string,
  currentLang: string,
  availableLanguages: string[],
) {
  return availableLanguages.map((lang) => {
    const location = router.buildLocation({
      to: currentPath,
      params: { lang },
    });
    return {
      lang,
      href: `${location.pathname}${location.search}${location.hash}`,
    };
  });
}

Esta función toma la ruta actual y genera URLs alternativas sustituyendo cada código de idioma en los parámetros de ruta.

2. Define tus idiomas disponibles

Crea un archivo de configuración que liste todos los idiomas que tu aplicación soporta.

export const AVAILABLE_LANGUAGES = ["en", "fr", "de", "es"];

export const DEFAULT_LANGUAGE = "en";

Esta lista centralizada se utilizará para generar enlaces hreflang para cada página.

3. Añade enlaces hreflang en la función head de una ruta

La función head recibe contexto incluyendo matches, params y loaderData, lo que proporciona acceso al parámetro de idioma actual y a la instancia del router.

import { createFileRoute } from "@tanstack/react-router";
import { buildLanguageAlternates, AVAILABLE_LANGUAGES } from "../i18n-config";

export const Route = createFileRoute("/$lang/about")({
  head: ({ params }) => {
    const alternates = buildLanguageAlternates(
      Route.router,
      "/$lang/about",
      params.lang,
      AVAILABLE_LANGUAGES,
    );

    return {
      links: alternates.map((alt) => ({
        rel: "alternate",
        hreflang: alt.lang,
        href: alt.href,
      })),
    };
  },
  component: AboutPage,
});

function AboutPage() {
  return <div>About page content</div>;
}

El atributo hreflang utiliza códigos de idioma ISO 639-1, y cada enlace apunta a la misma página en un idioma diferente.

4. Añade un hreflang x-default para comportamiento de respaldo

El atributo hreflang x-default señala la página predeterminada cuando ningún idioma coincide.

export const Route = createFileRoute("/$lang/about")({
  head: ({ params }) => {
    const alternates = buildLanguageAlternates(
      Route.router,
      "/$lang/about",
      params.lang,
      AVAILABLE_LANGUAGES,
    );

    const defaultUrl = alternates.find((alt) => alt.lang === "en");

    return {
      links: [
        ...alternates.map((alt) => ({
          rel: "alternate",
          hreflang: alt.lang,
          href: alt.href,
        })),
        {
          rel: "alternate",
          hreflang: "x-default",
          href: defaultUrl?.href || alternates[0].href,
        },
      ],
    };
  },
  component: AboutPage,
});

El enlace x-default proporciona una URL de respaldo para usuarios cuyas preferencias de idioma no coinciden con ninguna alternativa declarada.

5. Aplica a rutas dinámicas con parámetros

Para rutas con segmentos dinámicos adicionales más allá del idioma, incluye esos parámetros al construir las alternativas.

export const Route = createFileRoute("/$lang/posts/$postId")({
  head: ({ params }) => {
    const alternates = AVAILABLE_LANGUAGES.map((lang) => {
      const location = Route.router.buildLocation({
        to: "/$lang/posts/$postId",
        params: { lang, postId: params.postId },
      });
      return {
        lang,
        href: `${location.pathname}${location.search}${location.hash}`,
      };
    });

    return {
      links: [
        ...alternates.map((alt) => ({
          rel: "alternate",
          hreflang: alt.lang,
          href: alt.href,
        })),
        {
          rel: "alternate",
          hreflang: "x-default",
          href:
            alternates.find((a) => a.lang === "en")?.href || alternates[0].href,
        },
      ],
    };
  },
});

Cada página debe referenciar todas sus versiones de idioma, incluyéndose a sí misma, asegurando que los motores de búsqueda comprendan el conjunto completo de traducciones para ese contenido.

6. Verifica la implementación de hreflang

La función head devuelve elementos link que son renderizados por el componente HeadContent. Inspecciona el HTML renderizado para confirmar que los enlaces aparecen en el head del documento.

<link rel="alternate" hreflang="en" href="/en/about" />
<link rel="alternate" hreflang="fr" href="/fr/about" />
<link rel="alternate" hreflang="de" href="/de/about" />
<link rel="alternate" hreflang="es" href="/es/about" />
<link rel="alternate" hreflang="x-default" href="/en/about" />

Los enlaces bidireccionales entre páginas aseguran que los motores de búsqueda comprendan la relación entre versiones localizadas, permitiéndoles servir la versión más apropiada a cada usuario.