12k
All articles

Форматирование дат и чисел с помощью Intl API

Статья формирует точное понимание Intl.DateTimeFormat и Intl.NumberFormat, охватывая режимы округления, типы Temporal и форматирование диапазонов в JavaScript.

OpenReplay Team
OpenReplay Team
Форматирование дат и чисел с помощью Intl API

Вы уже использовали Intl.DateTimeFormat и Intl.NumberFormat раньше. Но если ваше представление об этих API сформировалось несколько лет назад, вы, вероятно, упускаете значительные возможности — особенно в части управления округлением в Intl.NumberFormat и того, как Temporal и Intl работают вместе в рантаймах конца 2025 года.

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

Ключевые выводы

  • Intl API форматирует значения в строки с учётом локали, но не парсит, не вычисляет и не хранит данные — держите представление отдельно от бизнес-логики приложения.
  • Intl.NumberFormat теперь поддерживает расширенные возможности управления округлением, включая roundingMode, roundingIncrement и trailingZeroDisplay.
  • Типы Temporal обрабатывают собственное форматирование через toLocaleString(), в то время как Intl.DateTimeFormat продолжает форматировать объекты Date.
  • Всегда переиспользуйте экземпляры форматтеров для производительности и избегайте жёстко заданных строк ожидаемого вывода в тестах.

Что на самом деле делает Intl

Пространство имён Intl обрабатывает форматирование значений с учётом локали. Оно не парсит, не вычисляет и не манипулирует данными — оно преобразует значения в читаемые человеком строки в соответствии с культурными конвенциями.

Два критически важных момента:

  1. Intl форматирует; он не хранит и не вычисляет. Бизнес-логика вашего приложения остаётся отдельной от представления.
  2. Вывод варьируется в зависимости от рантайма. Базовые данные локалей поступают из библиотек ICU, встроенных в браузеры и Node.js. Точные строки могут немного отличаться между Chrome и Safari или между версиями Node.

Никогда не задавайте жёстко ожидаемые строки вывода в тестах. Вместо этого используйте formatToParts() или структурные утверждения.

Intl.DateTimeFormat: за пределами базовых опций

Форматирование объектов Date

Intl.DateTimeFormat остаётся стандартным способом форматирования JavaScript-объектов Date:

const formatter = new Intl.DateTimeFormat('de-DE', {
  dateStyle: 'long',
  timeStyle: 'short',
  timeZone: 'Europe/Berlin'
});

formatter.format(new Date()); // "27. Juni 2025 um 14:30"

Опции dateStyle и timeStyle предоставляют предустановленные конфигурации. Когда вам нужен детальный контроль, используйте индивидуальные опции, такие как weekday, month, hour и timeZoneName.

Типы Temporal и форматирование с учётом локали

Temporal активно внедряется в современные движки JavaScript, но пока ещё не имеет широкой поддержки во всех браузерах и окружениях. Там, где он доступен, Temporal.PlainDate, Temporal.ZonedDateTime и другие типы Temporal форматируют сами себя через toLocaleString(), а не передаются напрямую в Intl.DateTimeFormat.prototype.format().

const date = Temporal.PlainDate.from('2025-06-27');
date.toLocaleString('ja-JP', { dateStyle: 'full' });
// "2025年6月27日金曜日"

Intl.DateTimeFormat принимает объекты Date. Типы Temporal обрабатывают собственную логику форматирования и могут внутренне делегировать поведение, чувствительное к локали, но они не форматируются самим Intl.DateTimeFormat. Это различие важно при проектировании API, принимающих входные данные с датами.

Диапазоны дат с formatRange

Для отображения диапазонов дат используйте formatRange():

const start = new Date(2025, 5, 27);
const end = new Date(2025, 6, 3);

new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' })
  .formatRange(start, end);
// "Jun 27 – Jul 3, 2025"

Форматтер интеллектуально сворачивает избыточные части на основе конвенций локали.

