Cómo manejar el respaldo de locales cuando el local preferido no está disponible

Selecciona automáticamente idiomas compatibles cuando los usuarios prefieren locales no compatibles

Introducción

No todas las aplicaciones web admiten todos los idiomas del mundo. Cuando un usuario prefiere un idioma que tu aplicación no admite, necesitas un mecanismo de respaldo para mostrar el contenido en el siguiente mejor idioma en lugar de mostrar errores o texto sin traducir.

El respaldo de configuración regional es el proceso de seleccionar una configuración regional alternativa cuando la preferida no está disponible. La API Intl de JavaScript maneja esto automáticamente al aceptar múltiples opciones de configuración regional y seleccionar la primera que admite. Esto asegura que tu aplicación siempre muestre contenido correctamente formateado, incluso cuando la configuración regional preferida exacta no está disponible.

Esta lección explica cómo funciona el respaldo de configuración regional en JavaScript, cómo implementarlo de manera efectiva y cómo construir una lógica de respaldo personalizada para aplicaciones con requisitos específicos de soporte de configuración regional.

El problema con las configuraciones regionales no admitidas

Cuando pasas un identificador de configuración regional a una API Intl, el entorno de ejecución de JavaScript debe admitir esa configuración regional para formatear el contenido correctamente. Si solicitas el formato para el noruego nynorsk pero el entorno de ejecución solo admite el noruego bokmål, el formateador necesita una manera de manejar esto con elegancia.

Sin respaldo, las aplicaciones no podrían mostrar contenido o mostrarían texto sin traducir al encontrar configuraciones regionales no admitidas. Los usuarios de regiones con variantes de idioma menos comunes experimentarían interfaces rotas.

Considera un usuario que habla francés canadiense. Si tu aplicación solo admite francés europeo, quieres que el formateador use las convenciones del francés europeo en lugar de fallar por completo. Aunque no sea perfecto, esto proporciona una mejor experiencia que ninguna localización en absoluto.

Cómo la API Intl maneja el respaldo automáticamente

Cada constructor Intl acepta una cadena de configuración regional única o un array de cadenas de configuración regional. Cuando pasas un array, el entorno de ejecución evalúa cada configuración regional en orden y usa la primera que admite.

const locales = ["fr-CA", "fr-FR", "en-US"];
const formatter = new Intl.DateTimeFormat(locales);

const date = new Date("2025-03-15");
console.log(formatter.format(date));
// Usa fr-CA si está disponible
// Recurre a fr-FR si fr-CA no está disponible
// Recurre a en-US si ninguna variante francesa está disponible

El entorno de ejecución examina el array de izquierda a derecha. Si admite el francés canadiense, lo usa. Si no, intenta con el francés europeo. Si ninguna variante francesa está disponible, recurre al inglés americano.

Este respaldo automático significa que no necesitas verificar el soporte manualmente o manejar errores al solicitar configuraciones regionales específicas. La API Intl garantiza que seleccionará una configuración regional admitida o recurrirá a la predeterminada del sistema.

Proporcionar múltiples opciones de localización

La forma más sencilla de implementar el fallback es pasar un array de localizaciones en orden de preferencia. Esto funciona con todos los constructores Intl, incluyendo DateTimeFormat, NumberFormat, Collator y otros.

const locales = ["es-MX", "es-ES", "es", "en"];
const numberFormatter = new Intl.NumberFormat(locales, {
  style: "currency",
  currency: "USD"
});

console.log(numberFormatter.format(1234.56));
// Usa español mexicano si está disponible
// Recurre al español de España
// Recurre al español genérico
// Recurre al inglés como opción final

Este patrón proporciona una ruta de degradación elegante. Los usuarios obtienen contenido en su dialecto preferido si está disponible, luego una variante más amplia de su idioma, y finalmente un idioma de respaldo común.

El orden importa. El entorno de ejecución selecciona la primera localización que soporta, así que coloca primero las localizaciones más específicas y preferidas.

