Как форматировать проценты со знаком процента

Используйте стиль percent в Intl.NumberFormat, чтобы отображать числа в виде процентов с учетом локальных правил форматирования

Введение

Проценты встречаются во многих приложениях: от отображения прогресса выполнения до показа процентных ставок и размеров скидок. Простой способ — просто добавить знак процента к числу, — работает только для базовых случаев, но не учитывает, как разные языки и регионы форматируют проценты. Например, в турецком знак процента ставится перед числом — %50. Во французском между числом и знаком процента ставится пробел — 50 %. В арабском используется специальный символ процента вместо стандартного знака %.

API JavaScript Intl.NumberFormat автоматически учитывает эти различия в форматировании. Если указать опцию style как "percent", вы получите корректное отображение процентов для любой локали без необходимости писать отдельный код для каждой из них.

Почему форматирование процентов зависит от локали

Положение знака процента и правила расстановки пробелов различаются в зависимости от локали. В английском знак процента ставится сразу после числа без пробела. Во французском перед знаком процента добавляется пробел. В турецком знак процента ставится перед числом. Эти различия отражают естественные привычки чтения и языковые нормы.

Различия в форматировании касаются не только расположения знака. В некоторых языках используется другой символ процента. Например, в арабском применяется ٪ (U+066A) вместо стандартного ASCII-знака. Также в разных локалях могут отличаться разделители целой и дробной части, а также разделители тысяч, как и при обычном форматировании чисел.

Если вы вручную задаёте формат процентов через конкатенацию строк, все пользователи будут видеть формат только одной локали. Например, француз увидит 50% вместо привычного 50 %, а турок — 50% вместо ожидаемого %50. API Intl решает эту проблему, автоматически применяя корректные правила форматирования для каждой локали.

Форматирование чисел в проценты

Опция style в Intl.NumberFormat определяет, будет ли число отображаться как обычное число, валюта, процент или единица измерения. Установите style в "percent", чтобы форматировать числа как проценты.

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

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

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

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

Форматтер автоматически умножает входное значение на 100 и добавляет знак процента. То есть вы передаёте дробные значения. Например, 0.75 станет 75%, а 1.5 — 150%.

Такой подход к дробным значениям соответствует математике и большинству вычислений в программировании. Когда вы вычисляете процент, например, темп роста или степень выполнения, результат обычно — десятичное число от 0 до 1. Это значение можно сразу передавать в форматтер, не преобразуя его в проценты заранее.

Как понимать дробные значения

Форматтер процентов ожидает на входе дробное число, где 1.0 — это 100%. Такой подход соответствует тому, как проценты рассчитываются в коде.

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

// Calculating completion percentage
const completed = 75;
const total = 100;
const ratio = completed / total;

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

// Calculating growth rate
const oldValue = 1000;
const newValue = 1250;
const growth = (newValue - oldValue) / oldValue;

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

Если разделить выполненное на общее, получится 0.75. Передайте это значение в форматтер — он покажет 75%. Если вычислить рост как отношение изменения к исходному значению, получится 0.25. Форматтер покажет это как 25%.

Такой подход помогает избежать частой ошибки, когда разработчики умножают на 100 до форматирования, и получают 7500% вместо 75%. Форматтер сам умножает на 100, так что в коде вы работаете с обычными десятичными числами.

Как работать со значениями вне диапазона 0-1

Проценты не ограничиваются значениями от 0% до 100%. Значения меньше нуля — это отрицательные проценты. Значения больше 1.0 — это проценты больше 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%"

Отрицательные значения отображаются со знаком минус. Это удобно для показа уменьшения, убытков или отрицательных темпов роста. Значения больше 1,0 отображаются как проценты выше 100%, что часто используется для показа годового роста или сравнений, когда новое значение превышает базовое.