Управление округлением и отображением в Intl.NumberFormat

Современные опции округления

Поведение округления в Intl.NumberFormat значительно расширилось в последних спецификациях, при этом поддержка варьируется в зависимости от рантайма. Помимо minimumFractionDigits и maximumFractionDigits, теперь у вас есть:

  • roundingMode: Управляет способом округления значений (ceil, floor, halfExpand, halfEven и т.д.)
  • roundingIncrement: Округляет до конкретных приращений (5, 10, 50 и т.д.)
  • trailingZeroDisplay: Управляет отображением конечных нулей (auto или stripIfInteger)
const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  roundingMode: 'halfEven',
  maximumFractionDigits: 2
});

formatter.format(2.225); // "$2.22" (банковское округление)
formatter.format(2.235); // "$2.24"

Паттерны форматирования чисел с учётом локали

Для компактной нотации и форматирования единиц измерения:

// Компактная нотация
new Intl.NumberFormat('en-US', {
  notation: 'compact',
  compactDisplay: 'short'
}).format(1234567); // "1.2M"

// Форматирование единиц измерения
new Intl.NumberFormat('de-DE', {
  style: 'unit',
  unit: 'kilometer-per-hour',
  unitDisplay: 'short'
}).format(120); // "120 km/h"

Практические соображения

Переиспользуйте экземпляры форматтеров

Создание форматтеров имеет накладные расходы. Кэшируйте их при форматировании множества значений:

// Делайте так
const priceFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});
prices.map(p => priceFormatter.format(p));

// Не так
prices.map(p => new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(p));

Определение поддержки возможностей

Проверяйте наличие новых опций программно:

try {
  new Intl.NumberFormat('en', { roundingMode: 'halfEven' });
  // Возможность поддерживается
} catch (e) {
  // Откат к округлению по умолчанию
}

Локаль vs. Часовой пояс

Это независимые концепции. Локаль (en-GB) определяет конвенции форматирования. Часовой пояс (Europe/London) определяет отображаемое время на часах. Вы можете форматировать дату в немецких конвенциях, показывая при этом токийское время.

Заключение

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

Типы Temporal существуют наряду с Intl — они обрабатывают собственные вызовы toLocaleString(), в то время как Intl.DateTimeFormat продолжает форматировать объекты Date. Постройте своё представление вокруг этого разделения, тестируйте без жёстко заданных строковых ожиданий и переиспользуйте экземпляры форматтеров для производительности.

Часто задаваемые вопросы

Могу ли я использовать Intl.DateTimeFormat для форматирования объектов Temporal напрямую?

Нет. Intl.DateTimeFormat.prototype.format() принимает только объекты Date. Типы Temporal, такие как PlainDate и ZonedDateTime, имеют собственные методы toLocaleString(). Они не форматируются самим Intl.DateTimeFormat, даже если могут использовать аналогичные данные локалей внутренне.

Почему мои отформатированные строки Intl выглядят по-разному в разных браузерах?

Intl полагается на данные локалей ICU, встроенные в каждый рантайм. Chrome, Safari, Firefox и Node.js могут поставляться с разными версиями ICU с небольшими вариациями в выводе. Избегайте жёсткого задания ожидаемых строк в тестах. Используйте formatToParts() или структурные утверждения для надёжной проверки поведения форматирования.

Что такое банковское округление и когда следует использовать halfEven?

Банковское округление (halfEven) округляет промежуточные значения к ближайшей чётной цифре, уменьшая накопительное смещение округления. Оно обычно используется в финансовых и бухгалтерских контекстах, но точные результаты всё ещё могут быть затронуты представлением чисел с плавающей точкой в двоичной системе.

Как проверить, поддерживает ли браузер новые опции Intl, такие как roundingMode?

Попытайтесь создать форматтер с включённой опцией. Если рантайм не поддерживает её, он может выбросить RangeError или молча проигнорировать опцию, в зависимости от движка. Всегда включайте резервную стратегию.

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.