Entender cómo funciona la coincidencia de localizaciones

Cuando proporcionas múltiples localizaciones, el entorno de ejecución de JavaScript utiliza un algoritmo de coincidencia para seleccionar la mejor opción disponible. Este algoritmo compara tus localizaciones solicitadas con el conjunto de localizaciones que el entorno soporta.

Si una localización solicitada coincide exactamente con una localización soportada, el entorno la utiliza inmediatamente. Si no existe una coincidencia exacta, el entorno puede seleccionar una localización relacionada basándose en los códigos de idioma y región.

Por ejemplo, si solicitas en-AU (inglés australiano) pero el entorno solo soporta en-US y en-GB, seleccionará una de esas variantes de inglés en lugar de recurrir a un idioma completamente diferente.

const locales = ["en-AU", "en"];
const formatter = new Intl.DateTimeFormat(locales);

const resolvedLocale = formatter.resolvedOptions().locale;
console.log(resolvedLocale);
// Podría mostrar "en-US" o "en-GB" dependiendo del entorno
// El entorno seleccionó una variante de inglés soportada

El método resolvedOptions() devuelve la localización real que el formateador está utilizando. Esto te permite verificar qué localización fue seleccionada después del fallback.

Comprobando qué locales son compatibles

El método estático supportedLocalesOf() comprueba qué locales de una lista son compatibles con un constructor Intl específico. Este método devuelve un array que contiene solo los locales compatibles.

const requestedLocales = ["fr-CA", "fr-FR", "de-DE", "ja-JP"];
const supportedLocales = Intl.DateTimeFormat.supportedLocalesOf(requestedLocales);

console.log(supportedLocales);
// La salida depende del soporte en tiempo de ejecución
// Ejemplo: ["fr-FR", "de-DE", "ja-JP"]
// El francés canadiense no era compatible, los otros sí

Este método filtra los locales solicitados para mostrar cuáles puede utilizar el entorno de ejecución sin recurrir a valores predeterminados. Los locales no compatibles se eliminan del array devuelto.

Puedes usar este método para verificar la compatibilidad antes de crear formateadores, o para mostrar qué opciones de idioma están disponibles para los usuarios.

const availableLocales = ["en-US", "es-MX", "fr-FR", "de-DE", "ja-JP"];
const supported = Intl.NumberFormat.supportedLocalesOf(availableLocales);

console.log("Este entorno de ejecución es compatible con:", supported);
// Muestra cuáles de los locales de tu aplicación funcionan en este entorno

Cada constructor Intl tiene su propio método supportedLocalesOf() porque la compatibilidad de locales puede variar entre diferentes características de internacionalización. Un entorno de ejecución podría ser compatible con el francés para el formato de números pero no para la segmentación de texto.

Construyendo cadenas de respaldo a partir de identificadores de locale

Cuando sabes que tu aplicación es compatible con locales específicos, puedes construir una cadena de respaldo que progresivamente se vuelve menos específica. Este patrón comienza con un identificador de locale completo y elimina componentes hasta encontrar una coincidencia.

function buildFallbackChain(locale) {
  const chain = [locale];

  const parts = locale.split("-");
  if (parts.length > 1) {
    chain.push(parts[0]);
  }

  chain.push("en");

  return chain;
}

const fallbacks = buildFallbackChain("zh-Hans-CN");
console.log(fallbacks);
// ["zh-Hans-CN", "zh", "en"]

const formatter = new Intl.DateTimeFormat(fallbacks);
// Intenta chino simplificado para China
// Recurre al chino genérico
// Recurre al inglés

Esta función crea una cadena de respaldo extrayendo el código de idioma de un identificador de locale y añadiendo un respaldo final en inglés. Puedes ampliar esta lógica para incluir reglas de respaldo más sofisticadas basadas en los locales compatibles con tu aplicación.

