En 2023 lancé un sitio de comercio electrónico multilingüe para un cliente basado en Berlín. Doce idiomas, App Router, todo se veía ordenado. Tres meses después sus páginas de productos en alemán se posicionaban en los SERPs del Reino Unido y sus páginas en inglés se servían a usuarios en Múnich. Las etiquetas hreflang estaban técnicamente presentes. Solo que eran incorrectas de una forma que me tomó una tarde vergonzosa desentrañar. Esa experiencia me enseñó más sobre Next.js i18n que cualquier página de documentación.
Este no es un post de "aquí está la documentación oficial resumida". Es lo que he aprendido construyendo sitios multilingües en Seahawk Media, qué problemas afectan repetidamente a la gente, y las decisiones específicas que realmente afectan los posicionamientos en 2026.
---
El App Router Lo Cambió Todo (Y No Todo para Mejor)
El Pages Router tenía soporte i18n integrado. Dejabas un bloque en next.config.js y Next.js manejaba la detección de locale, el enrutamiento por subruta, y el cambio de locale del componente Link casi automáticamente. No era perfecto, pero era opinado, lo que significaba menos riesgos.
El App Router eliminó completamente el enrutamiento i18n nativo. Sin clave i18n en next.config.js. Sin detección automática de idioma. Ahora eres responsable de toda la lógica de enrutamiento, y esa responsabilidad es donde la mayoría de los equipos silenciosamente hacen un desastre.
El enfoque recomendado en 2026 es un segmento dinámico [locale] en la raíz de tu directorio app/. Algo como app/[locale]/page.tsx. Combina eso con next-intl o i18next a través de react-i18next, un archivo middleware que lee los encabezados Accept-Language y redirige en consecuencia, y tienes una base funcional.
Por Qué el Middleware Se Entiende Mal
El archivo middleware está haciendo dos trabajos separados que la gente confunde. Primero, detección de idioma: leer el encabezado Accept-Language (o una cookie, o un prefijo de URL) y decidir qué idioma servir. Segundo, redirigir o reescribir la URL para que el usuario termine en /de/produkte en lugar de /produkte con algún estado de idioma oculto.
Ambas cosas deben suceder antes de que la página se renderice. Eso suena obvio. Pero he visto equipos ejecutar la detección de idioma dentro de un proveedor de contexto de React, lo que significa que el HTML inicial servido a Googlebot no tiene ninguna señal de idioma. El bot ve una página, la indexa bajo la URL raíz, y de repente tienes un desastre de canonicalización.
Mantén la resolución de idioma en middleware. Siempre.
---
Sub-ruta vs Sub-dominio: Elige Uno y Comprométete
El debate eterno. /en/about vs en.yoursite.com/about. Para la mayoría de los proyectos recomiendo enrutamiento de sub-rutas, y aquí está el porqué: concentra tu autoridad de dominio. Cada enlace que apunta a tu .com beneficia todos los idiomas. Con sub-dominios efectivamente estás ejecutando sitios separados a los ojos de Google, lo que significa que necesitas construir autoridad para cada uno independientemente.
Hay una excepción. Si estás en un mercado donde el TLD local importa enormemente para la confianza (.de en Alemania, .fr en Francia), un TLD de código de país supera tanto las sub-rutas como los sub-dominios para la credibilidad local percibida. Eso no es una afirmación de SEO, es una afirmación de conversión. He visto pruebas A/B donde .de vs yourbrand.com/de movieron métricas de confianza significativamente incluso cuando los rankings eran idénticos.
Pero no puedes ejecutar una configuración de ccTLD dentro de una sola aplicación Next.js fácilmente. Entonces, para el 99% de los proyectos: sub-rutas, una aplicación, un dominio.
---
hreflang: La Etiqueta Que Te Miente
Aquí es donde veo el daño más grave. hreflang no es conceptualmente difícil. Le dice a los motores de búsqueda: "esta página tiene equivalentes en otros idiomas, aquí están." En la práctica, la implementación tiene tres modos de falla específicos que sigo encontrando.
Modo de Falla 1: Las Etiquetas No Son Recíprocas
Cada página en tu conjunto hreflang debe hacer referencia a todas las demás páginas, incluida ella misma. Si /en/about tiene un hreflang que apunta a /de/about pero /de/about no tiene un hreflang que apunte de vuelta a /en/about, Google ignora el conjunto completo. No penaliza. Ignora. Como si las etiquetas no existieran.
Seahawk tuvo un proyecto de fintech el año pasado donde el 40% de sus etiquetas hreflang no eran recíprocas porque dos desarrolladores las habían implementado independientemente en componentes diferentes sin coordinarse. La corrección tomó una hora. La recuperación de posicionamiento tomó dos meses.
Modo de Falla 2: La Etiqueta x-default Falta o Está Mal Colocada
x-default es para páginas que no apuntan a un idioma o región específicos. Típicamente tu raíz / o una página selectora de idioma. Algunos equipos ponen x-default en su página en inglés, lo cual está bien si el inglés es genuinamente tu fallback para todas las locales sin coincidencia. Pero si sirves una página diferente a usuarios cuyo idioma no soportas, x-default debería apuntar allí.
He visto x-default omitido completamente. Google no lanza un error. Simplemente toma su propia decisión sobre qué versión mostrar para consultas sin coincidencia, y esa decisión frecuentemente es incorrecta.
Modo de fallo 3: Barras diagonales finales y conflictos canónicos
Tu etiqueta hreflang dice https://yoursite.com/de/ueber-uns . La etiqueta canónica en la misma página dice https://yoursite.com/de/ueber-uns/ (nota la barra diagonal final). Google trata esos como URLs diferentes. Tu canónica y hreflang ahora apuntan a cosas distintas, y has creado un conflicto de señal que suprime silenciosamente los rankings sin ningún error visible en Search Console.
Establece trailingSlash en next.config.js y sé consistente en todas partes. Verifica. Realmente verifica ejecutando curl en tus propias URLs y leyendo el resultado.
La documentación de Google Search Central sobre hreflang realmente vale la pena leerla completa. Es uno de esos raros documentos oficiales que es lo suficientemente específico para ser útil.
---
Generando hreflang a escala en Next.js
Para un sitio de 5 idiomas podrías escribir etiquetas hreflang a mano. Para 12 locales en 3,000 páginas de productos, necesitas un enfoque sistemático.
Esto es lo que uso:
- Define tu lista de locales una sola vez, en un único archivo de configuración. Algo como
locales.config.tsexportando un array:['en', 'de', 'fr', 'es', 'nl'...]. - Construye una función utilitaria
getHreflangAlternates(pathname: string)que tome una ruta agnóstica de locale (/about) y devuelva un array de objetos{ hreflang, href }para cada locale. - Llama esa función en tu root
layout.tsxe inyecta los resultados mediante la APImetadatade Next.js usando el campo alternates.languages. Esto se añadió en Next.js 13.3 y genera automáticamente las etiquetas<link rel="alternate" hreflang="...">correctas en<head>. - Para páginas con contenido dinámico (posts de blog, páginas de productos) donde no todos los idiomas tienen traducción, pasa un array
supportedLocalesa tu utilidad y genera etiquetas solo para los idiomas donde el contenido realmente existe. Una etiqueta hreflang que apunte a una página que devuelve 404 es peor que no tener etiqueta hreflang.
El enfoque alternates.languages en la API metadata es la forma más limpia que he encontrado para manejar esto. Sin manipulación de <Head> personalizada, sin paquetes de terceros para las etiquetas en sí.
---
Detección de Idioma Sin Ser Molesto
Hay una trampa UX aquí que también tiene implicaciones SEO. La redirección automática agresiva basada en el idioma del navegador es mala para ambos.
Si un usuario en Alemania visita yoursite.com/en/article y tu middleware lo redirige forzadamente a /de/artikel sin preguntar, acabas de romper su navegación deseada. También has roto potencialmente el compartir enlaces: alguien comparte la URL en inglés, el destinatario en Francia es redirigido al francés, y el contenido que se suponía que debería ver podría no existir en francés aún.
Mi regla: detecta, sugiere, no fuerces. Usa el header Accept-Language para establecer un idioma sensato por defecto en la primera visita (almacenado en una cookie), pero permite que los usuarios lo sobrescriban manualmente y respeta esa decisión en cada visita posterior. La cookie tiene prioridad sobre el header.
Para SEO específicamente: Googlebot no envía headers Accept-Language significativos. Rastreará tus URLs tal cual. Así que asegúrate de que cada URL de idioma sea directamente accesible sin redirección, y que tu sitemap incluya explícitamente todas las variantes de idioma.
La documentación del middleware next-intl cubre bien esta estrategia de detección, y es donde dirigiría a cualquiera que empiece un proyecto nuevo.
---
Mapas de sitio para sitios multilingües
Un único sitemap.xml que incluya todas las variantes de locale funciona bien para sitios más pequeños. Para sitios grandes (decenas de miles de URLs en múltiples locales), lo divido: un índice de sitemap en /sitemap.xml, luego archivos de sitemap separados por locale en /sitemaps/en.xml, /sitemaps/de.xml, y así sucesivamente.
Cada entrada de URL debe incluir etiquetas <xhtml:link rel="alternate"> que reflejen tu configuración de hreflang. Esto es redundante con las etiquetas en la página, pero ayuda a Googlebot a conectar los puntos en páginas que aún no ha rastreado.
En Next.js 14+ puedes generar sitemaps dinámicamente mediante app/sitemap.ts. El tipo de retorno soporta alternates.languages en la misma forma que la API de metadata. Conéctalo a la misma utilidad getHreflangAlternates que construiste antes y obtienes consistencia de forma gratuita.
Un número concreto: en un proyecto con 8 locales y 4,500 productos, dividir sitemaps redujo el tiempo promedio de rastreo por locale aproximadamente un 30% según las estadísticas de rastreo de GSC. No es espectacular, pero es significativo.
---
Librerías de traducción: lo que realmente uso
Hay tres que vale la pena conocer. next-intl es mi predeterminada para proyectos con App Router. Tiene soporte de primera clase para componentes de servidor, maneja pluralización correctamente, y la API es limpia. La he usado en alrededor de 40 proyectos desde mediados de 2023.
react-i18next está probada en batalla y tiene un ecosistema enorme, pero su modelo mental fue construido para componentes de cliente y agregar soporte de componentes de servidor mediante el core de i18next se siente como algo añadido en comparación con el enfoque nativo de next-intl.
Lingui vale la pena considerarlo si tu flujo de traducción involucra traductores profesionales en lugar de un CMS. Su herramienta de extracción es superior. Pero para la mayoría de proyectos de agencia donde el contenido viene de Contentful o Sanity, next-intl es más simple.
Lo que evitaría: construir el tuyo propio. Lo hice una vez en 2020 para un proyecto donde el cliente insistía en reutilizar directamente sus archivos de traducción PHP existentes. Funcionó, apenas, y pasé dos años manteniendo algo que next-intl ahora maneja en una única llamada useTranslations().
---
FAQ
¿Soporta Next.js App Router el bloque i18n antiguo de `next.config.js`?
No. La clave de configuración i18n era específica del Pages Router. Si estás migrando a App Router, necesitas implementar el enrutamiento a través de un segmento dinámico [locale] y un archivo middleware. El bloque de configuración antiguo se ignora silenciosamente en proyectos de App Router.
¿Debo usar `lang` en la ruta URL o confiar en subdominios?
Para la mayoría de proyectos, `lang` en la ruta URL (enrutamiento por subruta como /de/) es la opción correcta. Los subdominios fragmentan tu autoridad de dominio y requieren configuración DNS separada. Los ccTLDs solo valen la complejidad si las señales de confianza local están moviendo genuinamente las tasas de conversión en tus mercados específicos.
¿Cómo maneja Googlebot el contenido de locales renderizado con JavaScript?
Mal, a veces. Si tu detección de locale se ejecuta puramente del lado del cliente en un contexto React y el HTML inicial no tiene marcador de locale, Googlebot puede indexar el estado previo a la hidratación. Siempre resuelve el locale en middleware y asegúrate de que el atributo `lang` correcto esté en el elemento <html> en el HTML renderizado del servidor. Verifica esto viendo el código fuente, no usando las herramientas del desarrollador del navegador (que muestran el estado post-hidratación).
¿Cuál es el valor `x-default` correcto para hreflang en un sitio sin página de selector de idiomas?
Apúntalo a la página de inicio de tu locale principal, típicamente /en/. Le señala a Google que el inglés es tu opción de fallback para usuarios cuyo idioma no soportas. No es una penalización hacerlo, es solo una señal de preferencia suave que Google puede o no considerar.
¿Puedo verificar mi implementación de hreflang sin una herramienta SEO de pago?
Sí. La herramienta de Inspección de URLs de Google Search Console te mostrará los tags hreflang detectados en cualquier URL indexada. No validará la reciprocidad automáticamente, pero puedes verificar manualmente algunas páginas representativas. Para validación en masa, Screaming Frog (gratuito hasta 500 URLs) maneja verificaciones de reciprocidad en su pestaña hreflang.
---
Hacer Next.js multilingüe correctamente es genuinamente un trabajo poco glamoroso. Son archivos de configuración, consistencia de URLs, y asegurarte de que dos desarrolladores no hayan implementado lo mismo dos veces en componentes diferentes. Pero los sitios donde me he tomado el tiempo de hacerlo bien tienen rankings que se sostienen entre regiones, y los que corté esquinas me han enseñado las lecciones que he escrito aquí.
El cliente de Berlín, por cierto, eventualmente logró que sus páginas en alemán rankeen en Alemania. Nos tomó un ciclo de re-crawl completo y mucho café.
