Cómo gestionar el respaldo de locales cuando el locale preferido no está disponible
Selecciona automáticamente idiomas compatibles cuando los usuarios prefieren locales no admitidos
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 locales es el proceso de seleccionar un locale alternativo cuando el locale preferido no está disponible. La API Intl de JavaScript gestiona esto automáticamente al aceptar múltiples opciones de locale y seleccionar la primera que admite. Esto garantiza que tu aplicación siempre muestre contenido con formato adecuado, incluso cuando el locale preferido exacto no esté disponible.
Esta lección explica cómo funciona el respaldo de locales en JavaScript, cómo implementarlo de manera efectiva y cómo construir lógica de respaldo personalizada para aplicaciones con requisitos específicos de compatibilidad de locales.
El problema con los locales no admitidos
Cuando pasas un identificador de locale a una API Intl, el runtime de JavaScript debe admitir ese locale para formatear el contenido correctamente. Si solicitas formato para noruego nynorsk pero el runtime solo admite noruego bokmål, el formateador necesita una forma de gestionar esto de manera elegante.
Sin respaldo, las aplicaciones fallarían al mostrar contenido o mostrarían texto sin traducir al encontrar locales no admitidos. 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 completamente. Aunque no es 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 locale única o una matriz de cadenas de locales. Cuando pasas una matriz, el tiempo de ejecución evalúa cada locale en orden y usa el primero 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));
// Uses fr-CA if available
// Falls back to fr-FR if fr-CA is not available
// Falls back to en-US if neither French variant is available
El tiempo de ejecución examina la matriz de izquierda a derecha. Si admite francés canadiense, lo usa. Si no, intenta con francés europeo. Si ninguna variante de francés está disponible, recurre al inglés americano.
Este respaldo automático significa que no necesitas verificar la compatibilidad manualmente ni manejar errores al solicitar locales específicos. La API Intl garantiza que seleccionará un locale compatible o recurrirá al predeterminado del sistema.
Proporcionar múltiples opciones de locale
La forma más simple de implementar el respaldo es pasar una matriz de locales 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));
// Uses Mexican Spanish if available
// Falls back to European Spanish
// Falls back to generic Spanish
// Falls back to English as final option
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 después un idioma de respaldo común.
El orden importa. El tiempo de ejecución selecciona el primer locale que admite, así que coloca los locales más específicos y preferidos primero.
Comprender cómo funciona la coincidencia de locales
Cuando proporcionas múltiples locales, el tiempo de ejecución de JavaScript usa un algoritmo de coincidencia de locales para seleccionar la mejor opción disponible. Este algoritmo compara tus locales solicitados con el conjunto de locales que el tiempo de ejecución admite.
Si un locale solicitado coincide exactamente con un locale admitido, el tiempo de ejecución lo usa inmediatamente. Si no existe una coincidencia exacta, el tiempo de ejecución puede seleccionar un locale relacionado basándose en los códigos de idioma y región.
Por ejemplo, si solicitas en-AU (inglés australiano) pero el runtime solo admite 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);
// Might show "en-US" or "en-GB" depending on the runtime
// The runtime selected a supported English variant
El método resolvedOptions() devuelve el locale real que está utilizando el formateador. Esto te permite verificar qué locale se seleccionó después del fallback.
Verificar qué locales son compatibles
El método estático supportedLocalesOf() verifica 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);
// Output depends on runtime support
// Example: ["fr-FR", "de-DE", "ja-JP"]
// Canadian French was not supported, others were
Este método filtra los locales solicitados para mostrar cuáles puede usar el runtime 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("This runtime supports:", supported);
// Shows which of your application locales work in this environment
Cada constructor Intl tiene su propio método supportedLocalesOf() porque la compatibilidad de locales puede variar entre diferentes funciones de internacionalización. Un runtime podría admitir francés para formato de números pero no para segmentación de texto.
Construir cadenas de fallback a partir de identificadores de locale
Cuando sabes que tu aplicación admite locales específicos, puedes construir una cadena de fallback 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);
// Tries Simplified Chinese for China
// Falls back to generic Chinese
// Falls back to English
Esta función crea una cadena de fallback extrayendo el código de idioma de un identificador de locale y agregando un fallback final en inglés. Puedes expandir esta lógica para incluir reglas de fallback más sofisticadas basadas en los locales compatibles de tu aplicación.
Para aplicaciones que admiten múltiples variantes de un idioma, es posible que desees recurrir a dialectos relacionados antes de saltar 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"]
// Tries Argentinian Spanish
// Falls back to Mexican Spanish
// Falls back to European Spanish
// Falls back to generic Spanish
// Falls back to English
Este enfoque garantiza que los usuarios vean contenido en un dialecto relacionado de su idioma antes de recurrir al inglés.
Selección de un algoritmo de coincidencia de locale
La API Intl admite dos algoritmos de coincidencia de locale: lookup y best fit. Puedes especificar qué algoritmo usar mediante la opción localeMatcher al crear formateadores.
El algoritmo lookup sigue la especificación BCP 47 Lookup. Realiza una coincidencia estricta comparando identificadores de locale 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);
// Uses strict lookup matching rules
El algoritmo best fit permite que el runtime seleccione un locale usando su propia lógica de coincidencia. Este algoritmo puede tomar decisiones inteligentes sobre qué locale sirve 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);
// Uses runtime's best fit algorithm
// Might select a related locale more intelligently
El algoritmo predeterminado es best fit. La mayoría de las aplicaciones deberían usar el predeterminado porque proporciona mejores resultados en diferentes runtimes de JavaScript. Usa lookup solo cuando necesites un comportamiento de coincidencia estricto y predecible.
Uso de 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 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));
// Automatically uses user's preferred supported language
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 intenta automáticamente con su segunda preferencia, luego la tercera, y así sucesivamente.
Este patrón funciona bien cuando deseas 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));
// Tries Canadian French first
// Falls back through the user's complete preference list
La API Intl evalúa cada idioma en el array y selecciona el primero que admite, lo que hace de esta una solución robusta para manejar diversas preferencias de usuario.
Combinación del soporte de la aplicación con las preferencias del usuario
Para aplicaciones con soporte de configuración regional limitado, puedes filtrar las preferencias del usuario para que coincidan con tus configuraciones regionales admitidas antes de crear formateadores. Esto garantiza que solo intentes usar configuraciones regionales 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);
// Selected locale matches both user preference and app support
Esta función encuentra la primera coincidencia entre las preferencias del usuario y las configuraciones regionales admitidas, incluyendo intentar 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 compatibles con 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 garantiza que la configuración regional seleccionada sea compatible tanto con tu aplicación como con el entorno de ejecución de JavaScript.
Manejo de casos donde ninguna configuración regional coincide
Si ninguna de las configuraciones regionales solicitadas es compatible, la API Intl recurre a la configuración regional predeterminada 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);
// Shows the system default locale
// Might be "en-US" or another locale depending on the system
El valor predeterminado del sistema garantiza que los formateadores siempre funcionen, incluso con listas de configuraciones regionales completamente inválidas o no admitidas. Tu aplicación no generará errores debido a problemas de configuración regional.
Para garantizar un respaldo específico en lugar de depender del valor predeterminado del sistema, incluye siempre una configuración regional ampliamente compatible como en o en-US al final de tu array de configuraciones regionales.
const locales = ["xyz-INVALID", "en"];
const formatter = new Intl.DateTimeFormat(locales);
console.log(formatter.resolvedOptions().locale);
// Will use "en" since the first locale is invalid
// Guaranteed fallback to English instead of system default
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 crear aplicaciones en producción, combina 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 de respaldo completa que incluye preferencias del usuario, configuraciones regionales admitidas por 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);
// Uses comprehensive fallback chain
// Tries Portuguese for Brazil
// Falls back to Portuguese
// Falls back through navigator.languages
// Falls back through supported locales
// Guaranteed to use en-US if nothing else matches
Esta clase encapsula la lógica de respaldo de configuración regional y garantiza un comportamiento consistente en toda tu aplicación.
Para aplicaciones que necesitan responder a cambios de configuración regional del usuario en tiempo de ejecución, combina el gestor de configuración regional con event listeners.
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()));
// Automatically updates when user changes language preferences
Este patrón crea formateadores que permanecen sincronizados con los cambios de idioma del navegador, asegurando que tu aplicación siempre muestre contenido según las preferencias actuales.
Resumen
El respaldo de configuración regional garantiza que tu aplicación muestre contenido correctamente formateado incluso cuando los usuarios prefieren configuraciones regionales que no admites explícitamente. La API Intl maneja el respaldo automáticamente al aceptar arrays de preferencias de configuración regional y seleccionar la primera opción compatible.
Conceptos clave:
- Pasa arrays de localizaciones a los constructores Intl para respaldo automático
- El runtime selecciona la primera localización que admite del array
- Usa
supportedLocalesOf()para verificar qué localizaciones están disponibles - Construye cadenas de respaldo que progresen de localizaciones específicas a generales
- La opción
localeMatchercontrola el algoritmo de coincidencia - Pasa
navigator.languagesdirectamente para el manejo automático de preferencias del usuario - Incluye siempre un respaldo final ampliamente compatible como el inglés
- El runtime recurre a los valores predeterminados del sistema cuando ninguna localización coincide
Usa el respaldo automático con arrays de localizaciones para la mayoría de las aplicaciones. Implementa lógica de respaldo personalizada cuando necesites control específico sobre qué localizaciones se intentan y en qué orden.