Невалидные паттерны

В Globalize паттерны форматирования опираются на стандарты CLDR и ICU MessageFormat. Любое отклонение от синтаксических правил приводит к тому, что форматтер либо выбрасывает исключение, либо возвращает некорректный результат (например, исходную строку без обработки).

Невалидные паттерны возникают в трёх основных категориях:

  • синтаксические ошибки (сломанная структура строки)
  • семантические ошибки (валидный синтаксис, но недопустимые символы или комбинации)
  • несовместимость с данными CLDR для выбранной локали

Globalize не «исправляет» паттерны автоматически, а строго следует спецификации ICU.


Невалидные числовые паттерны

Форматирование чисел в Globalize основано на ICU Number Patterns.

Пример корректного паттерна:

Globalize("en").numberFormatter({ minimumFractionDigits: 2 })(1234.5);
// "1,234.50"

Нарушение структуры разрядности

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

  • двойные разделители групп
  • некорректное чередование # и 0
  • нарушение логики разрядов
Globalize("en").numberFormatter({ raw: "#,,##0.##" })(1234);

Такой паттерн не имеет смысла: двойная запятая ломает правила группировки.


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

В числовых паттернах допустим только ограниченный набор символов:

  • 0 — обязательная цифра
  • # — необязательная цифра
  • , — разделитель групп
  • . — десятичный разделитель
  • % — процент

Любые другие символы приводят к ошибке:

Globalize("en").numberFormatter({ raw: "###0abc###" })(1000);

Результат зависит от реализации, но чаще всего это Error: Invalid number pattern.


Множественные десятичные точки

Паттерн может содержать только один десятичный разделитель:

Globalize("en").numberFormatter({ raw: "#0.##.##" })(12.34);

Такая строка считается невалидной, поскольку ICU не определяет вторую точку как допустимую часть формата.


Некорректные процентные и экспоненциальные формы

Ошибки часто возникают при комбинировании символов:

Globalize("en").numberFormatter({ raw: "#0%#E0" })(1000);

Процентный формат и экспоненциальная запись не могут быть объединены в одном паттерне.


Невалидные паттерны дат и времени

Форматирование дат в Globalize опирается на ICU DateTime Pattern Syntax.

Недопустимые символы шаблона

Каждый символ в паттерне имеет строгое значение:

  • y — год
  • M — месяц
  • d — день
  • H — часы

Использование неизвестных символов приводит к ошибке:

Globalize("en").dateFormatter({ raw: "yyyy-XMM-dd" })(new Date());

Символ X не определён в ICU и делает паттерн невалидным.


Ошибки кавычек и литералов

ICU использует одинарные кавычки для экранирования текста.

Невалидные случаи:

Globalize("en").dateFormatter({ raw: "yyyy-MM-dd 'at HH:mm" })(new Date());

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


Конфликт локальных правил

Некоторые паттерны могут быть формально корректными, но не поддерживаться локалью:

Globalize("ar").dateFormatter({ raw: "EEEE, MMMM d, yyyy GGGG" })(new Date());

Если CLDR для данной локали не содержит данных для календарной эры GGGG, результат становится неопределённым или fallback-строкой.


Использование неподдерживаемых skeleton-символов

Хотя skeleton-подход более абстрактен, попытка смешивания его с raw-паттернами вызывает ошибки интерпретации:

Globalize("en").dateFormatter({ skeleton: "yMMMxyz" });

xyz не является частью допустимого skeleton-набора.


Невалидные паттерны MessageFormat

MessageFormat в Globalize использует ICU Message syntax.

Несбалансированные фигурные скобки

Самая частая ошибка:

Globalize("en").messageFormatter(
  "{count, plural, one {1 item} other {# items}"
);

Отсутствует закрывающая } для блока plural.


Ошибки plural категорий

ICU требует строго определённые категории: one, few, many, other.

Globalize("en").messageFormatter(
  "{count, plural, one {1 item} zero {no items} other {# items}}"
);

Категория zero не поддерживается для английской локали в CLDR, что делает паттерн невалидным.


Некорректный select/selectordinal

Globalize("en").messageFormatter(
  "{rank, selectordinal, 1 {1st} 2 {2nd} few {#th} other {#th}}"
);

Здесь категория few не соответствует правилам ordinal selection.


Экранирование апострофов

ICU использует апостроф как управляющий символ.

Ошибочный пример:

Globalize("en").messageFormatter(
  "It's {count} items"
);

Апостроф трактуется как начало escape-последовательности, и строка может быть разобрана неверно. Корректный вариант требует удвоения:

"It''s {count} items"

Несоответствие CLDR-данных

Даже корректный синтаксически паттерн может стать невалидным при отсутствии данных в CLDR.

Отсутствующие локали

Globalize("xx").numberFormatter({ style: "currency", currency: "USD" })(100);

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


Неполные наборы данных

Некоторые локали могут не содержать:

  • правил pluralization
  • форматов дат
  • символов валют

Это приводит к fallback-поведению, которое часто воспринимается как «невалидный паттерн».


Ошибки при использовании raw и skeleton одновременно

Globalize разделяет два подхода:

  • raw — явный ICU-паттерн
  • skeleton — абстрактное описание формата

Их смешивание в одном вызове приводит к неопределённому поведению:

Globalize("en").dateFormatter({
  raw: "yyyy-MM-dd",
  skeleton: "yMd"
});

Приоритет одного из параметров не гарантирован, результат может зависеть от версии реализации.


Проблемы экранирования и спецсимволов

Процент и промилле

Некорректное использование:

Globalize("en").numberFormatter({ raw: "#0%%" })(0.12);

Двойной символ % не соответствует ICU-правилам.


Неправильное использование знаков группировки

Globalize("en").numberFormatter({ raw: "#0,0,0" })(1000);

ICU требует строгого порядка разделителей групп, нарушение делает шаблон невалидным.


Диагностика невалидных паттернов

Типичные проявления ошибок:

  • выброс исключения Error: Invalid pattern
  • возврат исходной строки без форматирования
  • частичное применение формата (особенно в MessageFormat)
  • fallback на локаль root

Причины обычно фиксируются на этапе парсинга паттерна, а не во время выполнения форматирования.


Характерные классы ошибок в реальных сценариях

  • перенос ICU-паттернов из Java без адаптации под CLDR
  • ручное конструирование строк форматирования без проверки синтаксиса
  • смешивание разных систем форматирования (moment.js-like паттерны и ICU)
  • локализация без полной загрузки CLDR сегментов
  • использование устаревших категорий plural rules

Особенности поведения при частичной валидности

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

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

Это особенно характерно для сложных MessageFormat выражений с вложенными блоками.