Para aplicaciones que admiten múltiples variantes de un idioma, es posible que desees recurrir a dialectos relacionados antes de pasar al inglés.

function buildSmartFallbackChain(locale) {
  const chain = [locale];

  if (locale.startsWith("es-")) {
    chain.push("es-MX", "es-ES", "es");
  } else if (locale.startsWith("fr-")) {
    chain.push("fr-FR", "fr-CA", "fr");
  } else if (locale.startsWith("zh-")) {
    chain.push("zh-Hans-CN", "zh-Hant-TW", "zh");
  }

  const parts = locale.split("-");
  if (parts.length > 1 && !chain.includes(parts[0])) {
    chain.push(parts[0]);
  }

  if (!chain.includes("en")) {
    chain.push("en");
  }

  return chain;
}

const fallbacks = buildSmartFallbackChain("es-AR");
console.log(fallbacks);
// ["es-AR", "es-MX", "es-ES", "es", "en"]
// Intenta español argentino
// Recurre al español mexicano
// Recurre al español europeo
// Recurre al español genérico
// Recurre al inglés

Este enfoque asegura que los usuarios vean contenido en un dialecto relacionado con su idioma antes de recurrir al inglés.

Seleccionando un algoritmo de coincidencia de locales

La API Intl admite dos algoritmos de coincidencia de locales: lookup y best fit. Puedes especificar qué algoritmo usar a través de la opción localeMatcher al crear formateadores.

El algoritmo lookup sigue la especificación BCP 47 Lookup. Realiza una coincidencia estricta comparando identificadores de locales sistemáticamente y seleccionando la primera coincidencia exacta.

const locales = ["de-DE", "en-US"];
const formatter = new Intl.NumberFormat(locales, {
  localeMatcher: "lookup"
});

console.log(formatter.resolvedOptions().locale);
// Usa reglas de coincidencia estricta de lookup

El algoritmo best fit permite que el entorno de ejecución seleccione un locale utilizando su propia lógica de coincidencia. Este algoritmo puede tomar decisiones inteligentes sobre qué locale satisface mejor las necesidades del usuario, incluso si no es una coincidencia exacta.

const locales = ["de-DE", "en-US"];
const formatter = new Intl.NumberFormat(locales, {
  localeMatcher: "best fit"
});

console.log(formatter.resolvedOptions().locale);
// Usa el algoritmo best fit del entorno de ejecución
// Podría seleccionar un locale relacionado de manera más inteligente

El algoritmo predeterminado es best fit. La mayoría de las aplicaciones deberían usar el predeterminado porque proporciona mejores resultados en diferentes entornos de ejecución de JavaScript. Usa lookup solo cuando necesites un comportamiento de coincidencia estricto y predecible.

Usando las preferencias de idioma del navegador para fallback automático

La propiedad navigator.languages devuelve un array de los idiomas preferidos del usuario en orden de preferencia. Puedes pasar este array directamente a los constructores de Intl para implementar un fallback automático basado en la configuración del navegador.

const formatter = new Intl.DateTimeFormat(navigator.languages);

const date = new Date("2025-03-15");
console.log(formatter.format(date));
// Usa automáticamente el idioma compatible preferido del usuario

Este enfoque permite que el navegador maneje toda la lógica de fallback. Si la primera preferencia del usuario no es compatible, la API Intl automáticamente intenta con su segunda preferencia, luego la tercera, y así sucesivamente.

Este patrón funciona bien cuando quieres respetar todas las preferencias de idioma del usuario sin construir manualmente cadenas de fallback.

console.log(navigator.languages);
// ["fr-CA", "fr", "en-US", "en"]

const numberFormatter = new Intl.NumberFormat(navigator.languages, {
  style: "currency",
  currency: "USD"
});

console.log(numberFormatter.format(1234.56));
// Intenta primero con francés canadiense
// Hace fallback a través de la lista completa de preferencias del usuario