Очень маленькие значения могут округляться до 0% из-за стандартных настроек точности. Значение 0,0025, что соответствует 0,25%, отображается как 0%, потому что по умолчанию максимальное количество знаков после запятой для процентов — 0. В следующем уроке рассказывается, как управлять количеством знаков после запятой, чтобы точно показывать такие небольшие проценты.

Форматирование процентов в разных локалях

Форматирование процентов адаптируется к выбранной локали. Для каждой локали действуют свои правила размещения знака, пробелов и символов.

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"

В английском языке знак процента ставится сразу после числа. Во французском добавляется пробел перед знаком процента. В турецком знак процента ставится перед числом. В арабском используется арабский знак процента и направление текста справа налево.

Эти различия применяются автоматически на основе параметра локали. Не нужно писать отдельную логику для разных локалей. Просто создайте форматтер с локалью пользователя, и он выдаст правильный результат для этой локали.

Управление количеством знаков после запятой в процентах

По умолчанию при форматировании процентов не отображаются знаки после запятой. Например, значение 0,755 округляется до 76%. Вы можете управлять количеством знаков после запятой с помощью опций minimumFractionDigits и 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%"

Опция minimumFractionDigits гарантирует, что форматтер всегда покажет не меньше этого количества знаков после запятой, добавляя нули при необходимости. Опция maximumFractionDigits ограничивает количество знаков после запятой, округляя значение, если нужно.

Установка одинаковых значений обеспечивает единообразное форматирование всех процентов. Это удобно в таблицах и диаграммах, где важно, чтобы все проценты были выровнены визуально.

В следующем уроке мы подробнее рассмотрим управление количеством знаков после запятой, включая обработку конечных нулей и точное отображение очень малых процентов.

Комбинирование процентов с отображением знака

Форматирование процентов работает с опцией signDisplay, чтобы явно показывать плюсы для положительных процентов. Это полезно при отображении изменений или темпов роста, где знак указывает направление.

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%"

Опция signDisplay рассматривалась в предыдущем уроке. В сочетании с форматированием процентов она помогает понять, означает ли процент увеличение или уменьшение. Без явного плюса положительные проценты могут быть неочевидны в ситуациях, где показываются и приросты, и убытки.

Форматирование нуля как процента

По умолчанию ноль отображается как 0%. Как вы будете обрабатывать ноль, зависит от того, означает ли он отсутствие изменений, отсутствие значения или значимую величину.

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%"

Использование signDisplay: "exceptZero" показывает знаки для положительных и отрицательных процентов, но не ставит знак для нуля. Это делает ноль визуально отличимым, когда он означает нейтральное состояние, например, отсутствие изменений.

Когда использовать процентное форматирование

Используйте процентное форматирование для отображения соотношений, ставок или долей, которые естественно выражаются в процентах. Типичные случаи — прогресс выполнения, процентные ставки, суммы скидок, темпы роста, показатели успеха и значения вероятности.

Не используйте процентное форматирование, если вам нужно, чтобы знак процента всегда стоял в определённом месте вне зависимости от локали. Если по дизайну знак процента должен быть всегда справа для визуального единообразия, используйте конкатенацию строк. Однако это нарушает корректность интернационализации.

Также избегайте форматирования в процентах, если значения уже умножены на 100. Если ваши данные хранят проценты как целые числа, где 75 означает 75%, разделите на 100 перед форматированием или используйте обычное числовое форматирование и вручную добавьте знак процента.

Как работает точность форматирования процентов

По умолчанию при форматировании процентов используется 0 знаков после запятой. Это отличается от обычного числового форматирования, где точность подбирается в зависимости от значения. Форматирование процентов округляет значения до целых процентов, если вы явно не укажете количество знаков после запятой.

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%"

Значения округляются до ближайшего целого процента. Значение 0.755 округляется до 76%. Значение 0.754 округляется до 75%. Очень маленькие значения, например 0.001, округляются до 0%.

Если нужно показывать маленькие или дробные проценты, увеличьте максимальное количество знаков после запятой. В следующем уроке подробно рассказывается о настройке точности.