¿Cómo formatear listas de medidas con unidades?

Muestra múltiples medidas como 5 km, 10 km, 15 km con formato de lista apropiado para cada configuración regional usando las API Intl de JavaScript

Introducción

Las aplicaciones que muestran medidas a menudo necesitan mostrar múltiples valores juntos. Una aplicación de fitness podría mostrar tiempos parciales como "5 km, 10 km, 15 km". Una aplicación meteorológica podría mostrar temperaturas a lo largo de la semana como "20 °C, 22 °C, 25 °C, 23 °C". Una receta podría listar cantidades de ingredientes como "2 tazas, 1 cucharada, 3 cucharaditas".

Estas listas combinan dos desafíos de internacionalización. Primero, cada medida necesita un formato de unidad apropiado para la configuración regional. Segundo, la lista en sí necesita puntuación y separadores adecuados para el idioma de destino. El inglés usa comas y a veces conjunciones como "and". Otros idiomas usan diferentes separadores y siguen diferentes reglas gramaticales.

JavaScript proporciona dos API para resolver este problema. Intl.NumberFormat formatea medidas individuales con unidades. Intl.ListFormat combina múltiples valores en una lista gramaticalmente correcta. Esta lección explica cómo usar ambas API juntas para formatear listas de medidas que coincidan con las expectativas del usuario en cualquier configuración regional.

Las listas de medidas requieren dos pasos de formato

Cuando formateas una lista de medidas, no puedes omitir ninguno de los pasos de formato. Si formateas la lista sin formatear las medidas, obtienes separadores apropiados para la configuración regional pero visualización incorrecta de unidades. Si formateas las medidas sin formatear la lista, obtienes unidades correctas pero separadores incorrectos.

const distances = [5, 10, 15];

// Wrong: list formatted but not measurements
console.log(distances.join(', '));
// Output: "5, 10, 15" (missing units)

// Wrong: measurements formatted but not list
const formatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});
console.log(distances.map(d => formatter.format(d)).join(', '));
// Output: "5 km, 10 km, 15 km" (hardcoded comma might be wrong for some locales)

El enfoque correcto formatea las medidas primero, luego formatea el array resultante de cadenas como una lista.

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const formattedMeasurements = distances.map(d => numberFormatter.format(d));
// Result: ["5 km", "10 km", "15 km"]

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedMeasurements));
// Output: "5 km, 10 km, 15 km"

Este patrón funciona para cualquier tipo de medida y cualquier configuración regional. Formateas cada medida con su unidad, luego formateas el array de cadenas formateadas como una lista.

Usar tipo unit para listas de mediciones

El constructor Intl.ListFormat acepta una opción type que controla cómo se combinan los elementos de la lista. La opción type: 'unit' formatea listas siguiendo las convenciones para datos técnicos y científicos.

const measurements = ['5 km', '10 km', '15 km'];

const unitList = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(unitList.format(measurements));
// Output: "5 km, 10 km, 15 km"

Las listas con type: 'unit' omiten conjunciones como "y" o "o". Utilizan separadores simples entre elementos. Esto coincide con cómo se escriben típicamente las mediciones en contextos técnicos.

Compara esto con type: 'conjunction', que añade "y" antes del último elemento.

const measurements = ['5 km', '10 km', '15 km'];

const conjunctionList = new Intl.ListFormat('en-US', {
  type: 'conjunction'
});

console.log(conjunctionList.format(measurements));
// Output: "5 km, 10 km, and 15 km"

La forma con conjunción se lee naturalmente en prosa pero parece incorrecta en contextos técnicos. Al mostrar múltiples mediciones, usa type: 'unit' para seguir las convenciones estándar de escritura científica y técnica.

Formatear mediciones de distancia en listas

Las mediciones de distancia utilizan identificadores de unidad como kilometer, meter, mile y foot. Después de formatear cada distancia con su unidad, combínalas en una lista.

const distances = [5, 10, 15, 20];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "5 km, 10 km, 15 km, 20 km"

El mismo patrón funciona para millas.

const distances = [3, 6, 9];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'mile'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "3 mi, 6 mi, 9 mi"

