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 múltiples 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 busque 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 de habla inglesa podría terminar en 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 entender qué páginas son alternativas lingüísticas del mismo contenido, de modo que puedan servir la versión apropiada según las preferencias de idioma y la ubicación de cada usuario.
Solución
Añade etiquetas de enlace hreflang a cada página que listen todas las versiones de idioma disponibles de ese contenido. Estas etiquetas utilizan 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 para esa versión de idioma.
Al declarar estas relaciones en los metadatos de la página, ayudas a los motores de búsqueda a entender la estructura de tu sitio y a 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. Crear una función auxiliar para construir enlaces hreflang
El atributo hreflang utiliza códigos de idioma ISO 639-1 opcionalmente seguidos 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 soportados y la URL base de tu sitio, y luego devuelve un array de descriptores de enlace que la función meta puede renderizar.
2. Añadir un enlace de respaldo x-default
El valor hreflang x-default indica una página predeterminada cuando no hay otra página más adecuada y no se dirige a un idioma o configuración regional específica. Añade esto 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 en el idioma principal y sirve como respaldo para los usuarios cuyas preferencias de idioma no coinciden con ninguna de tus versiones de idioma específicas.
3. Exportar 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 puede incluir objetos con tagName establecido como "link". React Router renderiza estos como elementos de enlace en el head del documento.
4. Asegurar 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 idioma
Si tus rutas incluyen el idioma en la ruta (como /en/about), elimínalo antes de construir los 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 asegura que /en/about, /fr/about y /de/about generen enlaces hreflang que apunten a las URLs específicas de cada idioma para el mismo contenido subyacente.