При работе с интернационализированными приложениями числовые значения часто поступают в виде строк. Пользователь может вводить данные в формате своей локали:
1,234.56 — формат для английской локали;1 234,56 — формат для французской;1.234,56 — формат для немецкой;١٢٣٤٫٥٦ — арабские цифры.Стандартный parseFloat() в JavaScript не учитывает
локализацию. Он не умеет корректно интерпретировать:
Библиотека Globalize решает эту проблему через механизм локализованного парсинга.
Для преобразования строк в числа необходимы:
Пример подключения:
<script src="cldr.js"></script>
<script src="cldr/event.js"></script>
<script src="cldr/supplemental.js"></script>
<script src="globalize.js"></script>
<script src="globalize/number.js"></script>
Globalize использует данные Unicode CLDR. Без них парсинг невозможен.
Пример загрузки:
Globalize.load(
{
main: {
en: {
numbers: {
defaultNumberingSystem: "latn"
}
}
}
}
);
На практике обычно подключают JSON-файлы:
const enNumbers = require("cldr-data/main/en/numbers.json");
const likelySubtags = require("cldr-data/supplemental/likelySubtags.json");
Globalize.load(enNumbers);
Globalize.load(likelySubtags);
После загрузки данных создаётся объект локали:
const globalize = new Globalize("en");
Либо:
Globalize.locale("en");
Основной инструмент преобразования строк в числа —
parseNumber().
Синтаксис:
globalize.parseNumber(value);
Пример:
const globalize = new Globalize("en");
const result = globalize.parseNumber("1234.56");
console.log(result);
Результат:
1234.56
Главное преимущество Globalize — автоматическое понимание локальных форматов.
const en = new Globalize("en");
console.log(
en.parseNumber("1,234.56")
);
Результат:
1234.56
В немецком формате:
const de = new Globalize("de");
console.log(
de.parseNumber("1.234,56")
);
Результат:
1234.56
const fr = new Globalize("fr");
console.log(
fr.parseNumber("1 234,56")
);
Результат:
1234.56
parseFloat("1.234,56");
Результат:
1.234
Парсинг прекращается после запятой.
const de = new Globalize("de");
de.parseNumber("1.234,56");
Результат:
1234.56
Globalize корректно распознаёт разделители тысяч.
const en = new Globalize("en");
console.log(
en.parseNumber("10,000,000")
);
Результат:
10000000
const en = new Globalize("en");
console.log(
en.parseNumber("-1,250.75")
);
Результат:
-1250.75
const en = new Globalize("en");
console.log(
en.parseNumber("5000")
);
Результат:
5000
const en = new Globalize("en");
console.log(
en.parseNumber("98.75")
);
Результат:
98.75
Globalize умеет распознавать альтернативные системы записи чисел.
Пример арабской локали:
const ar = new Globalize("ar");
console.log(
ar.parseNumber("١٢٣٤٫٥٦")
);
Результат:
1234.56
Если строка не может быть интерпретирована как число, результатом
будет NaN.
const en = new Globalize("en");
console.log(
en.parseNumber("hello")
);
Результат:
NaN
Проверка:
const value = en.parseNumber("hello");
if (isNaN(value)) {
console.log("Ошибка преобразования");
}
Globalize поддерживает процентные форматы.
const en = new Globalize("en");
const parsePercent =
en.numberParser({ style: "percent" });
console.log(
parsePercent("50%")
);
Результат:
0.5
const fr = new Globalize("fr");
const parsePercent =
fr.numberParser({ style: "percent" });
console.log(
parsePercent("25 %")
);
Результат:
0.25
Globalize умеет преобразовывать строки валют в числовые значения.
const en = new Globalize("en");
const parseCurrency =
en.numberParser({
style: "currency",
currency: "USD"
});
console.log(
parseCurrency("$1,250.75")
);
Результат:
1250.75
Парсер учитывает:
Пример для евро:
const de = new Globalize("de");
const parseCurrency =
de.numberParser({
style: "currency",
currency: "EUR"
});
console.log(
parseCurrency("1.250,75 €")
);
Результат:
1250.75
parseNumber() удобен для простых случаев, однако
numberParser() предоставляет больше контроля.
Синтаксис:
const parser =
globalize.numberParser(options);
После создания функцию можно использовать многократно.
const en = new Globalize("en");
const parser = en.numberParser();
console.log(parser("100"));
console.log(parser("200"));
console.log(parser("300"));
Создание парсера требует подготовки шаблонов и анализа CLDR-данных. При интенсивной обработке выгоднее создавать парсер один раз.
Плохо:
for (const value of values) {
globalize.parseNumber(value);
}
Лучше:
const parser =
globalize.numberParser();
for (const value of values) {
parser(value);
}
Определяет формат данных.
Поддерживаемые значения:
"decimal""percent""currency"Используется при style: "currency".
const parser = globalize.numberParser({
style: "currency",
currency: "USD"
});
Типичный сценарий:
const userInput =
inputElement.value;
const parser =
globalize.numberParser();
const number =
parser(userInput);
if (isNaN(number)) {
showError();
}
Хотя Globalize умеет обрабатывать локализованные форматы, иногда требуется предварительная очистка:
const value =
input.trim();
const number =
parser(value);
Некоторые API возвращают локализованные строки:
{
"price": "1.234,56 €"
}
Обработка:
const parser =
de.numberParser({
style: "currency",
currency: "EUR"
});
const price =
parser(data.price);
Globalize удобно использовать совместно с пользовательской валидацией.
function validatePrice(value) {
const result =
parser(value);
return !isNaN(result);
}
form.addEventListener("submit", event => {
const value =
amountInput.value;
const amount =
parser(value);
if (isNaN(amount)) {
event.preventDefault();
alert("Некорректное число");
}
});
В некоторых локалях используются неразрывные пробелы.
Например:
"1 234,56"
Globalize корректно интерпретирует такие значения.
Метод не предназначен для:
Пример:
globalize.parseNumber("5 + 5");
Результат:
NaN
| Возможность | parseNumber() | numberParser() |
|---|---|---|
| Простое использование | Да | Да |
| Настройка параметров | Нет | Да |
| Повторное использование | Нет | Да |
| Производительность | Ниже | Выше |
| Поддержка валют | Ограниченная | Полная |
| Поддержка процентов | Ограниченная | Полная |
const globalize =
new Globalize("en");
globalize.parseNumber("100");
Ошибка:
E_MISSING_CLDR
const de = new Globalize("de");
de.parseNumber("1,234.56");
Результат будет некорректным, поскольку строка не соответствует немецкому формату.
const value =
parser(userInput);
save(value);
Без проверки можно сохранить некорректное значение.
Globalize.locale("fr");
const globalize =
new Globalize("fr");
const parsePrice =
globalize.numberParser({
style: "currency",
currency: "EUR"
});
function convertPrice(text) {
const result =
parsePrice(text);
if (isNaN(result)) {
throw new Error(
"Некорректная цена"
);
}
return result;
}
console.log(
convertPrice("1 250,50 €")
);
Результат:
1250.5
При большом количестве преобразований рекомендуется:
Globalize внутри циклов.Пример кэширования:
const parsers = {};
function getParser(locale) {
if (!parsers[locale]) {
parsers[locale] =
new Globalize(locale)
.numberParser();
}
return parsers[locale];
}
Globalize дополняет возможности встроенного Intl.
Intl.NumberFormat умеет форматировать числа:
const formatter =
new Intl.NumberFormat("de-DE");
console.log(
formatter.format(1234.56)
);
Результат:
1.234,56
Однако встроенного механизма обратного преобразования строк в числа у
Intl нет. Именно эту задачу решает Globalize.
Globalize корректно работает с:
Внутри библиотеки процесс состоит из нескольких этапов:
Схематично:
Строка
↓
Анализ локали
↓
Нормализация
↓
Удаление группировки
↓
Замена дробного разделителя
↓
Number
Внутри Globalize применяется механизм фабрики парсеров.
const parser =
globalize.numberParser();
Функция создаётся заранее и затем выполняет быстрый разбор строк без повторного анализа CLDR.
Стандартный парсер ориентирован на локализованные пользовательские числа, а не на инженерную запись.
Пример:
globalize.parseNumber("1e3");
Поведение зависит от локали и конфигурации, поэтому scientific notation обычно обрабатывают отдельно.
При обработке пользовательского ввода важно:
NaN;Пример:
const value =
parser(input);
if (isNaN(value)) {
throw new Error("Ошибка");
}
if (value < 0) {
throw new Error("Отрицательное число");
}