Нестрогий парсинг — механизм обработки строковых данных, при котором допускаются отклонения от идеально ожидаемого формата. В библиотеке Globalize такой подход особенно важен при работе с пользовательским вводом: датами, числами, валютами и процентами, введёнными в локализованной форме.
Строгий парсинг требует полного соответствия шаблону. Нестрогий — допускает вариативность:
Подобный механизм позволяет строить более устойчивые интерфейсы интернационализированных приложений.
При строгой проверке даже незначительное отклонение делает строку невалидной.
Пример:
const parseDate = Globalize.dateParser({
skeleton: "yMd"
});
parseDate("12/31/2025");
Если текущая локаль ожидает формат:
31.12.2025
то строка "12/31/2025" может не распознаться.
Аналогично с числами:
const parseNumber = Globalize.numberParser();
parseNumber("1 000,50");
В одной локали пробел между разрядами допустим, в другой — нет.
В Globalize парсеры создаются фабричными функциями:
numberParserdateParsercurrencyParserunitFormatter (косвенно участвует в разборе
локализованных данных)Каждый парсер:
Пример:
const Globalize = require("globalize");
const parseNumber = Globalize.numberParser();
const result = parseNumber("10,5");
Globalize не предоставляет отдельный режим
"lenient: true" как некоторые другие библиотеки, однако
нестрогий разбор достигается несколькими способами:
Разные локали используют разные символы:
| Локаль | Десятичный разделитель | Разделитель тысяч |
|---|---|---|
| en | . |
, |
| ru | , |
пробел |
| de | , |
. |
| fr | , |
пробел |
Globalize автоматически учитывает это.
Globalize.locale("ru");
const parseNumber = Globalize.numberParser();
parseNumber("10,5");
Результат:
10.5
Во многих локалях числа содержат разделители разрядов.
parseNumber("1 000 000");
Нестрогий подход предполагает предварительную очистку:
function normalizeNumber(value) {
return value.replace(/\s+/g, "");
}
parseNumber(normalizeNumber("1 000 000"));
Пользователь может вводить:
10.5
10,5
Даже если локаль ожидает только один вариант.
Пример нормализации:
function tolerantNumber(value) {
return value.replace(",", ".");
}
Комбинированный вариант:
function normalize(value) {
return value
.replace(/\s+/g, "")
.replace(",", ".");
}
const parseCurrency = Globalize.currencyParser("USD");
parseCurrency("$10.50");
Проблемные варианты:
10.50$
USD 10.50
10,50 $
Globalize может не обработать такие значения напрямую, поэтому часто применяется промежуточная обработка:
function normalizeCurrency(value) {
return value
.replace("USD", "")
.replace("$", "")
.trim();
}
После очистки:
parseNumber(normalizeCurrency("USD 10.50"));
Разные страны используют разные схемы:
| Страна | Формат |
|---|---|
| США | MM/DD/YYYY |
| Россия | DD.MM.YYYY |
| Япония | YYYY/MM/DD |
Globalize учитывает локаль:
Globalize.locale("ru");
const parseDate = Globalize.dateParser({
skeleton: "yMd"
});
Skeleton описывает структуру даты без жёсткой привязки к символам-разделителям.
Пример:
{
skeleton: "yMd"
}
Поддерживаются варианты:
2025-12-31
31.12.2025
31/12/2025
в зависимости от локали.
Это уже делает парсинг более гибким.
Пользователь может использовать:
31-12-2025
31/12/2025
31.12.2025
Предобработка:
function normalizeDate(value) {
return value.replace(/[-/]/g, ".");
}
function cleanDate(value) {
return value.trim();
}
Комбинация:
function prepareDate(value) {
return value
.trim()
.replace(/[-/]/g, ".");
}
Проблема:
9:5
09:05
9.05
Решение:
function normalizeTime(value) {
return value.replace(".", ":");
}
Практический подход — создание безопасного универсального парсера.
function createLenientNumberParser(globalize) {
const parser = globalize.numberParser();
return function(value) {
if (typeof value !== "string") {
return NaN;
}
const normalized = value
.trim()
.replace(/\s+/g, "")
.replace(",", ".");
return parser(normalized);
};
}
Использование:
const parse = createLenientNumberParser(Globalize);
parse("1 000,50");
function safeParse(parser, value) {
try {
return parser(value);
} catch (e) {
return null;
}
}
Важно помнить: некоторые ошибки не вызывают исключение, а возвращают
NaN.
const result = parseNumber("abc");
if (Number.isNaN(result)) {
console.log("Некорректное число");
}
Unicode Consortium поддерживает CLDR — набор локализационных данных, который используется Globalize.
CLDR содержит:
Благодаря этому Globalize способен корректно интерпретировать локализованные данные без ручного описания всех форматов.
const Globalize = require("globalize");
Globalize.load(
require("cldr-data/main/ru/numbers.json"),
require("cldr-data/main/ru/ca-gregorian.json"),
require("cldr-data/supplemental/likelySubtags.json"),
require("cldr-data/supplemental/numberingSystems.json")
);
Пользователь с русской локалью может вводить:
10.5
вместо:
10,5
Практический подход:
function universalNumberParser(globalize) {
const parser = globalize.numberParser();
return function(value) {
const variants = [
value,
value.replace(",", "."),
value.replace(".", ",")
];
for (const variant of variants) {
const result = parser(variant);
if (!Number.isNaN(result)) {
return result;
}
}
return NaN;
};
}
Строгая проверка:
Современные интерфейсы:
Пример:
function smartNormalize(value) {
return value
.replace(/\s+/g, "")
.replace(/[‐-‒–—]/g, "-")
.replace(",", ".");
}
Строка:
01/02/03
может означать:
Полностью нестрогий режим здесь опасен.
Слишком мягкая нормализация может привести к ошибкам:
"1,2,3"
После удаления запятых:
123
что уже меняет смысл данных.
Чрезмерная универсальность иногда разрушает преимущества локализации.
Например:
1.234
может означать:
Надёжная схема:
Globalize.locale("de");
Никогда не следует полагаться только на системные настройки браузера.
function parseWithLog(parser, value) {
const result = parser(value);
if (Number.isNaN(result)) {
console.warn("Ошибка парсинга:", value);
}
return result;
}
input.addEventListener("blur", () => {
const value = input.value;
const normalized = value
.trim()
.replace(/\s+/g, "")
.replace(",", ".");
const result = parseNumber(normalized);
if (!Number.isNaN(result)) {
input.value = result;
}
});
После успешного разбора значение обычно повторно форматируется:
const formatter = Globalize.numberFormatter({
minimumFractionDigits: 2
});
input.value = formatter(result);
Неправильно:
function parse(value) {
const parser = Globalize.numberParser();
return parser(value);
}
Правильно:
const parser = Globalize.numberParser();
function parse(value) {
return parser(value);
}
function createUniversalParser(globalize) {
const numberParser = globalize.numberParser();
return function(value) {
if (typeof value !== "string") {
return NaN;
}
let normalized = value.trim();
normalized = normalized.replace(/\s+/g, "");
const variants = [
normalized,
normalized.replace(",", "."),
normalized.replace(".", ",")
];
for (const variant of variants) {
try {
const result = numberParser(variant);
if (!Number.isNaN(result)) {
return result;
}
} catch (e) {}
}
return NaN;
};
}
| Особенность | Strict | Lenient |
|---|---|---|
| Точность | высокая | средняя |
| Гибкость | низкая | высокая |
| UX | хуже | лучше |
| Риск неоднозначности | низкий | высокий |
| Интернационализация | ограниченная | удобная |
| Устойчивость к ошибкам | слабая | высокая |
Опасно:
value.replace(/[^\d]/g, "")
Globalize.locale("fr");
вместо ручной обработки форматов.
Хорошая архитектура:
raw input
→ normalize
→ parse
→ validate
→ format
if (Number.isNaN(result)) {
// ошибка
}
Слишком агрессивный tolerant-парсинг делает поведение непредсказуемым.
Пользователь должен понимать: