Экранирование специальных символов

В библиотеке Globalize работа с форматированием строк опирается на правила ICU MessageFormat и CLDR. Одним из ключевых аспектов становится корректное экранирование символов, которые имеют синтаксическое значение в шаблонах сообщений, форматах чисел и дат. Нарушение этих правил приводит к ошибкам парсинга, некорректной интерполяции или полному разрушению структуры сообщения.

В системе сообщений Globalize выделяется набор символов, интерпретируемых не как текст, а как управляющие конструкции:

  • фигурные скобки { } — обозначают вставку переменных и выражений;
  • одинарная кавычка ' — используется для экранирования и управления режимом интерпретации текста;
  • символы шаблонов ICU (select, plural, offset) — зависят от контекста внутри фигурных блоков;
  • зарезервированные последовательности внутри message pattern.

Фигурные скобки являются основным элементом интерполяции:

Globalize.formatMessage(
  "messages",
  "Привет, {name}!"
);

Без корректного экранирования любые дополнительные фигурные скобки вне синтаксиса интерполяции приводят к ошибке разбора строки.


Механизм экранирования одинарных кавычек

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

Основные правила:

  • текст вне кавычек интерпретируется как потенциальный ICU-синтаксис;
  • текст внутри кавычек трактуется буквально;
  • одиночная кавычка внутри литерала экранируется удвоением.

Пример корректного использования:

Globalize.formatMessage(
  "messages",
  "It''s a simple message"
);

Здесь последовательность '' интерпретируется как один символ '.

Пример переключения режима литерала:

Globalize.formatMessage(
  "messages",
  "Start '{literal text}' end"
);

В данном случае блок {literal text} не интерпретируется как переменная.


Фигурные скобки и интерполяция

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

Globalize.formatMessage(
  "messages",
  "Balance: {amount} USD"
);

Если требуется отобразить символы { и } как часть текста, используется экранирование через кавычки:

Globalize.formatMessage(
  "messages",
  "Braces example: '{' and '}'"
);

В ICU-логике символы внутри одинарных кавычек не участвуют в синтаксическом разборе.


Экранирование в сложных ICU-выражениях

Внутри конструкций plural и select синтаксис становится более чувствительным к символам:

Globalize.formatMessage(
  "messages",
  "{count, plural, one {# item} other {# items}}"
);

Символ # имеет специальное значение — подстановка текущего числового значения. При необходимости отображения символа # как текста используется экранирование через кавычки:

Globalize.formatMessage(
  "messages",
  "{count, plural, one {'#' item} other {'#' items}}"
);

Без кавычек символ # преобразуется в значение count, что меняет смысл строки.


Символы в числовых и процентных форматах

Помимо message patterns, экранирование требуется в форматах чисел и процентов, где шаблоны используют специальные знаки:

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

Пример форматирования:

Globalize.numberFormatter({ maximumFractionDigits: 2 })(1234.567);

При необходимости отображения символов # или 0 как литералов используется экранирование через обратный слэш в формате шаблона чисел:

Globalize.numberFormatter({
  raw: "#0.00"
});

Для вывода символов как текста применяется:

'#'0.00

Экранирование в форматах дат

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

  • y — год;
  • M — месяц;
  • d — день;
  • E — день недели;
  • h, H — часы.

Любые буквенные последовательности, не являющиеся токенами, должны быть защищены кавычками.

Globalize.dateFormatter({ date: "yyyy 'year' MM 'month' dd 'day'" });

Без кавычек текстовые части интерпретируются как потенциальные токены и могут привести к ошибке или искажению результата.


Конфликты символов и неоднозначные случаи

Наиболее частые источники ошибок связаны с комбинацией ICU-синтаксиса и текстовых данных:

  1. Вложенные фигурные скобки
  2. Непарные кавычки
  3. Использование # вне контекста plural
  4. Неэкранированные апострофы в английских текстах
  5. Пересечение message format и числовых паттернов

Пример некорректного шаблона:

"Total: {count} items #final"

Символ #final может быть воспринят как часть синтаксиса plural, если находится внутри соответствующего блока.


Комбинированные стратегии экранирования

В сложных сообщениях применяется комбинация кавычек и управляющих конструкций:

Globalize.formatMessage(
  "messages",
  "{count, plural, one {'Item: #' item} other {'Items: #' items}}"
);

Здесь одновременно:

  • защищён символ #;
  • сохранена структура plural;
  • текстовые сегменты отделены кавычками;
  • синтаксис ICU не нарушен.

Поведение парсера при ошибках экранирования

При нарушении правил экранирования происходит:

  • разбор строки прерывается;
  • сообщение возвращается как необработанное;
  • либо генерируется ошибка форматирования;
  • либо подстановка переменных становится некорректной.

Типичный пример сбоя:

"Hello {name"

Отсутствие закрывающей скобки делает выражение недопустимым ICU-паттерном.


Влияние локалей на интерпретацию символов

Несмотря на то, что Globalize опирается на CLDR, правила экранирования остаются едиными для всех локалей. Отличия наблюдаются только в:

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

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