Числовой и текстовый стили

Экосистема FormatJS опирается на стандарты ECMA-402 (Intl API), расширяя их средствами форматирования сообщений и локализации. Числовые стили в этом контексте охватывают преобразование чисел в человекочитаемые строки с учётом локали, единиц измерения, валют и правил округления.

Базовое форматирование чисел

Основой служит Intl.NumberFormat, который используется напрямую или через обёртки FormatJS.

const formatter = new Intl.NumberFormat('ru-RU');
formatter.format(1234567.89);
// "1 234 567,89"

FormatJS в react-intl предоставляет компонент и API FormattedNumber, который инкапсулирует этот механизм:

import { FormattedNumber } from 'react-intl';

<FormattedNumber value={1234567.89} />

Поведение зависит от текущей локали провайдера IntlProvider.

Числовые стили: decimal, currency, percent

Intl.NumberFormat поддерживает несколько ключевых режимов форматирования через параметр style.

Decimal-формат

Используется по умолчанию, предназначен для отображения обычных чисел.

new Intl.NumberFormat('ru-RU', {
  style: 'decimal'
}).format(10000.5);
// "10 000,5"

В FormatJS:

<FormattedNumber value={10000.5} style="decimal" />

Валютный формат

Форматирование с учётом валюты включает код валюты ISO 4217 и локальные правила отображения.

new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'RUB'
}).format(1999.99);
// "1 999,99 ₽"

Особенности валютного форматирования:

  • автоматическое добавление символа валюты;
  • различие в позиционировании символа (до или после числа);
  • правила округления зависят от валюты.

В FormatJS:

<FormattedNumber value={1999.99} style="currency" currency="RUB" />

Для динамических валют используется передача параметров:

const currency = 'USD';

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency
}).format(120);
// "$120.00"

Процентный формат

Процентное представление предполагает умножение значения на 100 и добавление символа % с учётом локали.

new Intl.NumberFormat('ru-RU', {
  style: 'percent'
}).format(0.25);
// "25 %"

В FormatJS:

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

Важно учитывать, что входное значение всегда нормализовано (0–1), а не 0–100.


Разделители, точность и округление

Форматирование чисел включает управление точностью через параметры:

  • minimumFractionDigits
  • maximumFractionDigits
  • minimumIntegerDigits
  • roundingMode (в новых реализациях)
new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 4
}).format(12.3);
// "12.30"

FormatJS передаёт эти параметры через пропсы:

<FormattedNumber
  value={12.3}
  minimumFractionDigits={2}
  maximumFractionDigits={4}
/>

Особое значение имеет округление:

new Intl.NumberFormat('en-US', {
  maximumFractionDigits: 0
}).format(12.9);
// "13"

Форматирование единиц измерения

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

new Intl.NumberFormat('ru-RU', {
  style: 'unit',
  unit: 'kilometer-per-hour'
}).format(120);
// "120 км/ч"

Поддерживаемые единицы стандартизированы: скорость, масса, длина, энергия и др.

В FormatJS:

<FormattedNumber value={120} style="unit" unit="kilometer-per-hour" />

Особенности:

  • локализованные обозначения единиц;
  • автоматическое форматирование пробелов;
  • различие систем (метрическая/имперская).

Текстовые стили в FormatJS

Текстовые стили в FormatJS связаны с ICU Message Format, который используется в react-intl через FormattedMessage и API formatMessage. Основные механизмы включают интерполяцию, множественные формы, выбор вариантов и вложенные структуры.

Базовая интерполяция сообщений

ICU-сообщения позволяют внедрять переменные в текст:

const messages = {
  greeting: 'Привет, {name}'
};

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

<FormattedMessage id="greeting" values={{ name: 'Алексей' }} />

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


Плюрализация (plural rules)

Одной из ключевых возможностей является автоматическая обработка множественных форм.

const messages = {
  items: 'В корзине {count, plural, one {# товар} few {# товара} many {# товаров} other {# товара}}'
};

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

<FormattedMessage id="items" values={{ count: 5 }} />

Особенности:

  • правила зависят от локали (ru, en, ar и др.);
  • используется Intl.PluralRules под капотом;
  • поддерживаются категории: one, few, many, other.

Пример различий:

// ru-RU
1 товар
2 товара
5 товаров
// en-US
1 item
2 items
5 items

Порядковые числительные (selectordinal)

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

const messages = {
  rank: '{place, selectordinal, one {#-й} two {#-й} few {#-й} other {#-й}}'
};
<FormattedMessage id="rank" values={{ place: 2 }} />

Под капотом применяется Intl.PluralRules с типом ordinal.


Условный выбор (select)

Механизм select позволяет строить условные конструкции на основе строковых значений.

const messages = {
  status: '{gender, select, male {Он пришёл} female {Она пришла} other {Они пришли}}'
};

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

<FormattedMessage id="status" values={{ gender: 'female' }} />

Поддерживается любое количество вариантов, включая other как обязательный fallback.


Вложенные сообщения

ICU-синтаксис допускает композицию конструкций:

const messages = {
  inbox:
    'У {name} {count, plural, one {# новое сообщение} few {# новых сообщения} many {# новых сообщений} other {# сообщения}}'
};
<FormattedMessage
  id="inbox"
  values={{ name: 'Мария', count: 3 }}
/>

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


Форматирование чисел внутри сообщений

Числа внутри ICU-сообщений автоматически форматируются через Intl, если используется FormattedNumber или передаются через values.

const messages = {
  balance: 'Баланс: {amount, number, currency}'
};
<FormattedMessage
  id="balance"
  values={{ amount: 1234.56 }}
/>

При необходимости можно управлять стилем:

const messages = {
  balance: 'Баланс: {amount, number, ::currency/RUB}'
};

Форматирование дат и времени как текстовый стиль

Хотя это выходит за рамки числовых стилей, в ICU оно относится к текстовым форматам:

const messages = {
  date: 'Дата: {ts, date, long}'
};
<FormattedMessage
  id="date"
  values={{ ts: Date.now() }}
/>

Поддерживаются стили:

  • short
  • medium
  • long
  • full

Rich text форматирование

FormatJS поддерживает разметку внутри сообщений через функцию values, где значения могут быть функциями, возвращающими JSX.

const messages = {
  warning: 'Согласие с {terms} обязательно'
};
<FormattedMessage
  id="warning"
  values={{
    terms: (chunks) => <a href="/terms">{chunks}</a>
  }}
/>

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


Экранирование и безопасность текста

ICU-синтаксис требует экранирования фигурных скобок:

const messages = {
  example: "Символы '{' и '}' используются для параметров"
};

Также поддерживается защита от некорректной интерполяции за счёт строгого парсинга сообщений.


Комбинация числовых и текстовых стилей

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

const messages = {
  report:
    '{name} получил {score, number, ::percent} успешности и {tasks, plural, one {# задачу} few {# задачи} many {# задач} other {# задачи}}'
};
<FormattedMessage
  id="report"
  values={{
    name: 'Иван',
    score: 0.87,
    tasks: 5
  }}
/>

Такая комбинация демонстрирует ключевую идею FormatJS: единая декларативная система для локализованного представления данных разных типов.