Cómo formatear porcentajes con el signo de porcentaje

Utiliza el estilo percent en Intl.NumberFormat para mostrar números como porcentajes con formato apropiado según la configuración regional

Introducción

Los porcentajes aparecen en muchas aplicaciones, desde mostrar el progreso de finalización hasta mostrar tasas de interés y cantidades de descuento. Un enfoque simple como concatenar un número con un signo de porcentaje funciona para casos básicos, pero no tiene en cuenta cómo diferentes idiomas y regiones formatean los porcentajes. En turco, el signo de porcentaje aparece antes del número como %50. En francés, un espacio separa el número del signo de porcentaje como 50 %. El árabe utiliza un carácter de porcentaje especial en lugar del símbolo % estándar.

La API Intl.NumberFormat de JavaScript maneja estas diferencias de formato automáticamente. Al establecer la opción style en "percent", obtienes porcentajes correctamente formateados para cualquier configuración regional sin necesidad de escribir código específico para cada localización.

Por qué el formato de porcentaje varía según la configuración regional

La posición del signo de porcentaje y las reglas de espaciado difieren según las configuraciones regionales. El inglés coloca el signo de porcentaje directamente después del número sin espacio. El francés añade un espacio antes del signo de porcentaje. El turco coloca el signo de porcentaje antes del número. Estas variaciones reflejan los patrones de lectura naturales y las convenciones de cada idioma.

Las diferencias de formato se extienden más allá de la colocación del signo. Algunas configuraciones regionales utilizan caracteres diferentes para el símbolo de porcentaje. El árabe usa ٪ (U+066A) en lugar del signo de porcentaje ASCII. El separador decimal y el separador de miles también varían según la configuración regional, al igual que ocurre con el formato de números regulares.

Cuando codificas el formato de porcentaje con concatenación de cadenas, obligas a todos los usuarios a ver el formato de una única configuración regional. Un usuario francés que ve 50% en lugar de 50 % experimenta un formato no natural. Un usuario turco que ve 50% cuando espera %50 enfrenta el mismo problema. La API Intl resuelve esto aplicando las reglas de formato correctas para cada configuración regional.

Formateo de números como porcentajes

La opción style en Intl.NumberFormat controla si un número se formatea como un número simple, moneda, porcentaje o unidad. Establece style a "percent" para formatear números como porcentajes.

const formatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

console.log(formatter.format(0.75));
// Resultado: "75%"

console.log(formatter.format(0.05));
// Resultado: "5%"

console.log(formatter.format(1.5));
// Resultado: "150%"

El formateador multiplica automáticamente el valor de entrada por 100 y añade el signo de porcentaje. Esto significa que pasas valores fraccionarios al formateador. Un valor de 0.75 se convierte en 75%. Un valor de 1.5 se convierte en 150%.

Esta convención de entrada fraccionaria se alinea con cómo funcionan los porcentajes en matemáticas y en la mayoría de los cálculos de programación. Cuando calculas un porcentaje como tasa de crecimiento o ratio de finalización, el resultado es típicamente un valor decimal entre 0 y 1. Puedes pasar este valor directamente al formateador sin convertirlo primero a un porcentaje.

Entendiendo los valores de entrada fraccionarios

El formateador de porcentajes espera una entrada fraccionaria donde 1.0 representa el 100%. Esta elección de diseño coincide con cómo se calculan los porcentajes en el código.

const formatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

// Calculando porcentaje de finalización
const completed = 75;
const total = 100;
const ratio = completed / total;

console.log(formatter.format(ratio));
// Resultado: "75%"

// Calculando tasa de crecimiento
const oldValue = 1000;
const newValue = 1250;
const growth = (newValue - oldValue) / oldValue;

console.log(formatter.format(growth));
// Resultado: "25%"

Cuando divides completed entre total, obtienes 0.75. Pasa esto directamente al formateador, y se muestra como 75%. Cuando calculas el crecimiento como la proporción del cambio respecto al valor original, obtienes 0.25. El formateador muestra esto como 25%.

Este enfoque previene un error común donde los desarrolladores multiplican por 100 antes de formatear, resultando en valores como 7500% en lugar de 75%. El formateador maneja la multiplicación, así que trabajas con la representación decimal natural en tu código.

Manejo de valores fuera del rango 0-1

Los porcentajes no están limitados a valores entre 0% y 100%. Los valores por debajo de cero representan porcentajes negativos. Los valores por encima de 1.0 representan porcentajes mayores al 100%.

const formatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

console.log(formatter.format(-0.15));
// Output: "-15%"

console.log(formatter.format(2.5));
// Output: "250%"

console.log(formatter.format(0.0025));
// Output: "0%"

Los valores negativos se formatean con un signo menos. Esto es útil para mostrar disminuciones, pérdidas o tasas de crecimiento negativas. Los valores mayores a 1.0 se formatean como porcentajes superiores al 100%, lo cual es común para mostrar crecimiento interanual o comparaciones donde el nuevo valor excede la línea base.

Los valores muy pequeños podrían redondearse a 0% debido a la configuración de precisión predeterminada. El valor 0.0025, que representa 0.25%, se muestra como 0% porque el número máximo predeterminado de dígitos decimales para porcentajes es 0. La siguiente lección cubre cómo controlar los decimales para mostrar estos porcentajes más pequeños con precisión.

Formateo de porcentajes en diferentes locales

El formateo de porcentajes se adapta al locale que especifiques. Cada locale aplica sus propias reglas para la colocación de signos, espaciado y símbolos.

const enFormatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

console.log(enFormatter.format(0.5));
// Output: "50%"

const frFormatter = new Intl.NumberFormat("fr-FR", {
  style: "percent"
});

