Cómo dividir la salida formateada en piezas para aplicar estilos
Usa formatToParts() para acceder a componentes individuales de la salida formateada para personalizar estilos
Introducción
El método format() en los formateadores de JavaScript devuelve cadenas completas como "$1,234.56" o "January 15, 2025". Esto funciona bien para visualizaciones simples, pero no permite aplicar estilos diferentes a partes individuales. No puedes hacer que el símbolo de moneda esté en negrita, colorear el nombre del mes 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 única cadena, devuelve un array de objetos, cada uno representando una parte de la salida formateada. Cada parte tiene un tipo como currency, month o element, y un valor que contiene la cadena real. Luego puedes procesar estas partes para aplicar estilos personalizados, construir diseños complejos o integrar contenido formateado en interfaces de usuario enriquecidas.
El método formatToParts() está disponible en múltiples formateadores Intl, incluyendo NumberFormat, DateTimeFormat, ListFormat, RelativeTimeFormat y DurationFormat. Esto lo convierte en un patrón consistente en toda la formatación de internacionalización en JavaScript.
Por qué las cadenas formateadas no pueden estilizarse fácilmente
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 locales colocan los símbolos en diferentes posiciones. Algunos locales utilizan diferentes separadores. Analizar estas cadenas de manera fiable requiere una lógica compleja que duplica las reglas de formateo ya implementadas en la API Intl.
Considera un panel 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 diferentes locales
- 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 formateo del locale rompe tu lógica de análisis.
El mismo problema existe para fechas, listas y otras salidas formateadas. No puedes analizar de manera fiable las cadenas formateadas para identificar componentes sin reimplementar las reglas de formateo específicas del locale.
El método formatToParts() elimina este problema proporcionando los componentes por separado. Recibes datos estructurados que te indican exactamente qué parte es cada una, independientemente del locale.
Cómo funciona formatToParts
El método formatToParts() funciona de manera idéntica a format() excepto por su valor de retorno. Creas un formateador con las mismas opciones, luego llamas a formatToParts() en lugar de format().
El método devuelve un array de objetos. Cada objeto contiene dos propiedades:
type: Identifica lo que representa la parte, comocurrency,month, oliteralvalue: Contiene la cadena real para esa parte
Las partes aparecen en el mismo orden en que aparecerían en la salida formateada. Puedes verificar esto uniendo todos los valores, lo que produce exactamente la misma salida que al llamar a format().
Este patrón es consistente en todos los formateadores que soportan formatToParts(). Los tipos de partes específicos varían según el formateador, pero la estructura es siempre la misma.
Dividiendo números formateados en partes
El formateador NumberFormat proporciona formatToParts() para desglosar números formateados. Esto funciona para números básicos, monedas, porcentajes y otros estilos numéricos.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
const parts = formatter.formatToParts(1234.56);
console.log(parts);
Esto produce 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 identifica lo que representa la parte y proporciona su valor. El tipo currency representa el símbolo de moneda. El tipo integer representa los dígitos de números enteros. 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.
Puedes verificar que las partes coincidan con la salida formateada:
const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// Salida: "$1,234.56"
Las partes concatenadas producen exactamente la misma salida que al llamar a format().
Estilización de símbolos de moneda en números formateados
El caso de uso principal para formatToParts() es aplicar diferentes estilos a diferentes componentes. Puedes procesar el array de partes para envolver tipos específicos en elementos HTML.
Haciendo que el símbolo de moneda aparezca en negrita:
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);
// Resultado: "<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.
Estilizando las partes decimales de manera 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);
// Resultado: "1,234<span class="text-gray-500">.50</span>"
Este patrón es común en la visualización de precios donde la parte decimal aparece más pequeña o más clara.
Dividiendo fechas formateadas en partes
El formateador DateTimeFormat proporciona formatToParts() para desglosar fechas y horas formateadas.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
Esto produce un array de objetos:
[
{ type: "month", value: "January" },
{ type: "literal", value: " " },
{ type: "day", value: "15" },
{ type: "literal", value: ", " },
{ type: "year", value: "2025" }
]
El tipo month representa el nombre o número del mes. El tipo day representa el día del mes. El tipo year representa el año. El tipo literal representa espacios, puntuación u otro texto insertado por el formateador.
Aplicar estilo a los nombres de los meses en fechas formateadas
Puedes aplicar estilos personalizados a los componentes de fecha utilizando el mismo patrón que con los números.
Hacer que el nombre del mes aparezca en negrita:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
.map(part => {
if (part.type === "month") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// Resultado: "<strong>January</strong> 15, 2025"
Aplicar estilo a múltiples componentes de fecha:
const formatter = new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
.map(part => {
switch (part.type) {
case "weekday":
return `<span class="font-bold">${part.value}</span>`;
case "month":
return `<span class="text-blue-600">${part.value}</span>`;
case "year":
return `<span class="text-gray-500">${part.value}</span>`;
default:
return part.value;
}
})
.join("");
console.log(html);
// Resultado: "<span class="font-bold">Wednesday</span>, <span class="text-blue-600">January</span> 15, <span class="text-gray-500">2025</span>"
Este control granular permite aplicar estilos precisos a cada componente.
Dividir listas formateadas en partes
El formateador ListFormat proporciona formatToParts() para desglosar las listas formateadas.
const formatter = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction"
});
const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
console.log(parts);
Esto genera un array de objetos:
[
{ type: "element", value: "apples" },
{ type: "literal", value: ", " },
{ type: "element", value: "oranges" },
{ type: "literal", value: ", and " },
{ type: "element", value: "bananas" }
]
El tipo element representa cada elemento de la lista. El tipo literal representa los separadores y conjunciones añadidos por el formateador.
Aplicar estilo a elementos de lista individualmente
Puedes aplicar estilos personalizados a elementos de lista utilizando el mismo patrón.
Hacer que los elementos de la lista estén en negrita:
const formatter = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction"
});
const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
const html = parts
.map(part => {
if (part.type === "element") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// Output: "<strong>apples</strong>, <strong>oranges</strong>, and <strong>bananas</strong>"
Aplicar estilo a elementos específicos de la lista:
const formatter = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction"
});
const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
let itemIndex = 0;
const html = parts
.map(part => {
if (part.type === "element") {
const currentIndex = itemIndex++;
if (currentIndex === 0) {
return `<span class="text-green-600">${part.value}</span>`;
}
return part.value;
}
return part.value;
})
.join("");
console.log(html);
// Output: "<span class="text-green-600">apples</span>, oranges, and bananas"
Este enfoque te permite resaltar elementos específicos mientras mantienes el formato adecuado según la configuración regional.
Dividir tiempos relativos formateados en partes
El formateador RelativeTimeFormat proporciona formatToParts() para desglosar expresiones de tiempo relativo.
const formatter = new Intl.RelativeTimeFormat("en-US", {
numeric: "auto"
});
const parts = formatter.formatToParts(-1, "day");
console.log(parts);
Esto produce un array de objetos:
[
{ type: "literal", value: "yesterday" }
]
Para tiempos relativos numéricos:
const formatter = new Intl.RelativeTimeFormat("en-US", {
numeric: "always"
});
const parts = formatter.formatToParts(-3, "day");
console.log(parts);
// [
// { type: "integer", value: "3" },
// { type: "literal", value: " days ago" }
// ]
El tipo integer representa el valor numérico. El tipo literal representa la unidad de tiempo relativo y la dirección.
División de duraciones formateadas en partes
El formateador DurationFormat proporciona formatToParts() para desglosar las duraciones formateadas.
const formatter = new Intl.DurationFormat("en-US", {
style: "long"
});
const parts = formatter.formatToParts({
hours: 2,
minutes: 30,
seconds: 15
});
console.log(parts);
Esto genera un array de objetos similar a:
[
{ type: "integer", value: "2" },
{ type: "literal", value: " hours, " },
{ type: "integer", value: "30" },
{ type: "literal", value: " minutes, " },
{ type: "integer", value: "15" },
{ type: "literal", value: " seconds" }
]
El tipo integer representa valores numéricos. El tipo literal representa nombres de unidades y separadores.
Construcción de HTML a partir de partes formateadas
Puedes crear una función reutilizable que procese las partes y aplique reglas de estilo de manera consistente.
function formatWithStyles(parts, styleMap) {
return parts
.map(part => {
const style = styleMap[part.type];
if (style) {
return `<span class="${style}">${part.value}</span>`;
}
return part.value;
})
.join("");
}
const numberFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
const parts = numberFormatter.formatToParts(1234.56);
const html = formatWithStyles(parts, {
currency: "font-bold text-gray-700",
integer: "text-2xl",
fraction: "text-sm text-gray-500"
});
console.log(html);
// Output: "<span class="font-bold text-gray-700">$</span><span class="text-2xl">1</span>,<span class="text-2xl">234</span>.<span class="text-sm text-gray-500">56</span>"
Este patrón separa las reglas de estilo de la lógica de formateo, facilitando su mantenimiento y reutilización.
Comprensión del orden de las partes según la configuración regional
El array de partes mantiene automáticamente las reglas de formateo específicas de la configuración regional. Diferentes configuraciones regionales colocan los componentes en diferentes órdenes y utilizan diferentes formatos, 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 formateo 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 formateo se adapta automáticamente.
Creación de visualizaciones formateadas accesibles
Puedes usar formatToParts() para añadir atributos de accesibilidad a la salida formateada. Esto ayuda a los lectores de pantalla a anunciar los valores correctamente.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatAccessible(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(formatAccessible(1234.56));
// Output: "<span aria-label="1234.56 US dollars">$1,234.56</span>"
Esto asegura que los lectores de pantalla anuncien tanto el valor de visualización formateado como el valor numérico subyacente con el contexto adecuado.
Combinación de formatToParts con componentes de frameworks
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.
Cuándo usar formatToParts
Usa format() cuando necesites una cadena formateada simple sin ninguna personalización. Este es el caso común para la mayoría de los escenarios de visualización.
Usa formatToParts() cuando necesites:
- Aplicar diferentes estilos a diferentes partes de la salida formateada
- Construir HTML o JSX con contenido formateado
- Añadir atributos o metadatos a componentes específicos
- Integrar salida formateada en diseños complejos
- Procesar salida formateada programáticamente
- Crear diseños visuales personalizados que requieran control granular
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 valores por segundo, format() tiene mejor rendimiento.
Para la mayoría de las 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 personalizados o marcado, usa formatToParts().
Tipos de partes comunes entre formateadores
Los diferentes formateadores producen distintos tipos de partes, pero algunos tipos aparecen en múltiples formateadores:
literal: Espaciado, puntuación u otro texto añadido por el formateo. Aparece en fechas, números, listas y duraciones.integer: Dígitos de números enteros. Aparece en números, tiempos relativos y duraciones.decimal: Separador decimal. Aparece en números.fraction: Dígitos decimales. Aparece en números.
Los tipos específicos de formateadores incluyen:
- Números:
currency,group,percentSign,minusSign,plusSign,unit,compact,exponentInteger - Fechas:
weekday,era,year,month,day,hour,minute,second,dayPeriod,timeZoneName - Listas:
element - Tiempos relativos: Los valores numéricos aparecen como
integer, el texto aparece comoliteral
Comprender estos tipos te ayuda a escribir código de estilo que maneje correctamente cualquier salida de formateador.