Обработка группировщиков разрядов

В библиотеке Globalize обработка группировщиков разрядов является частью подсистемы форматирования чисел и тесно связана с локализацией числовых представлений. Группировщик разрядов (grouping separator) определяет, каким символом и с какой периодичностью разделяются разряды числа при отображении, а также как эти символы интерпретируются при обратном разборе строки в число.

В разных локалях используются различные символы и правила группировки:

  • запятая как разделитель тысяч в en-US (1,234,567)
  • пробел или неразрывный пробел в ru-RU (1 234 567)
  • тонкий пробел в ряде европейских локалей (1 234 567)
  • точка в некоторых локалях как группировщик, при этом запятая используется как десятичный разделитель

Globalize опирается на данные Unicode CLDR, где для каждой локали определены:

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

Модель группировки в CLDR и Globalize

Группировка разрядов описывается через паттерн числа, в котором задаётся регулярность вставки разделителя. Например:

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

Некоторые локали используют асимметричную модель:

  • первая группа меньше или больше последующих

  • пример: индийская система 12,34,56,789, где:

    • первая группа = 3
    • последующие = 2

Globalize интерпретирует эти данные через CLDR-модель, где группировка задаётся массивом:

"group": [3, 2]

или через строковое представление:

"3;2"

Форматирование чисел с группировкой разрядов

Основной механизм форматирования реализуется через numberFormatter. При вызове форматтера учитываются:

  • локаль
  • числовой шаблон
  • правила группировки
  • символы разделителей

Пример инициализации форматтера:

import Globalize from "globalize";

Globalize.load(
  require("cldr-data").entireSupplemental(),
  require("cldr-data").entireMainFor("ru")
);

const globalize = new Globalize("ru");

const formatter = globalize.numberFormatter();

Форматирование числа:

formatter(1234567);
// "1 234 567"

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


Символ группировщика и его локализация

Символ группировщика определяется через CLDR-свойство symbols-numberSystem-*, например:

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

Для русской локали:

{
  "group": " ",
  "decimal": ","
}

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


Внутренний алгоритм вставки группировщика

При форматировании числа выполняются следующие шаги:

  1. Число преобразуется в строку без форматирования

  2. Отделяется целая часть от дробной

  3. Целая часть разворачивается справа налево

  4. Применяется правило группировки:

    • берётся размер первой группы
    • затем применяется повторяющийся размер вторичной группы
  5. В позиции разрядов вставляется символ группировки

  6. Строка возвращается в нормальный порядок

Пример для числа 987654321:

  • исходная строка: 987654321
  • группы: 3;3
  • результат: 987,654,321 или 987 654 321 в зависимости от локали

Поддержка нестандартных систем группировки

Индийская система чисел

Особенность индийской системы — различие первой и последующих групп:

12,34,56,789

Globalize обрабатывает это через CLDR:

"group": [3, 2]

Алгоритм:

  • первая группа справа: 3 цифры
  • далее каждые 2 цифры

Результат:

const globalize = new Globalize("hi");
const formatter = globalize.numberFormatter();

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

Арабские и восточные системы

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

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

Globalize учитывает bidi-направление и корректно размещает разделители в строке, не нарушая порядок чисел.


Парсинг чисел с группировщиками разрядов

Обратная операция — numberParser — учитывает локализованные группировщики при преобразовании строки в число.

const parser = globalize.numberParser();

parser("1 234 567");
// 1234567

Особенности:

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

Нормализация символов группировки

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

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

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

  • копированные значения из интерфейсов
  • ввод с клавиатур разных раскладок
  • данные из внешних источников

Влияние группировки на точность вычислений

Группировщики разрядов не влияют на числовое значение, однако могут повлиять на:

  • корректность парсинга
  • ошибки интерпретации строки
  • смешение локалей

Globalize исключает эти проблемы за счёт строгой привязки к CLDR-данным.


Пользовательские шаблоны и группировка

При использовании кастомных форматов:

const formatter = globalize.numberFormatter({
  minimumFractionDigits: 0,
  maximumFractionDigits: 0
});

группировка сохраняется автоматически, если явно не отключена.

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

const formatter = globalize.numberFormatter({
  useGrouping: false
});

Результат:

formatter(1234567);
// "1234567"

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

Группировка применяется только к целой части числа. Дробная часть:

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

Пример:

formatter(1234567.89);
// "1 234 567,89"

Особенности отображения в разных числовых системах

Globalize поддерживает альтернативные числовые системы CLDR:

  • латинская (0–9)
  • арабско-индийская
  • восточно-арабская

Группировка сохраняется, но цифры заменяются согласно системе:

const arabic = new Globalize("ar");
arabic.numberFormatter()(1234567);
// "١٬٢٣٤٬٥٦٧"

Совместимость группировки с валютным форматированием

При форматировании валют:

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

Пример:

const formatter = globalize.currencyFormatter("USD");

formatter(1234567);
// "$1 234 567.00"

Производительность обработки группировщиков

Группировка реализована через предрасчитанные паттерны CLDR, что позволяет:

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

Основная нагрузка приходится на:

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

Ошибки, связанные с группировкой

Типичные проблемы:

  • смешение локалей (например, en-US и ru-RU)
  • некорректные символы пробела
  • ручная модификация форматированной строки
  • попытка парсинга без учета CLDR

Globalize предотвращает такие ошибки за счёт строгой модели локализации и централизованных данных.