console.log(frFormatter.format(0.5));
// Output: "50 %"

const trFormatter = new Intl.NumberFormat("tr-TR", {
  style: "percent"
});

console.log(trFormatter.format(0.5));
// Output: "%50"

const arFormatter = new Intl.NumberFormat("ar-SA", {
  style: "percent"
});

console.log(arFormatter.format(0.5));
// Output: "٪50"

El inglés coloca el signo de porcentaje inmediatamente después del número. El francés añade un espacio antes del signo de porcentaje. El turco coloca el signo de porcentaje antes del número. El árabe utiliza el signo de porcentaje árabe y sigue la dirección de texto de derecha a izquierda.

Estas diferencias ocurren automáticamente según el parámetro de locale. No necesitas escribir lógica condicional para manejar diferentes locales. Crea un formateador con el locale del usuario, y producirá la salida correcta para ese locale.

Control de decimales en porcentajes

Por defecto, el formato de porcentaje no muestra decimales. Un valor como 0.755 se formatea como 76% después de redondear. Puedes controlar el número de decimales usando las opciones minimumFractionDigits y maximumFractionDigits.

const defaultFormatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

console.log(defaultFormatter.format(0.755));
// Output: "76%"

const oneDecimalFormatter = new Intl.NumberFormat("en-US", {
  style: "percent",
  minimumFractionDigits: 1,
  maximumFractionDigits: 1
});

console.log(oneDecimalFormatter.format(0.755));
// Output: "75.5%"

const twoDecimalFormatter = new Intl.NumberFormat("en-US", {
  style: "percent",
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

console.log(twoDecimalFormatter.format(0.755));
// Output: "75.50%"

La opción minimumFractionDigits asegura que el formateador muestre al menos esa cantidad de decimales, añadiendo ceros a la derecha si es necesario. La opción maximumFractionDigits limita el número de decimales, redondeando el valor si es necesario.

Establecer ambos al mismo valor asegura un formato consistente en todos los porcentajes. Esto es útil en tablas y gráficos donde se desea que todos los porcentajes se alineen visualmente.

La siguiente lección cubre el control de decimales con mayor profundidad, incluyendo cómo manejar ceros a la derecha y mostrar porcentajes muy pequeños con precisión.

Combinación de porcentajes con visualización de signos

El formato de porcentaje funciona con la opción signDisplay para mostrar explícitamente signos positivos para porcentajes positivos. Esto es útil cuando se muestran cambios o tasas de crecimiento donde el signo indica la dirección.

const formatter = new Intl.NumberFormat("en-US", {
  style: "percent",
  signDisplay: "always",
  minimumFractionDigits: 1,
  maximumFractionDigits: 1
});

console.log(formatter.format(0.0523));
// Output: "+5.2%"

console.log(formatter.format(-0.0341));
// Output: "-3.4%"

console.log(formatter.format(0));
// Output: "+0.0%"

La opción signDisplay se cubrió en una lección anterior. Cuando se combina con el formato de porcentaje, aclara si un porcentaje representa un aumento o una disminución. Sin el signo más explícito, los porcentajes positivos podrían ser ambiguos en contextos donde se muestran tanto ganancias como pérdidas.

Formateo de cero como porcentaje

El cero se formatea como 0% por defecto. Cómo manejar el cero depende de si representa ningún cambio, ningún valor o una medición significativa.

const formatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

console.log(formatter.format(0));
// Output: "0%"

const withSignFormatter = new Intl.NumberFormat("en-US", {
  style: "percent",
  signDisplay: "exceptZero"
});

console.log(withSignFormatter.format(0.05));
// Output: "+5%"

console.log(withSignFormatter.format(0));
// Output: "0%"

Usar signDisplay: "exceptZero" muestra signos para porcentajes positivos y negativos pero omite el signo para cero. Esto hace que el cero sea visualmente distinto cuando representa un estado neutral como ningún cambio.

Cuándo usar el formateo de porcentajes

Utiliza el formateo de porcentajes cuando muestres ratios, tasas o proporciones que se expresan naturalmente como porcentajes. Los casos de uso comunes incluyen progreso de finalización, tasas de interés, cantidades de descuento, tasas de crecimiento, tasas de éxito y valores de probabilidad.

No uses el formateo de porcentajes cuando necesites que el signo de porcentaje aparezca en una posición específica independientemente de la configuración regional. Si tu diseño requiere que el signo de porcentaje siempre aparezca a la derecha para mantener la consistencia visual, usa concatenación de cadenas en su lugar. Sin embargo, esto sacrifica la corrección de internacionalización.

También evita el formateo de porcentajes cuando trabajes con valores que ya están multiplicados por 100. Si tus datos almacenan porcentajes como números enteros donde 75 significa 75%, divide por 100 antes de formatear o usa el formateo de números simples con un signo de porcentaje añadido manualmente.

Entendiendo la precisión del formateador de porcentajes

La precisión predeterminada para el formateo de porcentajes es 0 decimales. Esto difiere del formateo de números simples, que adapta la precisión según el valor de entrada. El formateador de porcentajes redondea los valores a porcentajes enteros a menos que establezcas explícitamente opciones de dígitos fraccionarios.

const formatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

console.log(formatter.format(0.755));
// Output: "76%"

console.log(formatter.format(0.754));
// Output: "75%"

console.log(formatter.format(0.001));
// Output: "0%"

Los valores se redondean al porcentaje entero más cercano. El valor 0.755 se redondea a 76%. El valor 0.754 se redondea a 75%. Valores muy pequeños como 0.001 se redondean a 0%.

Si necesitas mostrar porcentajes pequeños o porcentajes fraccionarios, aumenta el máximo de dígitos fraccionarios. La siguiente lección cubre el control de precisión en detalle.