Detectar el idioma del usuario y redirigir a la versión correcta

Aprende a leer el idioma del navegador, normalizar locales y redirigir de forma segura a la versión web adecuada sin crear bucles ni romper la experiencia del usuario.

Una web multidioma no debería obligar al usuario a buscar manualmente su versión si el navegador ya nos da pistas fiables sobre su preferencia.

La clave no es solo leer `navigator.language`, sino convertir ese dato en una decisión controlada: idioma soportado, fallback y redirección sin bucles.

En trabajo real también importa respetar una elección manual previa, porque el idioma detectado por el navegador no siempre coincide con lo que la persona quiere ver.

Al terminar esta lección podrás construir un snippet reutilizable para detectar el idioma del usuario y llevarlo a la ruta correcta con criterio profesional.

  • Detectar idioma no va de impresionar con una API: va de reducir fricción en una web internacional.
  • Piensa en una landing con versiones en español, inglés y francés. Si un usuario entra por la raíz `/`, lo razonable es enviarlo a la versión que mejor encaja con su navegador, pero sin secuestrar su navegación si ya eligió otra.
  • Ese matiz cambia mucho la implementación. Una redirección útil debe tomar una decisión clara, aplicar un fallback y evitar comportamientos molestos como volver a redirigir cada vez que se recarga la página.
  • La API más conocida es `navigator.language`, pero conviene conocer también `navigator.languages`.
  • `navigator.language` devuelve el idioma principal del navegador, por ejemplo `es-ES`, `es-MX` o `en-US`. Es suficiente para muchos casos simples, pero no te cuenta si el usuario tiene varias preferencias configuradas.

Qué problema resuelve esta redirección

Detectar idioma no va de impresionar con una API: va de reducir fricción en una web internacional.

Piensa en una landing con versiones en español, inglés y francés. Si un usuario entra por la raíz `/`, lo razonable es enviarlo a la versión que mejor encaja con su navegador, pero sin secuestrar su navegación si ya eligió otra.

Ese matiz cambia mucho la implementación. Una redirección útil debe tomar una decisión clara, aplicar un fallback y evitar comportamientos molestos como volver a redirigir cada vez que se recarga la página.

Paso 1: leer el idioma del navegador sin asumir demasiado

La API más conocida es `navigator.language`, pero conviene conocer también `navigator.languages`.

`navigator.language` devuelve el idioma principal del navegador, por ejemplo `es-ES`, `es-MX` o `en-US`. Es suficiente para muchos casos simples, pero no te cuenta si el usuario tiene varias preferencias configuradas.

`navigator.languages` devuelve una lista ordenada por prioridad. Si existe, es mejor empezar por ahí porque te permite probar primero la preferencia principal y luego otras opciones válidas antes de caer en el fallback.

  • Usa `navigator.languages` si está disponible.
  • Mantén un fallback por si el navegador no expone esa lista.
  • No compares todavía contra rutas: primero normaliza.

Paso 2: normalizar el locale y traducirlo a una ruta real

El navegador te da locales completos; tu web suele trabajar con códigos más simples.

Lo habitual es que tu web soporte idiomas base como `es`, `en` o `fr`, mientras que el navegador te devolverá variantes regionales como `es-AR` o `fr-CA`. Por eso conviene convertir `es-ES` en `es` antes de buscar la ruta.

Este paso evita condicionales interminables y te permite añadir idiomas nuevos editando un único objeto de configuración.

Paso 3: redirigir sin crear bucles ni pelearte con el botón atrás

El error típico no está en detectar el idioma, sino en redirigir con demasiada agresividad.

Antes de redirigir debes comprobar si ya estás en la ruta correcta. Si no haces esa verificación, entrar en `/es/` podría lanzar otra redirección innecesaria y complicar el flujo.

Además, en este caso `window.location.replace()` suele ser mejor que `window.location.href = ...` porque sustituye la entrada actual del historial. Así evitas que el usuario pulse Atrás y vuelva a una página que lo redirige otra vez.

  • Comprueba siempre si ya estás en la versión correcta.
  • Usa `replace()` si no quieres ensuciar el historial.
  • Si hay selector manual de idioma, guarda esa decisión y salta la detección automática.

