Cómo formatear números para diferentes locales en Next.js (Enrutador de Páginas) v16
Mostrar números con separadores específicos según el locale
Problema
Los números se escriben de manera diferente en todo el mundo. Lo que aparece como 10,000.5 en Estados Unidos se convierte en 10.000,5 en Alemania—las comas y los puntos intercambian roles por completo. Esto no es una cuestión de preferencia o estilo, sino de legibilidad. Un usuario alemán que ve 10,000.5 podría leerlo como diez, ignorando los separadores de agrupación. Un usuario estadounidense que ve 10.000,5 podría leerlo como diez mil, ignorando el separador decimal. Los mismos dígitos, significados opuestos.
Cuando las aplicaciones muestran números sin formato adaptado a la configuración regional, corren el riesgo de confundir a los usuarios o transmitir información incorrecta. Los importes de moneda, porcentajes, medidas y estadísticas dependen de convenciones regionales que los usuarios han aprendido desde la infancia.
Solución
Formatear números basándose en la configuración regional del usuario, utilizando reglas regionales para separadores decimales y de agrupación. Esto convierte los valores numéricos en cadenas que siguen las reglas de formato familiares para los usuarios en su región.
react-intl proporciona dos enfoques: el componente <FormattedNumber> para el formateo declarativo en JSX, y el método formatNumber del hook useIntl para el formateo imperativo. Ambos se basan en la API Intl.NumberFormat del navegador y aplican automáticamente la configuración regional configurada en tu IntlProvider. El resultado formateado respeta las convenciones regionales para separadores de miles, puntos decimales y agrupación de dígitos.
Pasos
1. Crear un componente que formatee números de manera declarativa
El componente <FormattedNumber> utiliza las APIs formatNumber e Intl.NumberFormat y acepta una prop value junto con opciones que corresponden a Intl.NumberFormatOptions.
import { FormattedNumber } from "react-intl";
export default function ProductPrice({ price }: { price: number }) {
return (
<div>
<FormattedNumber value={price} />
</div>
);
}
Por defecto, <FormattedNumber> renderiza el número formateado en un React.Fragment. El componente lee la configuración regional del IntlProvider más cercano y aplica las reglas de formato apropiadas.
2. Formatear números con opciones específicas
Pasa opciones de formato para controlar cómo aparecen los números. Las opciones comunes incluyen minimumFractionDigits, maximumFractionDigits y style.
import { FormattedNumber } from "react-intl";
export default function Statistics({ value }: { value: number }) {
return (
<dl>
<dt>Total de usuarios</dt>
<dd>
<FormattedNumber
value={value}
minimumFractionDigits={0}
maximumFractionDigits={0}
/>
</dd>
<dt>Tasa de conversión</dt>
<dd>
<FormattedNumber
value={value / 100}
style="percent"
minimumFractionDigits={2}
/>
</dd>
</dl>
);
}
Estas propiedades corresponden a Intl.NumberFormatOptions, dándote control sobre la precisión y presentación mientras mantienes los separadores específicos del idioma.
3. Formatear números imperativamente con el hook useIntl
Cuando un componente puede expresarse como un componente funcional, el hook useIntl proporciona acceso al objeto intl, que incluye un método formatNumber.
import { useIntl } from "react-intl";
export default function DataTable({ rows }: { rows: number[] }) {
const intl = useIntl();
return (
<table>
<tbody>
{rows.map((value, index) => (
<tr key={index}>
<td>{intl.formatNumber(value)}</td>
</tr>
))}
</tbody>
</table>
);
}
La función formatNumber devuelve una cadena de número formateada y acepta un valor que puede ser analizado como un número, junto con opciones que se ajustan a NumberFormatOptions.
4. Formatear números en contextos que no son JSX
Usa formatNumber cuando necesites números formateados en atributos, variables u otros contextos donde no se pueden usar componentes.
import { useIntl } from "react-intl";
export default function Chart({ dataPoints }: { dataPoints: number[] }) {
const intl = useIntl();
const formattedLabel = intl.formatNumber(dataPoints[0], {
notation: "compact",
maximumFractionDigits: 1,
});
return (
<div>
<img
src="/chart.png"
alt={`Gráfico mostrando ${formattedLabel} elementos`}
title={formattedLabel}
/>
</div>
);
}
La API imperativa es esencial para establecer atributos de texto como title, aria-label o alt, donde los componentes de React no pueden ser renderizados.