Puedes formatear distancias con decimales estableciendo opciones de formato numérico.

const distances = [5.2, 10.7, 15.3];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  maximumFractionDigits: 1
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "5.2 km, 10.7 km, 15.3 km"

El formateador de números maneja el redondeo y los decimales antes de que el formateador de listas combine los valores.

Formatear mediciones de peso en listas

Las mediciones de peso siguen el mismo patrón utilizando identificadores de unidad como kilogram, pound, ounce y gram.

const weights = [50, 75, 100];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilogram'
});

const formattedWeights = weights.map(w => numberFormatter.format(w));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedWeights));
// Output: "50 kg, 75 kg, 100 kg"

Puedes mostrar el peso en libras en su lugar.

const weights = [110, 165, 220];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'pound'
});

const formattedWeights = weights.map(w => numberFormatter.format(w));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedWeights));
// Output: "110 lb, 165 lb, 220 lb"

El formateador de números utiliza automáticamente la abreviatura correcta para cada unidad.

Formatear mediciones de temperatura en listas

Las mediciones de temperatura utilizan identificadores de unidad como celsius y fahrenheit.

const temperatures = [20, 22, 25, 23, 21];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'celsius'
});

const formattedTemperatures = temperatures.map(t => numberFormatter.format(t));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedTemperatures));
// Output: "20°C, 22°C, 25°C, 23°C, 21°C"

Los formateadores de temperatura incluyen automáticamente símbolos de grado en la salida.

Fahrenheit funciona de la misma manera.

const temperatures = [68, 72, 77, 73, 70];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'fahrenheit'
});

const formattedTemperatures = temperatures.map(t => numberFormatter.format(t));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedTemperatures));
// Output: "68°F, 72°F, 77°F, 73°F, 70°F"

El patrón permanece idéntico en diferentes tipos de medición. Solo cambia el identificador de unidad.

Formatear mediciones de volumen en listas

Las mediciones de volumen utilizan identificadores de unidad como liter, gallon, milliliter y fluid-ounce.

const volumes = [1, 2, 3];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'liter'
});

const formattedVolumes = volumes.map(v => numberFormatter.format(v));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedVolumes));
// Output: "1 L, 2 L, 3 L"

Las mediciones de volumen funcionan con valores decimales.

const volumes = [0.5, 1.5, 2.5];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'liter',
  maximumFractionDigits: 1
});

const formattedVolumes = volumes.map(v => numberFormatter.format(v));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedVolumes));
// Output: "0.5 L, 1.5 L, 2.5 L"

El formateador de números maneja la precisión decimal antes de que el formateador de listas procese los valores.

Formatear mediciones de velocidad en listas

Las mediciones de velocidad utilizan unidades compuestas como kilometer-per-hour y mile-per-hour.

const speeds = [50, 75, 100];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer-per-hour'
});

const formattedSpeeds = speeds.map(s => numberFormatter.format(s));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedSpeeds));
// Output: "50 km/h, 75 km/h, 100 km/h"

Millas por hora funciona de la misma manera.

const speeds = [30, 45, 60];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'mile-per-hour'
});

const formattedSpeeds = speeds.map(s => numberFormatter.format(s));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedSpeeds));
// Output: "30 mph, 45 mph, 60 mph"

Las unidades compuestas se formatean automáticamente con las abreviaturas y separadores correctos.

La configuración regional determina el formato del separador de lista

El parámetro de configuración regional controla cómo se separan y puntúan los elementos de la lista. Diferentes idiomas utilizan diferentes convenciones para el formato de listas.

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const enList = new Intl.ListFormat('en-US', {
  type: 'unit'
});

const frList = new Intl.ListFormat('fr-FR', {
  type: 'unit'
});

const deList = new Intl.ListFormat('de-DE', {
  type: 'unit'
});

console.log(enList.format(formattedDistances));
// Output: "5 km, 10 km, 15 km"

console.log(frList.format(formattedDistances));
// Output: "5 km, 10 km, 15 km"

console.log(deList.format(formattedDistances));
// Output: "5 km, 10 km, 15 km"

