Saltar al contenido

SEO multiidioma: hreflang, sitemaps por locale y canonical

Decirle a los buscadores que tus páginas son la misma en distintos idiomas para que sirvan la versión correcta a cada usuario. Las etiquetas hreflang recíprocas con x-default derivadas de getAbsoluteLocaleUrlList, la opción i18n del sitemap que declara las alternativas por idioma, la canónica que se refiere a sí misma en cada locale, y el catálogo de errores clásicos que sabotean el posicionamiento multilingüe.

hreflang: declarar las equivalencias entre idiomas

La señal central del SEO multilingüe es el conjunto de etiquetas hreflang. Cada versión de una página incluye, en su head, un enlace por cada idioma en que existe, indicando qué URL corresponde a cada lengua. Con ellas, el buscador entiende que las tres URLs son alternativas de un mismo contenido y sirve a cada usuario la que casa con su idioma y región. La pieza que no debe faltar es x-default: la versión a la que recurrir cuando ninguna encaja con el visitante.

ASTRO
---import { getAbsoluteLocaleUrlList } from 'astro:i18n';const ruta = Astro.url.pathname.replace(/^\/(en|fr)/, '') || '/';const alternativas = getAbsoluteLocaleUrlList(ruta);// las mismas URLs absolutas de esta pagina en cada idioma---

El material sale de la lección de helpers: getAbsoluteLocaleUrlList devuelve, para una ruta, sus URLs absolutas en todos los idiomas, que es justo el conjunto que las etiquetas hreflang necesitan. Emitirlas es recorrer esa lista, y añadir la x-default apuntando a la versión por defecto. Como todo se deriva de locales, el bloque crece solo cuando añades un idioma: nunca queda a mano una lista de alternativas que se desincronice de los idiomas reales.

ASTRO
{alternativas.map(({ code, url }) => (  <link rel="alternate" hreflang={code} href={url} />))}<link rel="alternate" hreflang="x-default" href={porDefecto} />

La regla de oro del hreflang es la reciprocidad: si la página española apunta a la inglesa como su alternativa, la inglesa tiene que apuntar de vuelta a la española. Un hreflang que no es correspondido, los buscadores lo ignoran. Por eso conviene generarlo desde un componente compartido que reciba la ruta y pinte el bloque completo en todas las versiones a la vez, garantizando por construcción que el conjunto sea simétrico.

El sitemap que habla de idiomas

Las etiquetas hreflang en el head son una vía; el sitemap es la otra, y las dos se complementan. La integración @astrojs/sitemap acepta una opción i18n que, a partir de tus idiomas, añade a cada entrada del mapa las alternativas por idioma en el formato que el estándar de sitemaps define. Así el buscador descubre las equivalencias sin depender solo de rastrear el head de cada página.

JS
// astro.config.mjsimport sitemap from '@astrojs/sitemap';export default defineConfig({  site: 'https://misitio.com',  integrations: [    sitemap({      i18n: {        defaultLocale: 'es',        locales: {          es: 'es-ES',          en: 'en-US',          fr: 'fr-FR',        },      },    }),  ],});

El mapa entre código interno y etiqueta de idioma es lo que traduce tu es en el es-ES que los buscadores esperan: un código de idioma con región, más preciso. La integración usa ese mapa para rellenar el campo de enlaces alternativos de cada URL, de modo que el sitemap no solo enumera páginas sino que declara qué traducciones son equivalentes. Es, de nuevo, un artefacto derivado: nace de tu configuración de idiomas, no de una tabla que mantengas aparte, y por eso no puede contradecir a las etiquetas hreflang que emites en el head.

Hay un detalle operativo que agradece atención: la integración solo puede declarar como alternativas las páginas que conoce en el build. Igual que ocurría con el sitemap general, las rutas dinámicas de SSR que nacen en tiempo de petición quedan fuera salvo que las enumeres, y con ellas se quedan fuera sus equivalencias por idioma. Para un blog o una documentación estáticos esto no supone problema; para un catálogo servido bajo demanda, recuerda que las alternativas multilingües de esas URLs tendrás que declararlas por otra vía.

canonical y los errores clásicos

La etiqueta canónica declara cuál es la dirección oficial de un contenido, y en un sitio multilingüe su regla es contraintuitiva pero firme: cada versión es canónica de sí misma. La francesa se declara canónica en /fr/blog/, la inglesa en /en/blog/. El error más dañino del SEO multiidioma es apuntar la canónica de todas las versiones a la del idioma por defecto: eso le dice al buscador que solo la española es la real y que las demás son duplicados prescindibles, y borra de un plumazo el posicionamiento de cada traducción.

ASTRO
---import { getAbsoluteLocaleUrl } from 'astro:i18n';const canon = getAbsoluteLocaleUrl(Astro.currentLocale, ruta);---<link rel="canonical" href={canon} />

Con la canónica bien puesta, la relación queda limpia: hreflang dice estas son alternativas equivalentes, y canonical dice y cada una es la oficial de su idioma. Las dos señales colaboran en vez de contradecirse. A partir de aquí, conviene tener presente el catálogo de errores que hunden un sitio multilingüe por lo demás correcto, porque casi todos son silenciosos: no dan un fallo, solo un mal posicionamiento que tarda semanas en notarse.