La API Intl evalúa cada idioma en el array y selecciona el primero que admite, lo que hace que esta sea una solución robusta para manejar diversas preferencias de usuario.

Combinación del soporte de aplicaciones con las preferencias del usuario

Para aplicaciones con soporte limitado de locales, puedes filtrar las preferencias del usuario para que coincidan con tus locales soportados antes de crear formateadores. Esto asegura que solo intentes usar locales que tu aplicación pueda manejar.

const supportedLocales = ["en-US", "es-MX", "fr-FR", "de-DE"];

function findBestLocale(userPreferences, appSupported) {
  const preferences = userPreferences.map(pref => {
    const parts = pref.split("-");
    return [pref, parts[0]];
  }).flat();

  for (const pref of preferences) {
    if (appSupported.includes(pref)) {
      return pref;
    }
  }

  return appSupported[0];
}

const userLocale = findBestLocale(navigator.languages, supportedLocales);
const formatter = new Intl.DateTimeFormat(userLocale);

console.log(formatter.resolvedOptions().locale);
// El locale seleccionado coincide tanto con la preferencia del usuario como con el soporte de la aplicación

Esta función encuentra la primera coincidencia entre las preferencias del usuario y los locales soportados, incluyendo la prueba de códigos de idioma sin códigos de región para una coincidencia más amplia.

Para una coincidencia más sofisticada, puedes verificar cuáles de las preferencias del usuario son soportadas por la API Intl en este entorno de ejecución.

const supportedLocales = ["en-US", "es-MX", "fr-FR", "de-DE"];

function findBestSupportedLocale(userPreferences, appSupported) {
  const runtimeSupported = Intl.DateTimeFormat.supportedLocalesOf(appSupported);

  for (const pref of userPreferences) {
    if (runtimeSupported.includes(pref)) {
      return pref;
    }

    const lang = pref.split("-")[0];
    const match = runtimeSupported.find(s => s.startsWith(lang));
    if (match) {
      return match;
    }
  }

  return runtimeSupported[0] || "en";
}

const userLocale = findBestSupportedLocale(navigator.languages, supportedLocales);
const formatter = new Intl.DateTimeFormat(userLocale);

Este enfoque asegura que el locale seleccionado sea soportado tanto por tu aplicación como por el entorno de ejecución de JavaScript.

Manejo de casos donde ningún locale coincide

Si ninguno de los locales solicitados es soportado, la API Intl recurre al locale predeterminado del sistema. Este valor predeterminado varía según el entorno y está determinado por el sistema operativo, el navegador o la configuración de Node.js.

const unsupportedLocales = ["non-existent-locale"];
const formatter = new Intl.DateTimeFormat(unsupportedLocales);

console.log(formatter.resolvedOptions().locale);
// Muestra el locale predeterminado del sistema
// Podría ser "en-US" u otro locale dependiendo del sistema

El valor predeterminado del sistema garantiza que los formateadores siempre funcionen, incluso con listas de locales completamente inválidas o no soportadas. Tu aplicación no arrojará errores debido a problemas de locale.

Para asegurar un respaldo específico en lugar de depender del valor predeterminado del sistema, incluye siempre un locale ampliamente soportado como en o en-US al final de tu array de locales.

const locales = ["xyz-INVALID", "en"];
const formatter = new Intl.DateTimeFormat(locales);

console.log(formatter.resolvedOptions().locale);
// Usará "en" ya que el primer locale es inválido
// Garantiza el respaldo a inglés en lugar del valor predeterminado del sistema

Este patrón hace que el comportamiento de tu aplicación sea más predecible en diferentes entornos.

Patrones prácticos para aplicaciones en producción

Al construir aplicaciones para producción, combine múltiples estrategias de respaldo para garantizar un manejo robusto de configuraciones regionales en bases de usuarios diversas.

Un patrón común crea formateadores con una cadena completa de respaldo que incluye las preferencias del usuario, las configuraciones regionales compatibles con la aplicación y un respaldo final garantizado.

