Avatar ·

Форматирование чисел в строку с валютой на JavaScript: лучшие способы

Всем привет! Я новичок в JavaScript и столкнулся с такой проблемой: нужно выводить цены товаров на сайте в виде строки с валютой, например "1 234.56 ₽" или "$1,234.56". Пробовал делать вручную через Math.round и конкатенацию, но код получается ужасным, особенно с учётом разных валют и локалей. Вот пример того, что я пытался сделать: ```javascript function formatPrice(price) { const rubles = Math.floor(price); const kopecks = Math.round((price - rubles) * 100); return rubles + ' ₽ ' + (kopecks < 10 ? '0' : '') + kopecks; } ``` Но это работает только для рублей, да и с большими числами (например, 1000000) получается "1000000 ₽ 00", а нужно "1 000 000.00 ₽". Плюс если валюта — доллары, то нужно ставить знак доллара перед числом и использовать точку для тысяч и запятую для центов. Я знаю, что в JavaScript есть какие-то встроенные методы, но не могу разобраться. Пробовал toFixed(2), но он не ставит пробелы между разрядами. Подскажите, как правильно и красиво форматировать числа как строки с валютой, чтобы поддерживались разные локали? Желательно без сторонних библиотек, только нативный JS. Спасибо! <<>>

Использование Intl.NumberFormat — нативный и гибкий способ

Лучший способ форматирования чисел как строк с валютой в современном JavaScript — использовать объект Intl.NumberFormat. Этот встроенный API поддерживает все основные валюты мира, учитывает правила форматирования для разных локалей (пробелы, разделители разрядов, положение знака валюты) и не требует сторонних библиотек.

Базовое использование

Для создания форматтера вызовите конструктор с локалью (например, 'ru-RU' для России или 'en-US' для США) и опциями:

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

console.log(formatter.format(1234.56)); // "1 234,56 ₽"
console.log(formatter.format(1000000)); // "1 000 000,00 ₽"

Как это работает

Параметры в опциях:

  • style: 'currency' — включает режим форматирования валюты.
  • currency: 'RUB' — код валюты по стандарту ISO 4217 (например, 'USD', 'EUR', 'GBP').
  • По умолчанию количество десятичных знаков определяется валютой (для RUB — 2, для JPY — 0).

Пример с долларами США

const usFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

console.log(usFormatter.format(1234.56)); // "$1,234.56"
console.log(usFormatter.format(0.99));    // "$0.99"

Настройка количества знаков после запятой

Вы можете явно указать минимальное и максимальное количество десятичных знаков:

const formatter = new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'RUB',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

console.log(formatter.format(100)); // "100,00 ₽" (а не "100,00 ₽")

Работа с большими числами и округление

Intl.NumberFormat автоматически округляет числа по математическим правилам:

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

console.log(formatter.format(1234.5678)); // "1 234,57 ₽" (округление до 2 знаков)

Обработка ошибок: если передана неверная валюта

Если код валюты некорректен, Intl.NumberFormat выдаст ошибку RangeError. Рекомендуется оборачивать вызов в try-catch или проверять код валюты:

function safeFormatCurrency(amount, currencyCode) {
  try {
    const formatter = new Intl.NumberFormat('ru-RU', {
      style: 'currency',
      currency: currencyCode.toUpperCase()
    });
    return formatter.format(amount);
  } catch (e) {
    console.error('Неверный код валюты:', currencyCode);
    return amount.toString(); // fallback
  }
}

console.log(safeFormatCurrency(100, 'usd')); // "100,00 $"
console.log(safeFormatCurrency(100, 'XYZ')); // "100" + ошибка в консоли

Полный пример для интернет-магазина

const prices = [
  { amount: 1299.99, currency: 'USD' },
  { amount: 4500, currency: 'RUB' },
  { amount: 89.95, currency: 'EUR' }
];

function formatPrice(amount, currency, locale = 'ru-RU') {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency
  }).format(amount);
}

prices.forEach(item => {
  console.log(formatPrice(item.amount, item.currency));
  // Вывод:
  // "1 299,99 $" (для USD в русской локали)
  // "4 500,00 ₽"
  // "89,95 €"
});

Важные замечания

  • Локаль влияет на формат: в 'en-US' разделитель тысяч — запятая, десятичный — точка; в 'ru-RU' — наоборот.
  • Положение знака валюты также зависит от локали: для 'en-US' знак перед числом ($10), для 'ru-RU' — после (10 ₽).
  • Этот метод работает во всех современных браузерах и Node.js (начиная с версии 0.12).
<<>>

Альтернативный подход: ручное форматирование с toLocaleString

Если вам нужно быстрое решение без создания объекта Intl.NumberFormat, можно использовать метод Number.prototype.toLocaleString(). Он вызывает тот же механизм, но в более компактной форме:

const price = 1234567.89;
console.log(price.toLocaleString('ru-RU', {
  style: 'currency',
  currency: 'RUB'
})); // "1 234 567,89 ₽"

Когда это полезно

  • Для разового форматирования без создания дополнительной переменной.
  • В простых скриптах, где не нужно переиспользовать форматтер.

Недостатки по сравнению с Intl.NumberFormat

  • При многократном вызове внутри цикла создаётся новый объект форматтера каждый раз, что может быть медленнее (хотя для большинства задач разница незаметна).
  • Меньше контроля над опциями (например, нельзя задать minimumIntegerDigits).

Пример с разными валютами

function formatCurrency(amount, currency, locale = 'ru-RU') {
  return amount.toLocaleString(locale, {
    style: 'currency',
    currency: currency
  });
}

console.log(formatCurrency(99.99, 'USD')); // "99,99 $"
console.log(formatCurrency(99.99, 'EUR', 'de-DE')); // "99,99 €" (немецкая локаль)

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