Cómo enlazar versiones de idiomas alternativos en React Router v7
Enlaza 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 enfrentan un desafío. Sin señales explícitas, tratan cada versión de idioma como una página separada y no relacionada. Un usuario francés que busca en francés podría ver la versión en inglés clasificada más alta que la versión en francés, aunque ambas existan. De manera similar, un usuario inglés podría llegar a una página en alemán. Esto ocurre porque los motores de búsqueda no pueden determinar automáticamente que /en/about y /fr/about son traducciones entre sí en lugar de páginas competidoras con contenido duplicado.
Esta confusión divide la autoridad de clasificación entre las versiones de idioma y degrada la experiencia del usuario. Los motores de búsqueda necesitan metadatos explícitos para comprender qué páginas son alternativas de idioma del mismo contenido, de modo que puedan servir la versión apropiada según la preferencia de idioma y ubicación de cada usuario.
Solución
Agrega etiquetas de enlace hreflang a cada página que enumeren todas las versiones de idioma disponibles de ese contenido. Estas etiquetas usan el atributo rel="alternate" para señalar que las páginas enlazadas son traducciones, no duplicados. Cada etiqueta especifica un código de idioma y apunta a la URL de esa versión de idioma.
Al declarar estas relaciones en los metadatos de la página, ayudas a los motores de búsqueda a comprender la estructura de tu sitio y servir la versión de idioma correcta a cada usuario. Esto mejora la relevancia de los resultados de búsqueda y previene penalizaciones por contenido duplicado.
Pasos
1. Crea una función auxiliar para construir enlaces hreflang
El atributo hreflang utiliza códigos de idioma ISO 639-1 seguidos opcionalmente por códigos de región ISO 3166-1 Alpha 2. Crea una utilidad que genere descriptores de enlace para todas las versiones de idioma de una página.
type HreflangLink = {
tagName: "link";
rel: "alternate";
hrefLang: string;
href: string;
};
export function buildHreflangLinks(
pathname: string,
locales: string[],
baseUrl: string,
): HreflangLink[] {
return locales.map((locale) => ({
tagName: "link",
rel: "alternate",
hrefLang: locale,
href: `${baseUrl}/${locale}${pathname}`,
}));
}
Esta función toma la ruta actual, una lista de locales admitidos y la URL base de tu sitio, y luego devuelve un array de descriptores de enlaces que la función meta puede renderizar.
2. Añade un enlace de respaldo x-default
El valor hreflang x-default señala una página predeterminada cuando ninguna otra página es más adecuada y no se dirige a un idioma o locale específico. Añádelo para guiar a los usuarios cuyo idioma no admites.
export function buildHreflangLinks(
pathname: string,
locales: string[],
baseUrl: string,
defaultLocale: string,
): HreflangLink[] {
const links = locales.map((locale) => ({
tagName: "link",
rel: "alternate",
hrefLang: locale,
href: `${baseUrl}/${locale}${pathname}`,
}));
links.push({
tagName: "link",
rel: "alternate",
hrefLang: "x-default",
href: `${baseUrl}/${defaultLocale}${pathname}`,
});
return links;
}
El enlace x-default normalmente apunta a tu versión de idioma principal y sirve como respaldo para usuarios cuyas preferencias de idioma no coinciden con ninguna de tus versiones de idioma específicas.
3. Exporta los enlaces hreflang desde tu función meta
La función meta puede establecer etiquetas de enlace basadas en datos. Úsala para devolver enlaces hreflang para cada ruta.
import type { Route } from "./+types/about";
import { buildHreflangLinks } from "~/utils/hreflang";
const SUPPORTED_LOCALES = ["en", "fr", "de", "es"];
const BASE_URL = "https://example.com";
const DEFAULT_LOCALE = "en";
export function meta({ location }: Route.MetaArgs) {
const hreflangLinks = buildHreflangLinks(
location.pathname,
SUPPORTED_LOCALES,
BASE_URL,
DEFAULT_LOCALE,
);
return [
{ title: "About Us" },
{ name: "description", content: "Learn about our company" },
...hreflangLinks,
];
}
La función meta devuelve un array de descriptores que pueden incluir objetos con tagName establecido en "link". React Router los renderiza como elementos link en el head del documento.
4. Asegúrate de que el componente Meta esté en tu layout raíz
El componente Meta renderiza todas las etiquetas meta creadas por la exportación meta del módulo de ruta y debe estar dentro del head de tu documento. Verifica que tu layout raíz lo incluya.
import { Links, Meta, Outlet, Scripts } from "react-router";
export default function Root() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
);
}
El componente Meta agrega y renderiza todos los descriptores meta de la ruta coincidente, incluidas las etiquetas de enlace hreflang que definiste en el paso 3.
5. Adapta la ruta para rutas con prefijo de locale
Si tus rutas incluyen el locale en la ruta (como /en/about), elimínalo antes de construir enlaces hreflang para que todas las versiones de idioma apunten a la misma página lógica.
export function meta({ location }: Route.MetaArgs) {
const pathWithoutLocale = location.pathname.replace(/^\/[a-z]{2}(\/|$)/, "/");
const hreflangLinks = buildHreflangLinks(
pathWithoutLocale,
SUPPORTED_LOCALES,
BASE_URL,
DEFAULT_LOCALE,
);
return [{ title: "About Us" }, ...hreflangLinks];
}
Esto garantiza que /en/about, /fr/about y /de/about generen enlaces hreflang que apunten a las URL específicas del idioma correctas para el mismo contenido subyacente.