Cómo personalizar locales con extensiones Unicode
Añade sistemas de calendario, formatos de numeración y preferencias de visualización de hora a los identificadores de locale
Introducción
Un identificador de configuración regional como en-US le indica a JavaScript qué idioma y región usar para el formato. Sin embargo, no especifica qué sistema de calendario usar, qué formato de numeración mostrar, o si mostrar la hora en formato de 12 horas o 24 horas. Estas preferencias de formato varían según la elección del usuario, no solo por ubicación.
Las extensiones Unicode resuelven este problema. Permiten agregar preferencias de formato directamente a los identificadores de configuración regional. En lugar de usar opciones de configuración separadas para cada formateador, se codifican las preferencias una vez en la propia cadena de configuración regional.
Esta guía explica cómo funcionan las extensiones Unicode, qué tipos de extensiones están disponibles y cuándo usarlas en su código de internacionalización.
Qué son las extensiones Unicode
Las extensiones Unicode son etiquetas adicionales que se agregan a los identificadores de configuración regional para especificar preferencias de formato. Siguen un formato estándar definido en BCP 47, la misma especificación que define los identificadores de configuración regional.
Una extensión comienza con -u- seguido de pares clave-valor. La u significa Unicode. Cada clave tiene dos letras, y los valores varían según el tipo de clave.
const locale = "en-US-u-ca-gregory-hc-h12";
Este identificador de configuración regional especifica inglés americano con el calendario gregoriano y visualización de hora en formato de 12 horas.
Cómo agregar extensiones a cadenas de configuración regional
Las extensiones aparecen al final de un identificador de configuración regional, después de los componentes de idioma, escritura y región. El marcador -u- separa el identificador principal de las extensiones.
La estructura básica sigue este patrón:
idioma-región-u-clave-valor-clave-valor
Cada par clave-valor especifica una preferencia de formato. Puede incluir múltiples pares clave-valor en una sola cadena de configuración regional.
const japanese = new Intl.Locale("ja-JP-u-ca-japanese-nu-jpan");
console.log(japanese.calendar); // "japanese"
console.log(japanese.numberingSystem); // "jpan"
El orden de los pares clave-valor no importa. Tanto "en-u-ca-gregory-nu-latn" como "en-u-nu-latn-ca-gregory" son válidos y equivalentes.
Extensiones de calendario
La clave ca especifica qué sistema de calendario usar para el formato de fecha. Diferentes culturas utilizan diferentes sistemas de calendario, y algunos usuarios prefieren calendarios no gregorianos por razones religiosas o culturales.
Los valores comunes de calendario incluyen:
gregorypara el calendario gregorianobuddhistpara el calendario budistaislamicpara el calendario islámicohebrewpara el calendario hebreochinesepara el calendario chinojapanesepara el calendario imperial japonés
const islamicLocale = new Intl.Locale("ar-SA-u-ca-islamic");
const date = new Date("2025-03-15");
const formatter = new Intl.DateTimeFormat(islamicLocale, {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(formatter.format(date));
// Output: "٢٠ رمضان ١٤٤٦ هـ"
Esto formatea la fecha según el calendario islámico. La misma fecha gregoriana aparece como un año, mes y día diferentes en el sistema de calendario islámico.
El calendario budista se utiliza comúnmente en Tailandia. Cuenta los años desde el nacimiento de Buda en el 543 a.C., lo que hace que los años budistas estén 543 años por delante de los años gregorianos.
const buddhistLocale = new Intl.Locale("th-TH-u-ca-buddhist");
const formatter = new Intl.DateTimeFormat(buddhistLocale, {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(formatter.format(new Date("2025-03-15")));
// Output: "15 มีนาคม 2568"
El año 2025 en el calendario gregoriano es 2568 en el calendario budista.
Extensiones de sistema de numeración
La clave nu especifica qué sistema de numeración usar para mostrar números. Aunque la mayoría de las configuraciones regionales utilizan numerales arábigos occidentales (0-9), muchas regiones tienen sus propios sistemas de numeración tradicionales.
Los valores comunes de sistemas de numeración incluyen:
latnpara numerales arábigos occidentales (0-9)arabpara numerales arábigo-índicoshanidecpara numerales decimales chinosdevapara numerales devanagarithaipara numerales tailandeses
const arabicLocale = new Intl.Locale("ar-EG-u-nu-arab");
const number = 123456;
const formatter = new Intl.NumberFormat(arabicLocale);
console.log(formatter.format(number));
// Output: "١٢٣٬٤٥٦"
Los numerales arábigo-índicos se ven diferentes de los numerales occidentales pero representan los mismos valores. El número 123456 aparece como ١٢٣٬٤٥٦.
Los numerales tailandeses proporcionan otro ejemplo:
const thaiLocale = new Intl.Locale("th-TH-u-nu-thai");
const formatter = new Intl.NumberFormat(thaiLocale);
console.log(formatter.format(123456));
// Output: "๑๒๓,๔๕๖"
Muchas configuraciones regionales árabes admiten tanto numerales arábigo-índicos como numerales latinos. Los usuarios pueden elegir su sistema preferido según su preferencia personal o contexto.
Extensiones de ciclo horario
La clave hc especifica cómo mostrar la hora. Algunas regiones prefieren el formato de 12 horas con indicadores AM y PM, mientras que otras prefieren el formato de 24 horas. El ciclo horario también determina cómo aparece la medianoche.
Hay cuatro valores de ciclo horario disponibles:
h12utiliza horas 1-12 con la medianoche a las 12:00 AMh11utiliza horas 0-11 con la medianoche a las 0:00 AMh23utiliza horas 0-23 con la medianoche a las 0:00h24utiliza horas 1-24 con la medianoche a las 24:00
Los valores h12 y h11 representan el formato de 12 horas, mientras que h23 y h24 representan el formato de 24 horas. La diferencia radica en si el rango de horas comienza en 0 o en 1.
const us12Hour = new Intl.Locale("en-US-u-hc-h12");
const japan11Hour = new Intl.Locale("ja-JP-u-hc-h11");
const europe23Hour = new Intl.Locale("en-GB-u-hc-h23");
const date = new Date("2025-03-15T00:30:00");
console.log(new Intl.DateTimeFormat(us12Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "12:30 AM"
console.log(new Intl.DateTimeFormat(japan11Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "0:30 AM"
console.log(new Intl.DateTimeFormat(europe23Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "00:30"
El formato h12 muestra la medianoche como 12:30 AM, mientras que h11 la muestra como 0:30 AM. El formato h23 la muestra como 00:30 sin AM o PM.
La mayoría de las aplicaciones utilizan h12 o h23. El formato h11 se utiliza principalmente en Japón, y h24 rara vez se utiliza en la práctica.
Extensiones de ordenación
La clave co especifica reglas de ordenación para clasificar cadenas de texto. La ordenación determina el orden de los caracteres al clasificar texto. Diferentes idiomas y regiones tienen diferentes convenciones de ordenación.
Los valores comunes de ordenación incluyen:
standardpara la ordenación estándar Unicodephonebkpara ordenación de guía telefónica (alemán)pinyinpara ordenación Pinyin (chino)strokepara ordenación por número de trazos (chino)
La ordenación de guía telefónica alemana trata las diéresis de manera diferente a la ordenación estándar. El orden de guía telefónica expande ä a ae, ö a oe, y ü a ue para propósitos de clasificación.
const names = ["Müller", "Meyer", "Möller", "Mueller"];
const standard = new Intl.Collator("de-DE");
const phonebook = new Intl.Collator("de-DE-u-co-phonebk");
console.log(names.sort((a, b) => standard.compare(a, b)));
// Output: ["Meyer", "Möller", "Mueller", "Müller"]
console.log(names.sort((a, b) => phonebook.compare(a, b)));
// Output: ["Meyer", "Möller", "Mueller", "Müller"]
La ordenación china ofrece múltiples sistemas de ordenación. La ordenación Pinyin clasifica por pronunciación, mientras que la ordenación por trazos clasifica por el número de trazos de pincel utilizados para escribir cada carácter.
const pinyinCollator = new Intl.Collator("zh-CN-u-co-pinyin");
const strokeCollator = new Intl.Collator("zh-CN-u-co-stroke");
Las extensiones de ordenación solo afectan a la API Intl.Collator y a métodos como Array.prototype.sort() cuando se utilizan con ordenadores.
Extensiones case-first
La clave kf determina si las letras mayúsculas o minúsculas se ordenan primero en la colación. Esta preferencia varía según el idioma y el caso de uso.
Hay tres valores disponibles:
upperpara ordenar las letras mayúsculas antes que las minúsculaslowerpara ordenar las letras minúsculas antes que las mayúsculasfalsepara usar el orden de casos predeterminado del idioma
const words = ["apple", "Apple", "APPLE", "banana"];
const upperFirst = new Intl.Collator("en-US-u-kf-upper");
const lowerFirst = new Intl.Collator("en-US-u-kf-lower");
console.log(words.sort((a, b) => upperFirst.compare(a, b)));
// Output: ["APPLE", "Apple", "apple", "banana"]
console.log(words.sort((a, b) => lowerFirst.compare(a, b)));
// Output: ["apple", "Apple", "APPLE", "banana"]
El orden case-first afecta a la colación cuando las palabras son idénticas excepto por las mayúsculas y minúsculas. Determina el orden de clasificación secundario después de comparar los caracteres base.
Extensiones de colación numérica
La clave kn habilita la colación numérica, que ordena las secuencias numéricas por su valor numérico en lugar de lexicográficamente. Sin la colación numérica, "10" se ordena antes que "2" porque "1" viene antes que "2" en el orden de caracteres.
La colación numérica acepta dos valores:
truepara habilitar la colación numéricafalsepara deshabilitar la colación numérica (predeterminado)
const items = ["item1", "item10", "item2", "item20"];
const standard = new Intl.Collator("en-US");
const numeric = new Intl.Collator("en-US-u-kn-true");
console.log(items.sort((a, b) => standard.compare(a, b)));
// Output: ["item1", "item10", "item2", "item20"]
console.log(items.sort((a, b) => numeric.compare(a, b)));
// Output: ["item1", "item2", "item10", "item20"]
Con la colación numérica habilitada, "item2" se ordena correctamente antes que "item10" porque 2 es menor que 10. Esto produce el orden de clasificación esperado para cadenas que contienen números.
La colación numérica es útil para ordenar nombres de archivos, números de versión, direcciones y cualquier texto que contenga números integrados.
Uso de objetos de opciones en lugar de cadenas de extensión
En lugar de codificar extensiones en la cadena de configuración regional, puedes pasarlas como opciones al constructor Intl.Locale. Este enfoque separa la configuración regional base de las preferencias de formato.
const locale = new Intl.Locale("ja-JP", {
calendar: "japanese",
numberingSystem: "jpan",
hourCycle: "h11"
});
console.log(locale.toString());
// Salida: "ja-JP-u-ca-japanese-hc-h11-nu-jpan"
El constructor convierte automáticamente las opciones en etiquetas de extensión. Ambos enfoques producen objetos de configuración regional idénticos.
El enfoque de objeto de opciones ofrece varios beneficios. Hace que el código sea más legible al usar nombres completos de propiedades en lugar de códigos de dos letras. También facilita la construcción dinámica de configuraciones regionales a partir de datos de configuración.
const userPreferences = {
language: "ar",
region: "SA",
calendar: "islamic",
numberingSystem: "arab"
};
const locale = new Intl.Locale(`${userPreferences.language}-${userPreferences.region}`, {
calendar: userPreferences.calendar,
numberingSystem: userPreferences.numberingSystem
});
También puedes pasar opciones directamente a los constructores de formateadores:
const formatter = new Intl.DateTimeFormat("th-TH", {
calendar: "buddhist",
numberingSystem: "thai",
year: "numeric",
month: "long",
day: "numeric"
});
Esto combina opciones de formato específicas de la configuración regional con opciones de presentación en una sola llamada al constructor.
Cuándo usar extensiones versus opciones de formateador
Las extensiones y las opciones de formateador sirven para diferentes propósitos. Entender cuándo usar cada enfoque te ayuda a escribir código más limpio y mantenible.
Utiliza extensiones en la cadena de configuración regional cuando las preferencias de formato son inherentes a la configuración regional del usuario. Si un usuario tailandés siempre quiere ver el calendario budista y los numerales tailandeses, codifica esas preferencias en su identificador de configuración regional.
const userLocale = "th-TH-u-ca-buddhist-nu-thai";
Esto te permite pasar la configuración regional a cualquier formateador sin repetir las preferencias:
const dateFormatter = new Intl.DateTimeFormat(userLocale);
const numberFormatter = new Intl.NumberFormat(userLocale);
Ambos formateadores utilizan automáticamente el calendario budista y los numerales tailandeses.
Utiliza opciones de formateador cuando las preferencias de formato son específicas para un caso de uso. Si quieres mostrar el calendario islámico en una parte de tu aplicación pero el calendario gregoriano en otra, pasa la opción de calendario al formateador específico.
const islamicFormatter = new Intl.DateTimeFormat("ar-SA", {
calendar: "islamic"
});
const gregorianFormatter = new Intl.DateTimeFormat("ar-SA", {
calendar: "gregory"
});
El mismo identificador de configuración regional produce diferentes formatos basados en la opción de calendario.
Las extensiones en la cadena de configuración regional actúan como valores predeterminados. Las opciones de formateador anulan esos valores predeterminados cuando se especifican. Esto te permite usar las preferencias del usuario como base mientras personalizas formateadores específicos.
const locale = "en-US-u-hc-h23";
const formatter12Hour = new Intl.DateTimeFormat(locale, {
hourCycle: "h12"
});
El usuario prefiere el formato de 24 horas, pero este formateador específico anula esa preferencia para mostrar el formato de 12 horas.
Lectura de valores de extensión desde locales
El objeto Intl.Locale expone los valores de extensión como propiedades. Puedes leer estas propiedades para inspeccionar o validar las preferencias de formato de un locale.
const locale = new Intl.Locale("ar-SA-u-ca-islamic-nu-arab-hc-h12");
console.log(locale.calendar); // "islamic"
console.log(locale.numberingSystem); // "arab"
console.log(locale.hourCycle); // "h12"
Estas propiedades devuelven los valores de extensión si están presentes, o undefined si la extensión no está especificada.
Puedes usar estas propiedades para construir interfaces de configuración o validar preferencias de usuario:
function describeLocalePreferences(localeString) {
const locale = new Intl.Locale(localeString);
return {
language: locale.language,
region: locale.region,
calendar: locale.calendar || "default",
numberingSystem: locale.numberingSystem || "default",
hourCycle: locale.hourCycle || "default"
};
}
console.log(describeLocalePreferences("th-TH-u-ca-buddhist-nu-thai"));
// Salida: { language: "th", region: "TH", calendar: "buddhist", numberingSystem: "thai", hourCycle: "default" }
Las propiedades collation, caseFirst y numeric corresponden a las claves de extensión co, kf y kn:
const locale = new Intl.Locale("de-DE-u-co-phonebk-kf-upper-kn-true");
console.log(locale.collation); // "phonebk"
console.log(locale.caseFirst); // "upper"
console.log(locale.numeric); // true
Observa que la propiedad numeric devuelve un booleano, no una cadena. El valor true indica que la ordenación numérica está habilitada.
Combinación de múltiples extensiones
Puedes combinar múltiples extensiones en un solo identificador de locale. Esto te permite especificar todas las preferencias de formato a la vez.
const locale = new Intl.Locale("ar-SA-u-ca-islamic-nu-arab-hc-h12-co-standard");
const dateFormatter = new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
const date = new Date("2025-03-15T14:30:00");
console.log(dateFormatter.format(date));
// La salida usa calendario islámico, números arábigo-índicos y formato de 12 horas
Cada clave de extensión puede aparecer solo una vez en una cadena de locale. Si especificas la misma clave varias veces, prevalece el último valor.
const locale = new Intl.Locale("en-US-u-hc-h23-hc-h12");
console.log(locale.hourCycle); // "h12"
Cuando construyas locales programáticamente, asegúrate de que cada clave de extensión aparezca una vez para evitar ambigüedades.
Casos de uso prácticos
Las extensiones Unicode resuelven problemas reales en aplicaciones internacionalizadas. Comprender los casos de uso comunes te ayuda a aplicar las extensiones de manera efectiva.
Almacenamiento de preferencias de usuario
Almacena las preferencias de formato del usuario en una sola cadena de configuración regional en lugar de múltiples campos de configuración:
function saveUserPreferences(userId, localeString) {
const locale = new Intl.Locale(localeString);
return {
userId,
language: locale.language,
region: locale.region,
localeString: locale.toString(),
preferences: {
calendar: locale.calendar,
numberingSystem: locale.numberingSystem,
hourCycle: locale.hourCycle
}
};
}
const preferences = saveUserPreferences(123, "ar-SA-u-ca-islamic-nu-arab-hc-h12");
Este enfoque almacena las preferencias de formato como una sola cadena mientras proporciona acceso estructurado a los componentes individuales.
Construcción de selectores de configuración regional
Permite a los usuarios elegir preferencias de formato a través de una interfaz de usuario mediante la construcción de cadenas de configuración regional con extensiones:
function buildLocaleFromUserInput(language, region, preferences) {
const options = {};
if (preferences.calendar) {
options.calendar = preferences.calendar;
}
if (preferences.numberingSystem) {
options.numberingSystem = preferences.numberingSystem;
}
if (preferences.hourCycle) {
options.hourCycle = preferences.hourCycle;
}
const locale = new Intl.Locale(`${language}-${region}`, options);
return locale.toString();
}
const userLocale = buildLocaleFromUserInput("th", "TH", {
calendar: "buddhist",
numberingSystem: "thai",
hourCycle: "h23"
});
console.log(userLocale);
// Output: "th-TH-u-ca-buddhist-hc-h23-nu-thai"
Respeto a los calendarios religiosos
Las aplicaciones que sirven a comunidades religiosas deben admitir sus sistemas de calendario:
function createReligiousCalendarFormatter(religion, baseLocale) {
const calendars = {
jewish: "hebrew",
muslim: "islamic",
buddhist: "buddhist"
};
const calendar = calendars[religion];
if (!calendar) {
return new Intl.DateTimeFormat(baseLocale);
}
const locale = new Intl.Locale(baseLocale, { calendar });
return new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "long",
day: "numeric"
});
}
const jewishFormatter = createReligiousCalendarFormatter("jewish", "en-US");
console.log(jewishFormatter.format(new Date("2025-03-15")));
// Output: "15 Adar II 5785"
Ordenación con reglas personalizadas
Utiliza extensiones de cotejo para implementar la ordenación específica de la configuración regional:
function sortNames(names, locale, collationType) {
const localeWithCollation = new Intl.Locale(locale, {
collation: collationType
});
const collator = new Intl.Collator(localeWithCollation);
return names.sort((a, b) => collator.compare(a, b));
}
const germanNames = ["Müller", "Meyer", "Möller", "Mueller"];
const sorted = sortNames(germanNames, "de-DE", "phonebk");
console.log(sorted);
Mostrar numerales tradicionales
Muestra números en sistemas de numeración tradicionales para una visualización culturalmente apropiada:
function formatTraditionalNumber(number, locale, numberingSystem) {
const localeWithNumbering = new Intl.Locale(locale, {
numberingSystem
});
return new Intl.NumberFormat(localeWithNumbering).format(number);
}
console.log(formatTraditionalNumber(123456, "ar-EG", "arab"));
// Output: "١٢٣٬٤٥٦"
console.log(formatTraditionalNumber(123456, "th-TH", "thai"));
// Output: "๑๒๓,๔๕๖"
Compatibilidad con navegadores
Las extensiones Unicode funcionan en todos los navegadores modernos. Chrome, Firefox, Safari y Edge soportan la sintaxis de extensión en identificadores de localización y las propiedades correspondientes en objetos Intl.Locale.
La disponibilidad de valores específicos de extensión depende de la implementación del navegador. Todos los navegadores soportan valores comunes como gregory para calendario, latn para sistema de numeración, y h12 o h23 para ciclo horario. Valores menos comunes como calendarios chinos tradicionales o sistemas de numeración de idiomas minoritarios pueden no funcionar en todos los navegadores.
Prueba tus identificadores de localización en los navegadores objetivo cuando uses valores de extensión menos comunes. Utiliza las propiedades de Intl.Locale para comprobar si el navegador reconoció tus valores de extensión:
const locale = new Intl.Locale("zh-CN-u-ca-chinese");
console.log(locale.calendar);
// Si el navegador soporta el calendario chino: "chinese"
// Si el navegador no lo soporta: undefined
Node.js soporta extensiones Unicode a partir de la versión 12, con soporte completo para todas las propiedades en la versión 18 y posteriores.
Resumen
Las extensiones Unicode te permiten añadir preferencias de formato a los identificadores de localización. En lugar de configurar cada formateador por separado, codificas las preferencias una vez en la cadena de localización.
Conceptos clave:
- Las extensiones comienzan con
-u-seguido de pares clave-valor - La clave
caespecifica el sistema de calendario - La clave
nuespecifica el sistema de numeración - La clave
hcespecifica el formato de ciclo horario - La clave
coespecifica las reglas de ordenación - La clave
kfespecifica el ordenamiento por mayúsculas/minúsculas - La clave
knhabilita la ordenación numérica - Puedes usar cadenas de extensión u objetos de opciones
- Las extensiones actúan como valores predeterminados que las opciones del formateador pueden anular
- El objeto
Intl.Localeexpone las extensiones como propiedades
Utiliza las extensiones Unicode para almacenar preferencias de usuario, respetar calendarios culturales, mostrar numerales tradicionales e implementar ordenación específica por localización. Proporcionan una forma estándar de personalizar el comportamiento de formato en código de internacionalización de JavaScript.