Парсинг валютных значений

Механизм разбора валютных значений в Globalize основан на данных CLDR (Unicode Common Locale Data Repository). Без предварительной загрузки соответствующих фрагментов локали корректный парсинг невозможен, поскольку библиотека не содержит встроенных правил форматирования чисел и валют.

Для работы с валютами требуются следующие категории данных:

  • numbers — базовые правила форматирования чисел
  • currencies — символы и коды валют
  • ca-gregorian — календарь (опционально, но часто подключается вместе с локалью)
  • timeZoneNames — не влияет на валюты напрямую, но обычно входит в пакет локали

Типичная инициализация включает загрузку CLDR-данных и локали:

import Globalize from "globalize";

Globalize.load(
  require("cldr-data/supplemental/likelySubtags"),
  require("cldr-data/main/ru/numbers"),
  require("cldr-data/main/ru/currencies"),
  require("cldr-data/supplemental/currencyData")
);

Globalize.locale("ru");

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


Базовый механизм разбора валютных строк

Globalize не предоставляет отдельной универсальной функции parseCurrency. Вместо этого используется общий механизм парсинга чисел с учётом формата валюты:

const parser = Globalize.numberParser({
  style: "currency",
  currency: "USD"
});

parser("$1,234.56"); // 1234.56

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

Основные принципы:

  • style: “currency” активирует правила валютного формата
  • currency задаёт конкретную валюту (USD, EUR, RUB и т.д.)
  • результат всегда возвращается как число JavaScript

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

Формат валютной строки определяется локалью, а не валютой как таковой.

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

  • en: $1,234.56
  • ru: 1 234,56 ₽
  • de: 1.234,56 €

Globalize учитывает:

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

Пример:

Globalize.locale("ru");

const parser = Globalize.numberParser({
  style: "currency",
  currency: "RUB"
});

parser("1 234,56 ₽"); // 1234.56

Символы валют и ISO-коды

В CLDR валюты представлены двумя основными способами:

  • ISO-код (USD, EUR, JPY)
  • символ ($, €, ¥)

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

Поэтому корректная конфигурация всегда должна явно указывать валюту:

Globalize.numberParser({
  style: "currency",
  currency: "USD"
});

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


Разделители, группировка и точность

Парсинг валютных значений тесно связан с правилами форматирования чисел.

Globalize учитывает:

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

  • пробел (ru)
  • запятая (en)
  • точка (de)

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

  • , в большинстве европейских локалей
  • . в англоязычных локалях

Группировка цифр

  • 3-значная группировка (1 000 000)
  • региональные исключения (индийская система: 1,00,000)

Пример:

Globalize.locale("de");

const parser = Globalize.numberParser({
  style: "currency",
  currency: "EUR"
});

parser("1.234,56 €"); // 1234.56

Отрицательные значения и учетный формат

В валютных данных встречаются два основных формата отрицательных значений:

Символ минуса

-1 234,56 ₽

Учетный (accounting) формат

(1 234,56 ₽)

Globalize учитывает оба варианта при наличии корректных CLDR-данных.

Пример:

const parser = Globalize.numberParser({
  style: "currency",
  currency: "USD"
});

parser("($1,234.56)"); // -1234.56
parser("-$1,234.56");  // -1234.56

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


Создание повторно используемых парсеров

Парсеры в Globalize являются тяжёлыми в создании, но дешёвыми в использовании после инициализации. Поэтому типичная практика — кэширование функций.

const parseUSD = Globalize.numberParser({
  style: "currency",
  currency: "USD"
});

const parseEUR = Globalize.numberParser({
  style: "currency",
  currency: "EUR"
});

Дальнейшее использование:

parseUSD("$10,000.00");
parseEUR("10.000,00 €");

Рекомендуемая модель:

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

Влияние локали на результат парсинга

Смена локали полностью изменяет правила интерпретации строки.

Globalize.locale("en");
const parserEN = Globalize.numberParser({ style: "currency", currency: "USD" });

Globalize.locale("ru");
const parserRU = Globalize.numberParser({ style: "currency", currency: "USD" });

Хотя валюта одна и та же, строки будут интерпретироваться по-разному:

  • parserEN("$1,234.56")
  • parserRU("1 234,56 $")

Это связано с тем, что Globalize опирается на локализованные шаблоны CLDR.


Нормализация входных данных

Перед парсингом часто требуется подготовка строки:

  • удаление лишних пробелов
  • замена неразрывных пробелов
  • унификация символов валют
  • устранение текстовых пометок

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

function normalize(input) {
  return input
    .replace(/\u00A0/g, " ")
    .replace(/\s+/g, " ")
    .trim();
}

После нормализации строка становится предсказуемой для парсера.


Типичные ошибки при парсинге валют

Несоответствие валюты и символа

parserUSD("1 234,56 €"); // некорректно интерпретируется или дает NaN

Отсутствие загрузки CLDR currencies

Без данных валюты парсер не распознаёт символы.

Неверная локаль

Globalize.locale("en");
parser("1 234,56 €"); // ошибка интерпретации

Смешение форматов

Комбинации вроде:

$1.234,56

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


Производительность и кэширование

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

  • кэширование парсеров
  • избегание повторной инициализации Globalize
  • предварительная компиляция функций парсинга
  • пакетная обработка строк

Эффективная схема:

  • инициализация Globalize один раз при старте
  • создание набора парсеров для всех используемых валют
  • использование их без пересоздания

Особенности работы с нестандартными валютами

Некоторые валюты имеют нестандартные форматы:

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

Пример:

const parserJPY = Globalize.numberParser({
  style: "currency",
  currency: "JPY"
});

parserJPY("¥1,000"); // 1000

Для таких валют важно учитывать, что:

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

Обработка неоднозначных строк

В реальных данных часто встречаются строки, содержащие:

  • дополнительные текстовые метки
  • HTML-символы
  • сокращения

Пример:

≈ $1,234.56 USD

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