class LocaleManager {
  constructor(supportedLocales) {
    this.supportedLocales = supportedLocales;
    this.defaultLocale = "en-US";
  }

  buildLocaleChain(userPreference) {
    const chain = [];

    if (userPreference) {
      chain.push(userPreference);

      const lang = userPreference.split("-")[0];
      if (lang !== userPreference) {
        chain.push(lang);
      }
    }

    chain.push(...navigator.languages);
    chain.push(...this.supportedLocales);
    chain.push(this.defaultLocale);

    const unique = [...new Set(chain)];
    return unique;
  }

  createDateFormatter(userPreference, options = {}) {
    const locales = this.buildLocaleChain(userPreference);
    return new Intl.DateTimeFormat(locales, options);
  }

  createNumberFormatter(userPreference, options = {}) {
    const locales = this.buildLocaleChain(userPreference);
    return new Intl.NumberFormat(locales, options);
  }
}

const manager = new LocaleManager(["en-US", "es-MX", "fr-FR", "de-DE"]);
const dateFormatter = manager.createDateFormatter("pt-BR");

console.log(dateFormatter.resolvedOptions().locale);
// Utiliza una cadena de respaldo completa
// Intenta con portugués para Brasil
// Recurre al portugués
// Recurre a través de navigator.languages
// Recurre a través de las configuraciones regionales compatibles
// Garantiza el uso de en-US si nada más coincide

Esta clase encapsula la lógica de respaldo de configuraciones regionales y garantiza un comportamiento consistente en toda la aplicación.

Para aplicaciones que necesitan responder a cambios en la configuración regional del usuario en tiempo de ejecución, combine el administrador de configuraciones regionales con detectores de eventos.

class LocaleAwareFormatter {
  constructor(supportedLocales) {
    this.supportedLocales = supportedLocales;
    this.updateFormatters();

    window.addEventListener("languagechange", () => {
      this.updateFormatters();
    });
  }

  updateFormatters() {
    const locales = [...navigator.languages, ...this.supportedLocales, "en"];

    this.dateFormatter = new Intl.DateTimeFormat(locales);
    this.numberFormatter = new Intl.NumberFormat(locales);
  }

  formatDate(date) {
    return this.dateFormatter.format(date);
  }

  formatNumber(number) {
    return this.numberFormatter.format(number);
  }
}

const formatter = new LocaleAwareFormatter(["en-US", "es-MX", "fr-FR"]);
console.log(formatter.formatDate(new Date()));
// Se actualiza automáticamente cuando el usuario cambia las preferencias de idioma

Este patrón crea formateadores que permanecen sincronizados con los cambios de idioma del navegador, asegurando que su aplicación siempre muestre el contenido de acuerdo con las preferencias actuales.

Resumen

El sistema de respaldo de locales garantiza que tu aplicación muestre contenido con formato adecuado incluso cuando los usuarios prefieren locales que no admites explícitamente. La API Intl maneja el respaldo automáticamente al aceptar matrices de preferencias de locales y seleccionar la primera opción compatible.

Conceptos clave:

  • Pasa matrices de locales a los constructores Intl para el respaldo automático
  • El tiempo de ejecución selecciona el primer local compatible de la matriz
  • Usa supportedLocalesOf() para verificar qué locales están disponibles
  • Construye cadenas de respaldo que progresen de locales específicos a generales
  • La opción localeMatcher controla el algoritmo de coincidencia
  • Pasa navigator.languages directamente para manejar automáticamente las preferencias del usuario
  • Incluye siempre un respaldo final ampliamente compatible como el inglés
  • El tiempo de ejecución recurre a los valores predeterminados del sistema cuando ningún local coincide

Utiliza el respaldo automático con matrices de locales para la mayoría de las aplicaciones. Implementa lógica de respaldo personalizada cuando necesites un control específico sobre qué locales se intentan y en qué orden.