La API de Internacionalización de JavaScript (Intl)
Resumen
La internacionalización (i18n) en JavaScript dependió históricamente de pesadas librerías externas como Moment.js o date-fns. La API Intl (disponible de forma nativa en todos los navegadores modernos) soluciona esto ofreciendo formateo numérico, manipulación de fechas, ordenamiento de strings y segmentación directamente en el motor de JavaScript.
Este artículo explora los constructores clave del espacio de nombres Intl, enseñando cómo adaptar tus aplicaciones a múltiples culturas con cero dependencias.
1. El objeto global Intl
El objeto global Intl actúa como un espacio de nombres (similar a Math). No puede instanciarse con new. En su lugar, aloja varios constructores especializados.
Casi todos estos constructores aceptan dos parámetros:
locales: un string con el código BCP 47 (ej."es-US","en-GB") o un array de ellos. Si se omite, usa el locale del sistema del usuario.options: un objeto de configuración para ajustar el comportamiento del constructor.
2. Formateo de Números y Monedas (Intl.NumberFormat)
El constructor Intl.NumberFormat formatea números basándose en convenciones regionales (separadores de miles, decimales, etc.).
2.1 Monedas y Porcentajes
Configurando la propiedad style, podemos formatear finanzas o unidades sin concatenar strings manualmente:
const monto = 123456.789;
// Formato español (España) - usa coma para decimales
const formatoES = new Intl.NumberFormat('es-ES');
console.log(formatoES.format(monto)); // "123.456,789"
// Formato de moneda (Euros)
const formatoEuro = new Intl.NumberFormat('es-ES', {
style: 'currency',
currency: 'EUR'
});
console.log(formatoEuro.format(monto)); // "123.456,79 €"
// Formato contable de EE. UU. (números negativos en paréntesis)
const formatoContable = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencySign: 'accounting'
});
console.log(formatoContable.format(-monto)); // "($123,456.79)"
2.2 Unidades Físicas
Podemos formatear unidades estandarizadas (kilómetros, litros, bytes) de forma nativa:
const velocidad = new Intl.NumberFormat('es-MX', {
style: 'unit',
unit: 'kilometer-per-hour',
unitDisplay: 'long'
});
console.log(velocidad.format(120)); // "120 kilómetros por hora"
3. Pluralización (Intl.PluralRules)
Los diferentes idiomas tienen reglas gramaticales de pluralización sumamente complejas. Intl.PluralRules no devuelve un string formateado, sino que clasifica un número en categorías ("zero", "one", "two", "few", "many", "other"), que puedes usar como llaves para tu sistema de traducciones.
const reglasArabe = new Intl.PluralRules('ar-EG');
console.log(reglasArabe.select(0)); // "zero"
console.log(reglasArabe.select(1)); // "one"
console.log(reglasArabe.select(2)); // "two"
console.log(reglasArabe.select(6)); // "few"
// Uso de ordinales en inglés (1st, 2nd, 3rd)
const reglasOrdinalesEN = new Intl.PluralRules('en-US', { type: 'ordinal' });
console.log(reglasOrdinalesEN.select(1)); // "one" (para el sufijo "st")
console.log(reglasOrdinalesEN.select(2)); // "two" (para el sufijo "nd")
console.log(reglasOrdinalesEN.select(3)); // "few" (para el sufijo "rd")
console.log(reglasOrdinalesEN.select(4)); // "other" (para el sufijo "th")
4. Manipulación del Tiempo
El tiempo se gestiona de forma integral a través de tres pilares en la API Intl:
4.1 Formateadores Absolutos: Intl.DateTimeFormat
Convierte fechas en texto humano según horarios y calendarios regionales. Soporta macros como dateStyle y timeStyle para simplificar opciones.
const fecha = new Date(Date.UTC(2026, 0, 10, 10, 0, 0));
const formatoCompleto = new Intl.DateTimeFormat('es-ES', {
dateStyle: 'full',
timeStyle: 'long',
timeZone: 'Europe/Madrid'
});
console.log(formatoCompleto.format(fecha));
// "sábado, 10 de enero de 2026, 11:00:00 CET"
// Soporta rangos sin redundancias temporales
const fin = new Date(Date.UTC(2026, 0, 12, 10, 0, 0));
const formatoRango = new Intl.DateTimeFormat('es-MX', { dateStyle: 'long' });
console.log(formatoRango.formatRange(fecha, fin));
// "10-12 de enero de 2026"
4.2 Tiempo Relativo: Intl.RelativeTimeFormat
Ideal para interfaces amigables (“hace 5 minutos”, “ayer”).
const tiempoRelativo = new Intl.RelativeTimeFormat('es-ES', { numeric: 'auto' });
console.log(tiempoRelativo.format(-1, 'day')); // "ayer"
console.log(tiempoRelativo.format(2, 'day')); // "pasado mañana"
console.log(tiempoRelativo.format(-3, 'month')); // "hace 3 meses"
4.3 Duraciones: Intl.DurationFormat
Muestra la duración de un lapso independientemente del contexto de la fecha. Recibe objetos de la nueva especificación Temporal.
const lapso = { hours: 2, minutes: 4, seconds: 35 };
const cronometro = new Intl.DurationFormat('en-US', {
style: 'digital',
hoursDisplay: 'always',
minutes: '2-digit'
});
console.log(cronometro.format(lapso)); // "2:04:35"
5. Manipulación de Cadenas y Datos
5.1 Ordenamiento Natural: Intl.Collator
Soluciona las deficiencias del .sort() predeterminado de los arrays, ordenando números e ignorando tildes correctamente.
const docs = ['archivo10.txt', 'archivo2.txt', 'Álbum.txt', 'Zebra.txt'];
// Array.sort() fallará al poner "10" antes del "2", y la "Á" al final.
// Intl.Collator lo arregla:
const colador = new Intl.Collator('es-ES', { numeric: true, sensitivity: 'base' });
docs.sort(colador.compare);
console.log(docs);
// ["Álbum.txt", "archivo2.txt", "archivo10.txt", "Zebra.txt"]
5.2 Formateo de Listas: Intl.ListFormat
Genera las conjunciones o disyunciones correctas según el idioma (“y”, “o”, u otras dependiendo del locale).
const ingredientes = ['Harina', 'Huevos', 'Azúcar'];
const formatterES = new Intl.ListFormat('es-ES', { type: 'conjunction' });
console.log(formatterES.format(ingredientes)); // "Harina, Huevos y Azúcar"
const formatterEN = new Intl.ListFormat('en-US', { type: 'conjunction' });
console.log(formatterEN.format(ingredientes)); // "Harina, Huevos, and Azúcar"
5.3 Segmentación Inteligente: Intl.Segmenter
Identificar los límites de las palabras usando .split(' ') falla con lenguajes logográficos (como japonés o chino), o con emojis compuestos de múltiples glifos.
const textoJapones = "吾輩は猫である。名前はたぬき。";
const segmentador = new Intl.Segmenter('ja-JP', { granularity: 'word' });
const segmentos = segmentador.segment(textoJapones);
for (const fragmento of segmentos) {
if (fragmento.isWordLike) {
console.log(fragmento.segment);
// Muestra "吾輩", "は", "猫", "で", "ある", "名前", "は", "たぬき"
}
}
5.4 Nombres de Regiones e Idiomas: Intl.DisplayNames
Evita cargar enormes diccionarios JSON traduciendo nombres de lenguajes, regiones geográficas o monedas de forma nativa.
const traductorRegiones = new Intl.DisplayNames(['es-ES'], { type: 'region' });
console.log(traductorRegiones.of('US')); // "Estados Unidos"
console.log(traductorRegiones.of('JP')); // "Japón"
const traductorIdiomas = new Intl.DisplayNames(['en-US'], { type: 'language' });
console.log(traductorIdiomas.of('es-419')); // "Latin American Spanish"
6. Rendimiento y Caching
Inicializar constructores del espacio Intl es una operación relativamente costosa en términos de rendimiento.
Si tu aplicación realiza el mismo formateo intensamente (por ejemplo al mapear miles de transacciones hacia la interfaz), nunca debes inicializar la clase dentro del ciclo iterativo.
Patrón recomendado de Caching:
const precios = [10.5, 20.9, 100, 450.2];
// ✅ CORRECTO: Instanciamos el objeto formateador UNA sola vez fuera del ciclo.
const formatoMXN = new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN' });
const renderizado = precios.map(precio => formatoMXN.format(precio));
7. Conclusión
La API de ECMAScript Intl representa el estándar dorado para la globalización de aplicaciones web y Node.js. Al transferir las dependencias algorítmicas hacia el motor del navegador o servidor, Intl elimina la recarga de librerías, garantizando aplicaciones más eficientes, robustas y resilientes ante las constantes modificaciones normativas mundiales.
Fuentes: MDN Web Docs, Especificación ECMA-402 API de Internacionalización.