Aunque las abreviaturas de kilómetros permanecen similares en estas configuraciones regionales, el espaciado y las convenciones de separadores pueden variar. La API Intl.ListFormat maneja estas reglas de formato específicas de cada configuración regional automáticamente.

Algunos idiomas utilizan diferentes separadores o patrones de puntuación para las listas. La API garantiza que tus listas sigan las convenciones correctas para cada configuración regional sin que necesites conocer las reglas específicas.

Hacer coincidir la configuración regional del número con la de la lista

Al formatear listas de mediciones, utiliza la misma configuración regional tanto para el formateador de números como para el formateador de listas. Esto garantiza un formato consistente en toda la salida.

const distances = [1000, 2000, 3000];

const locale = 'de-DE';

const numberFormatter = new Intl.NumberFormat(locale, {
  style: 'unit',
  unit: 'meter'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat(locale, {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "1.000 m, 2.000 m, 3.000 m"

El formato alemán utiliza puntos como separadores de miles. Tanto el formateador de números como el formateador de listas utilizan convenciones alemanas porque comparten la misma configuración regional.

Utilizar diferentes configuraciones regionales para el formato de números y listas crea una salida inconsistente.

const distances = [1000, 2000, 3000];

const numberFormatter = new Intl.NumberFormat('de-DE', {
  style: 'unit',
  unit: 'meter'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "1.000 m, 2.000 m, 3.000 m"

Esto crea un formato mixto donde los números usan las convenciones alemanas pero la lista usa las convenciones inglesas. Usa siempre la misma configuración regional para ambos formateadores.

Controlar el estilo de visualización de la lista

La opción style controla cuán detallado aparece el formato de la lista. La opción acepta tres valores: "long", "short" y "narrow".

const measurements = ['5 km', '10 km', '15 km'];

const longList = new Intl.ListFormat('en-US', {
  type: 'unit',
  style: 'long'
});

const shortList = new Intl.ListFormat('en-US', {
  type: 'unit',
  style: 'short'
});

const narrowList = new Intl.ListFormat('en-US', {
  type: 'unit',
  style: 'narrow'
});

console.log(longList.format(measurements));
// Output: "5 km, 10 km, 15 km"

console.log(shortList.format(measurements));
// Output: "5 km, 10 km, 15 km"

console.log(narrowList.format(measurements));
// Output: "5 km 10 km 15 km"

Los estilos long y short producen resultados similares para listas de unidades en inglés. El estilo narrow usa un espaciado mínimo y omite los separadores entre elementos.

Las diferentes configuraciones regionales muestran más variación entre estilos. La configuración regional determina el formato exacto para cada nivel de estilo.

Combinar con nombres de unidades largos

Puedes formatear medidas con nombres de unidades completos en lugar de abreviaturas estableciendo unitDisplay: 'long' en el formateador de números.

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "5 kilometers, 10 kilometers, 15 kilometers"

El formateador de números maneja automáticamente las formas singular y plural. El formateador de listas combina las cadenas formateadas independientemente de si usan abreviaturas o nombres completos.

const distances = [1, 5];

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output: "1 kilometer, 5 kilometers"

El formateador de números usa "kilómetro" para 1 y "kilómetros" para 5. El formateador de listas los combina con los separadores apropiados.

Reutilizar formateadores para mejor rendimiento

Crear instancias de Intl.NumberFormat y Intl.ListFormat implica cargar datos de configuración regional y procesar opciones. Cuando formateas múltiples listas de medidas, crea los formateadores una vez y reutilízalos.

const numberFormatter = new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer'
});

const listFormatter = new Intl.ListFormat('en-US', {
  type: 'unit'
});

const distanceLists = [
  [5, 10, 15],
  [20, 25, 30],
  [35, 40, 45]
];

distanceLists.forEach(distances => {
  const formattedDistances = distances.map(d => numberFormatter.format(d));
  console.log(listFormatter.format(formattedDistances));
});
// Output:
// "5 km, 10 km, 15 km"
// "20 km, 25 km, 30 km"
// "35 km, 40 km, 45 km"

Este patrón crea cada formateador una vez y lo usa múltiples veces. La diferencia de rendimiento se vuelve significativa al formatear muchas listas.

Crear una función de formateador reutilizable

Puedes encapsular el patrón de formato de dos pasos en una función reutilizable.

function formatMeasurementList(values, locale, unit) {
  const numberFormatter = new Intl.NumberFormat(locale, {
    style: 'unit',
    unit: unit
  });

  const formattedValues = values.map(v => numberFormatter.format(v));

  const listFormatter = new Intl.ListFormat(locale, {
    type: 'unit'
  });

  return listFormatter.format(formattedValues);
}

console.log(formatMeasurementList([5, 10, 15], 'en-US', 'kilometer'));
// Output: "5 km, 10 km, 15 km"

console.log(formatMeasurementList([50, 75, 100], 'en-US', 'kilogram'));
// Output: "50 kg, 75 kg, 100 kg"

console.log(formatMeasurementList([20, 22, 25], 'en-US', 'celsius'));
// Output: "20°C, 22°C, 25°C"

Esta función maneja cualquier tipo de medida y configuración regional. Puedes extenderla para aceptar opciones de formato adicionales.

function formatMeasurementList(values, locale, unit, options = {}) {
  const numberFormatter = new Intl.NumberFormat(locale, {
    style: 'unit',
    unit: unit,
    ...options
  });

  const formattedValues = values.map(v => numberFormatter.format(v));

  const listFormatter = new Intl.ListFormat(locale, {
    type: 'unit'
  });

  return listFormatter.format(formattedValues);
}

console.log(formatMeasurementList(
  [5.123, 10.789, 15.456],
  'en-US',
  'kilometer',
  { maximumFractionDigits: 1 }
));
// Output: "5.1 km, 10.8 km, 15.5 km"

console.log(formatMeasurementList(
  [1, 5, 10],
  'en-US',
  'kilometer',
  { unitDisplay: 'long' }
));
// Output: "1 kilometer, 5 kilometers, 10 kilometers"

La función pasa opciones adicionales al formateador de números, permitiendo controlar los decimales, la visualización de unidades y otros ajustes de formato.

Formatear listas para la configuración regional del usuario

En lugar de codificar una configuración regional específica, puedes usar las preferencias de idioma del navegador del usuario. La propiedad navigator.language devuelve la configuración regional preferida del usuario.

const userLocale = navigator.language;

const distances = [5, 10, 15];

const numberFormatter = new Intl.NumberFormat(userLocale, {
  style: 'unit',
  unit: 'kilometer'
});

const formattedDistances = distances.map(d => numberFormatter.format(d));

const listFormatter = new Intl.ListFormat(userLocale, {
  type: 'unit'
});

console.log(listFormatter.format(formattedDistances));
// Output varies by user's locale

Este enfoque muestra listas de mediciones según las expectativas de formato de cada usuario. Diferentes usuarios ven los mismos datos formateados según las convenciones de su configuración regional.

Mostrar listas de mediciones en aplicaciones

Puedes usar este patrón en cualquier lugar donde muestres múltiples mediciones a los usuarios. Esto incluye aplicaciones de fitness que muestran tiempos parciales, aplicaciones meteorológicas que muestran pronósticos de temperatura, aplicaciones de recetas que muestran cantidades de ingredientes y aplicaciones científicas que muestran datos experimentales.

const splitTimes = [5, 10, 15, 20];

const numberFormatter = new Intl.NumberFormat(navigator.language, {
  style: 'unit',
  unit: 'kilometer',
  maximumFractionDigits: 1
});

const formattedTimes = splitTimes.map(t => numberFormatter.format(t));

const listFormatter = new Intl.ListFormat(navigator.language, {
  type: 'unit'
});

const result = listFormatter.format(formattedTimes);

document.getElementById('split-times').textContent = result;
// Displays: "5 km, 10 km, 15 km, 20 km" (or locale equivalent)

Las cadenas formateadas funcionan como cualquier otro valor de cadena. Puedes insertarlas en contenido de texto, atributos o cualquier contexto donde muestres información a los usuarios.