Разделители тысяч и десятичные разделители

Форматирование чисел в JavaScript через международный API Intl основано на локализации представления числовых значений. Одним из ключевых аспектов локализации является отображение разделителей: тысячного (grouping separator) и десятичного (decimal separator). Эти символы зависят от языкового и регионального контекста и не являются универсальными.


Локализационная природа числовых разделителей

Разные культуры используют различные соглашения для визуального разделения чисел:

  • в англоязычной локали (en-US):

    • тысячный разделитель: запятая ,
    • десятичный разделитель: точка .
  • в большинстве европейских локалей (de-DE, fr-FR):

    • тысячный разделитель: пробел или точка
    • десятичный разделитель: запятая ,
  • в русской локали (ru-RU):

    • тысячный разделитель: пробел
    • десятичный разделитель: запятая ,

Таким образом, одно и то же число может выглядеть по-разному:

  • 1234567.89

    • 1,234,567.89 (en-US)
    • 1 234 567,89 (ru-RU)
    • 1.234.567,89 (de-DE)

Базовое форматирование через Intl.NumberFormat

Основной инструмент форматирования чисел — конструктор Intl.NumberFormat.

const formatter = new Intl.NumberFormat('en-US');

formatter.format(1234567.89);
// "1,234,567.89"

При смене локали меняются и символы разделителей:

const formatterRU = new Intl.NumberFormat('ru-RU');

formatterRU.format(1234567.89);
// "1 234 567,89"

Разделитель тысяч: группировка разрядов

Группировка разрядов регулируется опцией useGrouping.

const formatter = new Intl.NumberFormat('en-US', {
  useGrouping: true
});

formatter.format(1000000);
// "1,000,000"

Отключение группировки:

const formatter = new Intl.NumberFormat('en-US', {
  useGrouping: false
});

formatter.format(1000000);
// "1000000"

Типы группировки зависят от локали:

  • стандартная западная: группы по 3 цифры
  • индийская система: 12,34,56,789 (en-IN)
const formatter = new Intl.NumberFormat('en-IN');

formatter.format(123456789);
// "12,34,56,789"

Десятичный разделитель и управление дробной частью

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

Минимальное и максимальное количество дробных цифр

const formatter = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});

formatter.format(12.5);
// "12.50"

Если дробная часть отсутствует:

formatter.format(12);
// "12.00"

Влияние округления на отображение разделителей

Изменение числа знаков после запятой может приводить к округлению:

const formatter = new Intl.NumberFormat('en-US', {
  maximumFractionDigits: 2
});

formatter.format(1.239);
// "1.24"

Локаль определяет только символ разделителя, но не правило округления.


Разбор числа на части через formatToParts

Для точного понимания структуры числа используется метод formatToParts, который позволяет увидеть отдельные компоненты: цифры и разделители.

const formatter = new Intl.NumberFormat('en-US');

formatter.formatToParts(1234.56);

Результат:

[
  { type: 'integer', value: '1' },
  { type: 'group', value: ',' },
  { type: 'integer', value: '234' },
  { type: 'decimal', value: '.' },
  { type: 'fraction', value: '56' }
]

Здесь явно выделяются:

  • group — тысячный разделитель
  • decimal — десятичный разделитель
  • integer — целая часть
  • fraction — дробная часть

Локаль и выбор символов разделителей

Выбор символов определяется таблицами Unicode CLDR, которые используются движком JavaScript для интернационализации.

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

new Intl.NumberFormat('en-US').format(1000);
// "1,000"

new Intl.NumberFormat('de-DE').format(1000);
// "1.000"

new Intl.NumberFormat('ru-RU').format(1000);
// "1 000"

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

В ряде локалей используется не обычный пробел, а неразрывный пробел или узкий неразрывный пробел.

const formatter = new Intl.NumberFormat('fr-FR');

formatter.format(1000000);

Результат визуально выглядит как:

1 000 000

Символ между группами — узкий неразрывный пробел (U+202F), что предотвращает разрыв числа при переносе строки.


Связь с числовыми стилями

Разделители используются не только в стандартном числовом формате (style: "decimal"), но и в других стилях:

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

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(1234.56);
// "$1,234.56"

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

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

Во всех случаях система разделителей остаётся локализованной.


Управление отображением через numberingSystem

Помимо разделителей, форматирование зависит от системы счисления:

new Intl.NumberFormat('en-US', {
  numberingSystem: 'latn'
}).format(1234567);

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


Практическое различие между локалями

Сравнение одной и той же величины:

Локаль Результат
en-US 1,234,567.89
de-DE 1.234.567,89
ru-RU 1 234 567,89
fr-FR 1 234 567,89

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


Внутреннее представление и независимость от строк

Intl.NumberFormat не работает с уже отформатированными строками, а оперирует числовыми значениями. Это гарантирует корректное применение разделителей независимо от входного представления:

new Intl.NumberFormat('ru-RU').format(1000.5);
// "1 000,5"

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

new Intl.NumberFormat('ru-RU').format("1000.5");
// "1 000,5" (строка приводится к числу)

Поведение при больших числах

При увеличении разрядности сохраняется та же логика группировки:

new Intl.NumberFormat('en-US').format(9876543210123);
// "9,876,543,210,123"

Локаль продолжает определять только символы разделителей, а не алгоритм группировки.


Контекст использования разделителей в приложениях

Локализованные разделители используются в интерфейсах, где важна читаемость чисел:

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

Единообразие достигается за счёт применения Intl.NumberFormat вместо ручного форматирования через toLocaleString или строковые манипуляции.