FormatJS предоставляет набор инструментов для форматирования чисел, валют и сообщений, основанных на ICU MessageFormat, что делает работу с ценами и скидками в интерфейсах предсказуемой и локализуемой. В контексте коммерческих интерфейсов ключевую роль играют два направления: корректное отображение валюты и выразительное представление скидок с учётом локали пользователя.
Основой работы с ценами в FormatJS является использование числового
форматирования через Intl.NumberFormat, которое
оборачивается удобными абстракциями из react-intl.
Форматирование валюты строится на сочетании локали и кода валюты:
const formatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
});
formatter.format(1999.99);
Результат зависит от локали:
1 999,99 ₽ для ru-RU$1,999.99 для en-USВ экосистеме FormatJS аналогичный результат достигается через
FormattedNumber:
import { FormattedNumber } from 'react-intl';
<FormattedNumber
value={1999.99}
style="currency"
currency="RUB"
/>
Такой подход позволяет декларативно встроить форматирование в UI, сохраняя единый стандарт отображения цен.
В коммерческих интерфейсах цена почти всегда является динамическим значением, передаваемым через сообщения или props. FormatJS поддерживает интерполяцию значений в ICU-сообщениях:
const message = "Цена товара: {price, number, currency}";
Передача значения:
<FormattedMessage
id="product.price"
defaultMessage="Цена товара: {price, number, currency}"
values={{
price: 1999.99
}}
/>
Если требуется строгий контроль валюты, используется расширенный
формат через formatNumber:
const intl = useIntl();
intl.formatNumber(1999.99, {
style: 'currency',
currency: 'RUB'
});
Такой подход позволяет централизовать логику форматирования и избежать дублирования правил в компонентах.
Скидка в интерфейсе обычно состоит из трёх элементов:
FormatJS не выполняет бизнес-логику расчётов, но обеспечивает корректное отображение результатов.
const originalPrice = 2500;
const finalPrice = 2000;
const discount = (1 - finalPrice / originalPrice);
Форматирование процента:
<FormattedNumber
value={discount}
style="percent"
/>
Результат:
20% для большинства локалей при значении
0.2Важно учитывать, что style="percent" ожидает дробное
значение, а не уже умноженное на 100 число.
В интерфейсах электронной коммерции распространён паттерн отображения старой цены с зачёркиванием. FormatJS отвечает за форматирование, а стилизация выполняется отдельно:
<span style={{ textDecoration: 'line-through' }}>
<FormattedNumber value={2500} style="currency" currency="RUB" />
</span>
Рядом отображается итоговая цена:
<span>
<FormattedNumber value={2000} style="currency" currency="RUB" />
</span>
Такой подход разделяет визуальную семантику и международное форматирование.
ICU MessageFormat позволяет выразить сложные ценовые конструкции внутри строки:
const message = `
Вы экономите {amount, number, currency} ({percent, number, percent})
`;
Использование:
<FormattedMessage
id="discount.info"
defaultMessage="Вы экономите {amount, number, currency} ({percent, number, percent})"
values={{
amount: 500,
percent: 0.2
}}
/>
Такой формат удобен для локализации, поскольку переводчики работают со структурой сообщения, а не с кодом.
Цены требуют строгого контроля округления.
Intl.NumberFormat внутри FormatJS поддерживает настройки
точности:
intl.formatNumber(1999.999, {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
Поведение:
1999.999 → 2 000,00 ₽1999.9 → 1 999,90 ₽Контроль дробной части критичен при работе с налогами и скидками, где погрешности недопустимы на уровне интерфейса.
Для карточек товаров и списков часто используется компактное представление:
intl.formatNumber(1200000, {
notation: 'compact',
compactDisplay: 'short'
});
Результаты:
1,2 млн (ru-RU)1.2M (en-US)В FormatJS это позволяет поддерживать единый интерфейс при разных локалях без ручной адаптации строк.
В каталогах часто используется диапазон:
ICU-сообщения позволяют выразить это явно:
const message = "от {min, number, currency}";
или диапазон:
const message = "{min, number, currency} – {max, number, currency}";
Использование:
<FormattedMessage
id="price.range"
defaultMessage="{min, number, currency} – {max, number, currency}"
values={{
min: 1000,
max: 3000
}}
/>
Сценарий с одновременным отображением двух типов скидки требует комбинированного форматирования:
const original = 4000;
const final = 3000;
const saved = original - final;
const percent = saved / original;
ICU-сообщение:
<FormattedMessage
id="discount.summary"
defaultMessage="Скидка {percent, number, percent} — экономия {saved, number, currency}"
values={{
percent,
saved
}}
/>
Такой формат обеспечивает универсальность отображения для разных рынков, где предпочтения к форме скидки различаются.
FormatJS опирается на ISO 4217 коды валют:
intl.formatNumber(1999, {
style: 'currency',
currency: 'KZT'
});
Результат адаптируется под локаль:
1 999 ₸KZT 1,999 (в некоторых англоязычных локалях)FormattedNumber поддерживает функциональный рендеринг,
что полезно для кастомной стилизации:
<FormattedNumber
value={1999}
style="currency"
currency="RUB"
>
{(formattedValue) => (
<span className="price">{formattedValue}</span>
)}
</FormattedNumber>
Это позволяет отделить форматирование от визуального слоя и внедрять сложные UI-паттерны: бейджи, анимации изменения цены, сравнение версий.
Хотя FormatJS не занимается расчетами, он часто используется для отображения уже рассчитанных значений:
const priceWithTax = basePrice * 1.2;
Далее форматирование:
<FormattedNumber
value={priceWithTax}
style="currency"
currency="USD"
/>
В интерфейсах с налогами важно не смешивать вычисления и локализацию, чтобы избежать различий между регионами.
В некоторых интерфейсах требуется показать цену в разных валютах одновременно:
<FormattedMessage
id="price.multi"
defaultMessage="≈ {usd, number, currency} или {eur, number, currency}"
values={{
usd: 25,
eur: 23
}}
/>
Такой подход применяется для предварительной оценки стоимости в международных сервисах.
Форматирование процентов и валют в FormatJS строго зависит от локали:
Пример:
20%20 %Это поведение нельзя фиксировать вручную без потери универсальности интерфейса.
Хук useIntl применяется для динамического форматирования
в логике компонентов:
const intl = useIntl();
const formattedPrice = intl.formatNumber(price, {
style: 'currency',
currency: 'EUR'
});
Далее значение используется в сложных сценариях:
В мультивалютных интерфейсах форматирование зависит от выбранного региона:
intl.formatNumber(price, {
style: 'currency',
currency: selectedCurrency
});
Такой механизм позволяет одному и тому же компоненту работать в разных странах без изменения кода отображения.
В архитектуре приложений, использующих FormatJS, ценовые данные обычно разделяются на три слоя:
Intl и ICUТакое разделение обеспечивает предсказуемость отображения и корректность локализации в любых сценариях коммерческого интерфейса.