Paso 4: montar el snippet final bien atado

Ahora sí: junta detección, normalización, preferencia guardada y redirección segura en una sola pieza reutilizable.

El snippet final de esta lección sigue cuatro reglas: primero respeta un idioma guardado, después revisa la lista de idiomas del navegador, luego cae en un fallback estable y por último evita redirigir si ya estás en la ruta correcta.

Ese orden importa. Si cambias la prioridad y obligas siempre a seguir el idioma del navegador, puedes romper la experiencia de alguien que prefiere ver la web en inglés aunque tenga el sistema en español.

Debug común: por qué a veces redirige mal

Los fallos más habituales son de criterio, no de sintaxis.

  • Comparar `es-ES` directamente contra una clave `es` y no normalizar el locale.
  • Olvidar el fallback y romper la navegación cuando llega un idioma no soportado como `de-DE`.
  • Usar `href` o `assign()` sin pensar en el historial y generar sensación de rebote al pulsar Atrás.
  • Forzar redirección incluso cuando la ruta actual ya pertenece a un idioma válido.
  • No respetar una preferencia guardada por el usuario y sobreescribirla en cada visita.

Código del tema: navigator.language | navigator.languages | locale | location.replace

📘 Teoría

Qué problema resuelve esta redirección

Detectar idioma no va de impresionar con una API: va de reducir fricción en una web internacional.

Piensa en una landing con versiones en español, inglés y francés. Si un usuario entra por la raíz `/`, lo razonable es enviarlo a la versión que mejor encaja con su navegador, pero sin secuestrar su navegación si ya eligió otra.

Ese matiz cambia mucho la implementación. Una redirección útil debe tomar una decisión clara, aplicar un fallback y evitar comportamientos molestos como volver a redirigir cada vez que se recarga la página.

1

Entrada

El usuario llega a `/` o a una portada neutra.

  • Sin idioma fijado aún
  • Sin preferencia guardada
2

Decisión

La web comprueba idioma preferido y soporte real.

  • `navigator.languages`
  • Mapa de idiomas válidos
3

Salida

Se envía a la ruta correcta o al fallback.

  • `/es/`
  • `/en/`
  • `/fr/`

Paso 1: leer el idioma del navegador sin asumir demasiado

La API más conocida es `navigator.language`, pero conviene conocer también `navigator.languages`.

`navigator.language` devuelve el idioma principal del navegador, por ejemplo `es-ES`, `es-MX` o `en-US`. Es suficiente para muchos casos simples, pero no te cuenta si el usuario tiene varias preferencias configuradas.

`navigator.languages` devuelve una lista ordenada por prioridad. Si existe, es mejor empezar por ahí porque te permite probar primero la preferencia principal y luego otras opciones válidas antes de caer en el fallback.

  • Usa `navigator.languages` si está disponible.
  • Mantén un fallback por si el navegador no expone esa lista.
  • No compares todavía contra rutas: primero normaliza.
Leer idioma principal y lista de preferencias
const primaryLocale = navigator.language || navigator.userLanguage || 'en';
const localeList = navigator.languages?.length
  ? navigator.languages
  : [primaryLocale];

console.log('Idioma principal:', primaryLocale);
console.log('Lista de preferencias:', localeList);

Paso 2: normalizar el locale y traducirlo a una ruta real

El navegador te da locales completos; tu web suele trabajar con códigos más simples.

Lo habitual es que tu web soporte idiomas base como `es`, `en` o `fr`, mientras que el navegador te devolverá variantes regionales como `es-AR` o `fr-CA`. Por eso conviene convertir `es-ES` en `es` antes de buscar la ruta.

Este paso evita condicionales interminables y te permite añadir idiomas nuevos editando un único objeto de configuración.

Normalizar locale y buscar ruta soportada
const SUPPORTED_LOCALES = {
  es: '/es/',
  en: '/en/',
  fr: '/fr/'
};

function normalizeLocale(locale) {
  return String(locale || '')
    .toLowerCase()
    .split('-')[0];
}

function getSupportedLocale(locales) {
  for (const locale of locales) {
    const normalized = normalizeLocale(locale);
    if (SUPPORTED_LOCALES[normalized]) {
      return normalized;
    }
  }

  return 'en';
}

