Cómo obtener partes individuales de números formateados para visualización personalizada
Divide números formateados en componentes para aplicar estilos personalizados y construir interfaces complejas
Introducción
El método format() devuelve una cadena formateada completa como "$1,234.56" o "1.5M". Esto funciona bien para visualización simple, pero no puedes aplicar estilos diferentes a partes individuales. No puedes poner el símbolo de moneda en negrita, colorear la parte decimal de forma diferente o aplicar marcado personalizado a componentes específicos.
JavaScript proporciona el método formatToParts() para resolver este problema. En lugar de devolver una sola cadena, devuelve un array de objetos, cada uno representando una parte del número formateado. Cada parte tiene un tipo como currency, integer o decimal, y un valor como $, 1234 o .. Luego puedes procesar estas partes para aplicar estilos personalizados, construir diseños complejos o integrar números formateados en interfaces de usuario enriquecidas.
Por qué las cadenas formateadas son difíciles de personalizar
Cuando recibes una cadena formateada como "$1,234.56", no puedes identificar fácilmente dónde termina el símbolo de moneda y dónde comienza el número. Diferentes configuraciones regionales colocan los símbolos en posiciones diferentes. Algunas configuraciones regionales usan separadores diferentes. Analizar estas cadenas de forma confiable requiere lógica compleja que duplica las reglas de formato ya implementadas en la API Intl.
Considera un panel de control que muestra cantidades monetarias con el símbolo de moneda en un color diferente. Con format(), necesitarías:
- Detectar qué caracteres son el símbolo de moneda
- Tener en cuenta los espacios entre el símbolo y el número
- Manejar diferentes posiciones de símbolos en distintas configuraciones regionales
- Analizar la cadena cuidadosamente para evitar romper el número
Este enfoque es frágil y propenso a errores. Cualquier cambio en las reglas de formato de la configuración regional rompe tu lógica de análisis.
El método formatToParts() elimina este problema al proporcionar los componentes por separado. Recibes datos estructurados que te indican exactamente qué parte es cuál, independientemente de la configuración regional.
Usar formatToParts para obtener componentes numéricos
El método formatToParts() funciona de forma idéntica a format() excepto por su valor de retorno. Creas un formateador con las mismas opciones y luego llamas a formatToParts() en lugar de format().
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
const parts = formatter.formatToParts(1234.56);
console.log(parts);
Esto genera un array de objetos:
[
{ type: "currency", value: "$" },
{ type: "integer", value: "1" },
{ type: "group", value: "," },
{ type: "integer", value: "234" },
{ type: "decimal", value: "." },
{ type: "fraction", value: "56" }
]
Cada objeto contiene una propiedad type que identifica lo que representa la parte y una propiedad value que contiene la cadena real. Las partes aparecen en el mismo orden en que aparecerían en la salida formateada.
Puedes verificar esto uniendo todos los valores:
const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// Output: "$1,234.56"
Las partes concatenadas producen exactamente la misma salida que llamar a format().
Comprender los tipos de partes
La propiedad type identifica cada componente. Diferentes opciones de formato producen diferentes tipos de partes.
Para formato numérico básico:
const formatter = new Intl.NumberFormat("en-US");
const parts = formatter.formatToParts(1234.56);
console.log(parts);
// [
// { type: "integer", value: "1" },
// { type: "group", value: "," },
// { type: "integer", value: "234" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "56" }
// ]
El tipo integer representa la parte del número entero. Aparecen múltiples partes integer cuando los separadores de grupo dividen el número. El tipo group representa el separador de miles. El tipo decimal representa el punto decimal. El tipo fraction representa los dígitos después del decimal.
Para formato de moneda:
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "EUR"
});
const parts = formatter.formatToParts(1234.56);
console.log(parts);
// [
// { type: "currency", value: "€" },
// { type: "integer", value: "1" },
// { type: "group", value: "," },
// { type: "integer", value: "234" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "56" }
// ]
El tipo currency aparece antes o después del número según las convenciones de la configuración regional.
Para porcentajes:
const formatter = new Intl.NumberFormat("en-US", {
style: "percent"
});
const parts = formatter.formatToParts(0.1234);
console.log(parts);
// [
// { type: "integer", value: "12" },
// { type: "percentSign", value: "%" }
// ]
El tipo percentSign representa el símbolo de porcentaje.
Para notación compacta:
const formatter = new Intl.NumberFormat("en-US", {
notation: "compact"
});
const parts = formatter.formatToParts(1500000);
console.log(parts);
// [
// { type: "integer", value: "1" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "5" },
// { type: "compact", value: "M" }
// ]
El tipo compact representa el indicador de magnitud como K, M o B.
Aplicar estilos personalizados a las partes de números
El caso de uso principal de formatToParts() es aplicar diferentes estilos a diferentes componentes. Puedes procesar el array de partes para envolver tipos específicos en elementos HTML.
Poner en negrita el símbolo de moneda:
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
const parts = formatter.formatToParts(1234.56);
const html = parts
.map(part => {
if (part.type === "currency") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// Output: "<strong>$</strong>1,234.56"
Este enfoque funciona para cualquier lenguaje de marcado. Puedes generar HTML, JSX o cualquier otro formato procesando el array de partes.
Estilizar las porciones decimales de forma diferente:
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2
});
const parts = formatter.formatToParts(1234.5);
const html = parts
.map(part => {
if (part.type === "decimal" || part.type === "fraction") {
return `<span class="text-gray-500">${part.value}</span>`;
}
return part.value;
})
.join("");
console.log(html);
// Output: "1,234<span class="text-gray-500">.50</span>"
Este patrón es común en las visualizaciones de precios donde la porción decimal aparece más pequeña o más clara.
Codificar con colores los números negativos
Las aplicaciones financieras a menudo muestran los números negativos en rojo. Con formatToParts(), puedes detectar el signo menos y aplicar estilos en consecuencia.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatWithColor(number) {
const parts = formatter.formatToParts(number);
const hasMinusSign = parts.some(part => part.type === "minusSign");
const html = parts
.map(part => part.value)
.join("");
if (hasMinusSign) {
return `<span class="text-red-600">${html}</span>`;
}
return html;
}
console.log(formatWithColor(-1234.56));
// Output: "<span class="text-red-600">-$1,234.56</span>"
console.log(formatWithColor(1234.56));
// Output: "$1,234.56"
Este enfoque detecta números negativos de forma fiable en todas las configuraciones regionales, incluso aquellas que usan diferentes símbolos o posiciones para los indicadores negativos.
Construir visualizaciones de números personalizadas con múltiples estilos
Las interfaces complejas a menudo combinan múltiples reglas de estilo. Puedes aplicar diferentes clases o elementos a diferentes tipos de partes simultáneamente.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatCurrency(number) {
const parts = formatter.formatToParts(number);
return parts
.map(part => {
switch (part.type) {
case "currency":
return `<span class="currency-symbol">${part.value}</span>`;
case "integer":
return `<span class="integer">${part.value}</span>`;
case "group":
return `<span class="group">${part.value}</span>`;
case "decimal":
return `<span class="decimal">${part.value}</span>`;
case "fraction":
return `<span class="fraction">${part.value}</span>`;
case "minusSign":
return `<span class="minus">${part.value}</span>`;
default:
return part.value;
}
})
.join("");
}
console.log(formatCurrency(1234.56));
// Output: "<span class="currency-symbol">$</span><span class="integer">1</span><span class="group">,</span><span class="integer">234</span><span class="decimal">.</span><span class="fraction">56</span>"
Este control granular permite un estilo preciso para cada componente. Luego puedes usar CSS para estilizar cada clase de forma diferente.
Todos los tipos de partes disponibles
La propiedad type puede tener estos valores dependiendo de las opciones de formato utilizadas:
integer: dígitos de números enterosfraction: dígitos decimalesdecimal: separador decimalgroup: separador de milescurrency: símbolo de monedaliteral: espaciado u otro texto literal añadido por el formatopercentSign: símbolo de porcentajeminusSign: indicador de número negativoplusSign: indicador de número positivo (cuandosignDisplayestá configurado)unit: cadena de unidad para formato de unidadescompact: indicador de magnitud en notación compacta (K, M, B)exponentInteger: valor del exponente en notación científicaexponentMinusSign: signo negativo en el exponenteexponentSeparator: símbolo que separa la mantisa del exponenteinfinity: representación de infinitonan: representación de no-es-un-númerounknown: tokens no reconocidos
No todas las opciones de formato producen todos los tipos de partes. Las partes que recibes dependen del valor numérico y de la configuración del formateador.
La notación científica produce partes relacionadas con el exponente:
const formatter = new Intl.NumberFormat("en-US", {
notation: "scientific"
});
const parts = formatter.formatToParts(1234);
console.log(parts);
// [
// { type: "integer", value: "1" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "234" },
// { type: "exponentSeparator", value: "E" },
// { type: "exponentInteger", value: "3" }
// ]
Los valores especiales producen tipos de partes específicos:
const formatter = new Intl.NumberFormat("en-US");
console.log(formatter.formatToParts(Infinity));
// [{ type: "infinity", value: "∞" }]
console.log(formatter.formatToParts(NaN));
// [{ type: "nan", value: "NaN" }]
Crear visualizaciones de números accesibles
Puedes usar formatToParts() para añadir atributos de accesibilidad a números formateados. Esto ayuda a los lectores de pantalla a anunciar los valores correctamente.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatAccessibleCurrency(number) {
const parts = formatter.formatToParts(number);
const formatted = parts.map(part => part.value).join("");
return `<span aria-label="${number} US dollars">${formatted}</span>`;
}
console.log(formatAccessibleCurrency(1234.56));
// Output: "<span aria-label="1234.56 US dollars">$1,234.56</span>"
Esto garantiza que los lectores de pantalla anuncien tanto el valor de visualización formateado como el valor numérico subyacente con el contexto adecuado.
Resaltar rangos numéricos específicos
Algunas aplicaciones resaltan números que se encuentran dentro de ciertos rangos. Con formatToParts(), puedes aplicar estilos basados en el valor mientras mantienes el formato adecuado.
const formatter = new Intl.NumberFormat("en-US");
function formatWithThreshold(number, threshold) {
const parts = formatter.formatToParts(number);
const formatted = parts.map(part => part.value).join("");
if (number >= threshold) {
return `<span class="text-green-600 font-bold">${formatted}</span>`;
}
return formatted;
}
console.log(formatWithThreshold(1500, 1000));
// Output: "<span class="text-green-600 font-bold">1,500</span>"
console.log(formatWithThreshold(500, 1000));
// Output: "500"
El número recibe el formato adecuado para la configuración regional mientras se aplica el estilo condicional basado en la lógica de negocio.
Cuándo usar formatToParts versus format
Usa format() cuando necesites una cadena formateada simple sin ninguna personalización. Este es el caso común para la mayoría de visualizaciones de números.
Usa formatToParts() cuando necesites:
- Aplicar diferentes estilos a diferentes partes del número
- Construir HTML o JSX con números formateados
- Añadir atributos o metadatos a componentes específicos
- Integrar números formateados en diseños complejos
- Procesar la salida formateada de forma programática
El método formatToParts() tiene un poco más de sobrecarga que format() porque crea un array de objetos en lugar de una sola cadena. Esta diferencia es insignificante para aplicaciones típicas, pero si formateas miles de números por segundo, format() tiene mejor rendimiento.
Para la mayoría de aplicaciones, elige según tus necesidades de estilo en lugar de preocupaciones de rendimiento. Si no necesitas personalizar la salida, usa format(). Si necesitas estilos o marcado personalizados, usa formatToParts().
Cómo las partes preservan el formato específico de la configuración regional
El array de partes mantiene las reglas de formato específicas de la configuración regional automáticamente. Diferentes configuraciones regionales colocan los símbolos en diferentes posiciones y usan diferentes separadores, pero formatToParts() maneja estas diferencias.
const usdFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
console.log(usdFormatter.formatToParts(1234.56));
// [
// { type: "currency", value: "$" },
// { type: "integer", value: "1" },
// { type: "group", value: "," },
// { type: "integer", value: "234" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "56" }
// ]
const eurFormatter = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR"
});
console.log(eurFormatter.formatToParts(1234.56));
// [
// { type: "integer", value: "1" },
// { type: "group", value: "." },
// { type: "integer", value: "234" },
// { type: "decimal", value: "," },
// { type: "fraction", value: "56" },
// { type: "literal", value: " " },
// { type: "currency", value: "€" }
// ]
El formato alemán coloca la moneda después del número con un espacio. El separador de grupo es un punto y el separador decimal es una coma. Tu código de estilo procesa el array de partes de la misma manera independientemente de la configuración regional, y el formato se adapta automáticamente.
El tipo literal representa cualquier espacio o texto insertado por el formateador que no encaja en otras categorías. En el formato de moneda alemán, representa el espacio entre el número y el símbolo de moneda.
Combinación de formatToParts con componentes de framework
Los frameworks modernos como React pueden usar formatToParts() para construir componentes de manera eficiente.
function CurrencyDisplay({ value, locale, currency }) {
const formatter = new Intl.NumberFormat(locale, {
style: "currency",
currency: currency
});
const parts = formatter.formatToParts(value);
return (
<span className="currency-display">
{parts.map((part, index) => {
if (part.type === "currency") {
return <strong key={index}>{part.value}</strong>;
}
if (part.type === "fraction" || part.type === "decimal") {
return <span key={index} className="text-sm text-gray-500">{part.value}</span>;
}
return <span key={index}>{part.value}</span>;
})}
</span>
);
}
Este componente aplica diferentes estilos a diferentes partes mientras mantiene el formato adecuado para cualquier configuración regional y moneda.