Замена ручного форматирования

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

Типичный пример ручного форматирования чисел:

function formatPrice(value) {
  return value.toFixed(2) + " $";
}

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

Для даты аналогичная проблема:

function formatDate(date) {
  return date.getDate() + "." + (date.getMonth() + 1) + "." + date.getFullYear();
}

Такое представление не учитывает различия между форматами DD.MM.YYYY, MM/DD/YYYY, порядком компонентов и локальными стандартами отображения времени.

Intl.NumberFormat: универсальная замена форматированию чисел

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

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

Ключевые возможности:

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

Форматирование валют

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

priceFormatter.format(99.99); // "$99.99"

В других локалях результат изменяется без изменения кода:

new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR"
}).format(99.99); // "99,99 €"

Проценты

new Intl.NumberFormat("en-US", {
  style: "percent"
}).format(0.42); // "42%"

Управление точностью

new Intl.NumberFormat("en-US", {
  minimumFractionDigits: 0,
  maximumFractionDigits: 2
}).format(12.3456); // "12.35"

Intl.DateTimeFormat: отказ от ручной сборки дат

Intl.DateTimeFormat устраняет необходимость вручную собирать строки дат и времени.

const dt = new Intl.DateTimeFormat("ru-RU");
dt.format(new Date());

Управление детализацией

new Intl.DateTimeFormat("en-GB", {
  year: "numeric",
  month: "long",
  day: "2-digit"
}).format(new Date());

Результат зависит от локали и настроек, но структура остаётся консистентной.

Формат времени

new Intl.DateTimeFormat("en-US", {
  hour: "2-digit",
  minute: "2-digit"
}).format(new Date());

Таймзоны

new Intl.DateTimeFormat("en-US", {
  timeZone: "UTC",
  year: "numeric",
  month: "2-digit",
  day: "2-digit"
}).format(new Date());

Ручное преобразование через getTimezoneOffset заменяется декларативной настройкой.

Intl.RelativeTimeFormat: устранение ручных вычислений “назад/вперёд”

Отображение относительного времени (например, “3 дня назад”) часто реализуется вручную через разницу дат.

function formatAgo(days) {
  return days + " days ago";
}

Intl.RelativeTimeFormat заменяет такие конструкции:

const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });

rtf.format(-3, "day"); // "3 days ago"
rtf.format(2, "hour");  // "in 2 hours"

Поддерживаемые единицы:

  • second
  • minute
  • hour
  • day
  • week
  • month
  • year

Intl.ListFormat: замена ручной сборки списков

Склейка строк через join не учитывает локальные союзы и правила перечислений.

["яблоко", "груша", "слива"].join(", ");

Intl.ListFormat корректно оформляет списки:

new Intl.ListFormat("ru", { style: "long", type: "conjunction" })
  .format(["яблоко", "груша", "слива"]);

Возможные режимы:

  • conjunction — и/and списки
  • disjunction — или/or списки
  • unit — единицы измерения

Intl.PluralRules: устранение ручных условий склонения

Ручная реализация множественных форм числительных часто выглядит громоздко:

function getLabel(n) {
  if (n === 1) return "item";
  return "items";
}

Intl.PluralRules формализует правила:

const pr = new Intl.PluralRules("ru");

pr.select(1); // "one"
pr.select(2); // "few"
pr.select(5); // "many"

Далее правила связываются с локализованными строками.

Каскадная локализация и единая точка форматирования

Использование Intl позволяет централизовать форматирование через фабрики:

const formatters = {
  number: new Intl.NumberFormat("ru-RU"),
  currency: new Intl.NumberFormat("ru-RU", {
    style: "currency",
    currency: "RUB"
  }),
  date: new Intl.DateTimeFormat("ru-RU"),
};

Такой подход исключает дублирование логики форматирования в бизнес-коде.

Локализация без условных конструкций

Ручные конструкции типа:

if (locale === "ru") { ... }
else if (locale === "en") { ... }

заменяются передачей локали в Intl:

new Intl.NumberFormat(locale).format(value);
new Intl.DateTimeFormat(locale).format(date);

Логика выбора представления переносится в движок ECMAScript.

Нормализация отображения данных

Форматирование перестаёт зависеть от:

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

Пример комплексного вывода без ручной логики:

const locale = "de-DE";

const price = new Intl.NumberFormat(locale, {
  style: "currency",
  currency: "EUR"
}).format(12345.67);

const date = new Intl.DateTimeFormat(locale, {
  dateStyle: "medium"
}).format(new Date());

Единообразие в разных окружениях

Intl работает в браузерах и Node.js с единым API, что устраняет необходимость платформенных ветвлений.

globalThis.Intl.NumberFormat
globalThis.Intl.DateTimeFormat

Это делает форматирование независимым от UI-слоя, серверной среды или сборщика.

Минимизация пользовательских утилит форматирования

Самописные библиотеки форматирования часто дублируют функциональность стандартного API:

  • обработка локалей
  • падежи и множественные формы
  • валютные символы
  • формат дат и времени

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