Процентные значения

В экосистеме FormatJS работа с процентными значениями основана на стандарте Intl.NumberFormat, который предоставляет нативные возможности локализованного форматирования чисел в JavaScript. Основной механизм заключается в использовании опции style: 'percent', при которой входное число интерпретируется как доля единицы и автоматически масштабируется.

Ключевая особенность: значение 1 соответствует 100%, а не 1%.

Это фундаментальное правило влияет на всю логику обработки данных:

  • 0.15 → 15%
  • 1 → 100%
  • 1.2 → 120%
  • 0.003 → 0.3%
const formatter = new Intl.NumberFormat('ru-RU', {
  style: 'percent'
});

formatter.format(0.15); // "15 %"

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


Использование в FormatJS (React Intl)

В библиотеке FormatJS чаще всего процентные значения используются через компонент FormattedNumber.

import { FormattedNumber } from 'react-intl';

<FormattedNumber
  value={0.42}
  style="percent"
/>

Результат будет локализован автоматически:

  • en-US42%
  • fr-FR42 %
  • de-DE42 %

При этом не требуется ручное управление символами или форматированием — вся логика делегируется ICU и Intl API.


Масштабирование и типичные ошибки

Наиболее частая ошибка при работе с процентами — двойное масштабирование значения.

Если данные уже представлены в процентах (например, 15 означает 15%), а затем применяется style: 'percent', результат будет некорректным:

// Ошибка интерпретации
formatter.format(15); // 1500%

Правильный подход зависит от источника данных:

  • Доля (0–1): использовать напрямую
  • Проценты (0–100): делить на 100
const normalizePercent = (value) => value / 100;

formatter.format(normalizePercent(15)); // 15%

Округление и точность

FormatJS опирается на настройки minimumFractionDigits и maximumFractionDigits, которые позволяют контролировать отображение дробных процентов.

const formatter = new Intl.NumberFormat('ru-RU', {
  style: 'percent',
  maximumFractionDigits: 1
});

formatter.format(0.1234); // "12,3 %"

Особенности округления:

  • используется банковское округление (half-even в некоторых реализациях)
  • поведение зависит от движка JavaScript
  • локаль влияет только на формат, но не на математическую логику

ICU MessageFormat и проценты

В FormatJS процентные значения могут быть встроены в ICU-сообщения, что особенно важно для интернационализации интерфейсов.

const messages = {
  completion: 'Прогресс: {value, number, percent}'
};

Использование:

<FormattedMessage
  id="completion"
  values={{ value: 0.78 }}
/>

Здесь 0.78 автоматически преобразуется в 78% согласно локали пользователя.


Локализация и особенности отображения

Разные локали не только меняют формат, но и влияют на визуальное представление:

  • разделитель дробной части (, или .)
  • наличие неразрывного пробела перед %
  • позиция символа процента
  • ширина и тип пробелов

Примеры:

  • en-US: 75%
  • fr-FR: 75 %
  • ar-EG: ٧٥٪

FormatJS не требует ручной обработки этих различий, так как использует данные CLDR.


Работа с динамическими значениями

При частом обновлении значений (например, прогресс-бар, загрузка, аналитика) важно учитывать производительность форматирования.

Рекомендации:

  • избегать создания нового Intl.NumberFormat при каждом рендере
  • использовать мемоизацию
  • применять форматтеры на уровне модуля или контекста
const percentFormatter = new Intl.NumberFormat('ru-RU', {
  style: 'percent'
});

Использование в TypeScript

FormatJS хорошо интегрируется с TypeScript, особенно при использовании react-intl.

import { FormattedNumber } from 'react-intl';

type Props = {
  value: number;
};

function Progress({ value }: Props) {
  return <FormattedNumber value={value} style="percent" />;
}

Типизация помогает избежать передачи строк вместо чисел, что критично для корректного форматирования.


Частичные значения и бизнес-логика

Процентные значения часто используются в:

  • аналитических панелях
  • финансовых отчётах
  • системах мониторинга
  • UI прогресса операций

Важно разделять:

  • сырые данные (например, 0.0345)
  • отображаемые значения (3.45%)
  • округлённые представления (3.5%)

FormatJS отвечает только за последний слой — визуализацию, не вмешиваясь в бизнес-логику.


Особенности работы с отрицательными процентами

Отрицательные значения также корректно форматируются:

formatter.format(-0.12); // "-12 %"

Такие значения часто используются для:

  • финансовых потерь
  • изменения показателей
  • отклонений от нормы

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


Влияние настроек пользователя

FormatJS учитывает пользовательскую локаль, которая может динамически меняться. При изменении locale формат процентов пересоздаётся автоматически в контексте IntlProvider.

<IntlProvider locale="ru">
  <App />
</IntlProvider>

Смена локали приводит к:

  • изменению символов
  • перераспределению пробелов
  • корректировке округления отображения

Связь с другими числовыми стилями

Процентный стиль тесно связан с другими режимами форматирования:

  • decimal — обычные числа
  • currency — денежные значения
  • unit — физические единицы
new Intl.NumberFormat('ru-RU', {
  style: 'decimal'
});

new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'RUB'
});

new Intl.NumberFormat('ru-RU', {
  style: 'percent'
});

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