Paso 3: redirigir sin crear bucles ni pelearte con el botón atrás

El error típico no está en detectar el idioma, sino en redirigir con demasiada agresividad.

Antes de redirigir debes comprobar si ya estás en la ruta correcta. Si no haces esa verificación, entrar en `/es/` podría lanzar otra redirección innecesaria y complicar el flujo.

Además, en este caso `window.location.replace()` suele ser mejor que `window.location.href = ...` porque sustituye la entrada actual del historial. Así evitas que el usuario pulse Atrás y vuelva a una página que lo redirige otra vez.

  • Comprueba siempre si ya estás en la versión correcta.
  • Usa `replace()` si no quieres ensuciar el historial.
  • Si hay selector manual de idioma, guarda esa decisión y salta la detección automática.
Comprobar ruta actual antes de redirigir
function isCurrentLocalePath(pathname, locale, routes) {
  const targetPath = routes[locale];
  return pathname === targetPath || pathname.startsWith(targetPath);
}

function redirectToLocale(locale, routes) {
  if (isCurrentLocalePath(window.location.pathname, locale, routes)) {
    return;
  }

  window.location.replace(routes[locale]);
}

Paso 4: montar el snippet final bien atado

Ahora sí: junta detección, normalización, preferencia guardada y redirección segura en una sola pieza reutilizable.

El snippet final de esta lección sigue cuatro reglas: primero respeta un idioma guardado, después revisa la lista de idiomas del navegador, luego cae en un fallback estable y por último evita redirigir si ya estás en la ruta correcta.

Ese orden importa. Si cambias la prioridad y obligas siempre a seguir el idioma del navegador, puedes romper la experiencia de alguien que prefiere ver la web en inglés aunque tenga el sistema en español.

Debug común: por qué a veces redirige mal

Los fallos más habituales son de criterio, no de sintaxis.

  • Comparar `es-ES` directamente contra una clave `es` y no normalizar el locale.
  • Olvidar el fallback y romper la navegación cuando llega un idioma no soportado como `de-DE`.
  • Usar `href` o `assign()` sin pensar en el historial y generar sensación de rebote al pulsar Atrás.
  • Forzar redirección incluso cuando la ruta actual ya pertenece a un idioma válido.
  • No respetar una preferencia guardada por el usuario y sobreescribirla en cada visita.

🧭 Visuales clave

Flujo de detección y redirección por idioma

Resume la secuencia completa: preferencia guardada, lista del navegador, normalización, fallback y redirección con `replace()`.

Diagrama del flujo que pasa de idiomas del navegador a locale soportado y redirección segura

🧪 Aprende probando

Ejemplo Ejemplo guiado: elegir el primer idioma soportado Recorre la lista del navegador, normaliza cada locale y devuelve el primer idioma que tu web soporta.
Ejemplo Demo interactiva: ver qué ruta elegiría tu web Prueba distintas combinaciones de preferencias del navegador y observa cómo cambia la ruta objetivo antes de redirigir de verdad.
Ejemplo Snippet final: detección de idioma con fallback y redirección segura Versión lista para adaptar a tu web multidioma, con prioridad a la preferencia guardada y uso de `replace()` para no generar rebotes.

🏁 Retos

Reto Reto: construir `getLocaleRoute()` con fallback fiable Crea una función que reciba una lista de locales del navegador y devuelva la ruta correcta según tus idiomas soportados.

¿Qué es esto?

Soy Cristian Eslava y a veces hago webs para procrastinar yo y vosotros 😉.

Esta la hice en febrero de 2026 para facilitar el aprendizaje de mis alumnxs. Aprender desarrollo web practicando. La idea es que crezca semanalmente con nuevos temas, tests y retos.

Inspirado en MDN, en W3Schools, en Codepen, en el crack de Manz y en mil sitios de documentación sobre desarrollo web. Quería aportar además de bloques teóricos con ejemplos, la gamificación de los retos y el sistema de test que ya tenía en culTest .

Si te gustó, si no te gustó, si quieres saludarme, o invitarme a 🍻 no dudes en escribirme en cristianeslava@gmail.com .