Проблемы с кодировкой

Unicode является фундаментом любой современной системы интернационализации, а в контексте FormatJS именно он определяет границы корректной работы сообщений, плейсхолдеров и форматирования. Ошибки, связанные с кодировкой, в таких системах редко проявляются сразу: они возникают на стыке сериализации данных, сборки, передачи через API и последующего рендеринга сообщений в браузере или Node.js-среде.

JavaScript использует UTF-16 для представления строк, что создаёт ряд системных особенностей при работе с международным текстом. Один символ Unicode может занимать один или два 16-битных кодовых слова. Это приводит к тому, что:

  • длина строки не равна количеству символов
  • индексация может разрывать суррогатные пары
  • визуально целостные символы (emoji, редкие иероглифы) могут обрабатываться некорректно

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

Особенно критично это при работе с данными, которые проходят через несколько слоёв: backend → JSON → транспорт → фронтенд → форматирование сообщений.

Источники нарушений кодировки в интернационализированных приложениях

Основные проблемы возникают не в самой библиотеке FormatJS, а на границах системы:

  • некорректная кодировка HTTP-ответов (например, отсутствие charset=utf-8)
  • разная интерпретация символов при сериализации JSON
  • потеря информации при логировании или проксировании
  • использование устаревших БД с несоответствующей кодировкой
  • автоматическая экранизация символов в промежуточных слоях

Особое значение имеет этап агрегации переводов. Message catalogs часто хранятся в JSON-файлах, где любое нарушение UTF-8 приводит к тому, что ICU-сообщения становятся невалидными и перестают компилироваться.

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

FormatJS использует синтаксис ICU MessageFormat, в котором фигурные скобки, кавычки и специальные управляющие конструкции имеют семантическое значение.

Ключевые источники проблем:

  • незакрытые фигурные скобки { }
  • отсутствие экранирования одинарных кавычек
  • неправильное использование плейсхолдеров
  • смешивание литерального текста и синтаксиса ICU

Например, сообщение:

Hello {name}

является корректным только при условии, что name существует в контексте. Если же текст приходит из внешнего источника и содержит { или }, парсер ICU может интерпретировать это как начало выражения.

В ICU MessageFormat одинарная кавычка используется как escape-символ. Это приводит к частым ошибкам при переводах:

  • текст с апострофами ломает парсинг
  • автоматические переводчики добавляют лишние кавычки
  • HTML-атрибуты с кавычками конфликтуют с ICU-синтаксисом

JSON и влияние сериализации на кодировку сообщений

Message catalogs FormatJS обычно представлены в JSON-формате. Несмотря на то, что JSON строго определяет UTF-8 как стандарт, на практике возникают отклонения:

  • сохранение файлов в UTF-16 или ANSI в старых редакторах
  • потеря символов при копировании через буфер обмена
  • некорректная обработка escape-последовательностей \uXXXX

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

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

"\uD83D\uDE00"

Это корректная суррогатная пара emoji, но при частичном разбиении или неправильной сериализации результатом становится «битый» символ.

HTML-контекст и двойное экранирование

FormatJS часто используется совместно с React, где сообщения могут попадать в DOM. В этом контексте возникает проблема двойного экранирования:

  • JSON экранирует символы
  • React может дополнительно экранировать HTML
  • браузер интерпретирует сущности

В результате текст:

Tom & Jerry

может последовательно превратиться в:

Tom & Jerry

а затем при неправильной обработке — в:

Tom & Jerry

Подобные эффекты особенно часто возникают при смешивании ICU-сообщений и HTML-сущностей внутри одного каталога переводов.

Нормализация Unicode и визуально идентичные символы

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

  • в виде составного символа (NFC)
  • в виде разложенной последовательности (NFD)

FormatJS не выполняет автоматическую нормализацию строк, поэтому различия в форме представления приводят к:

  • несоответствию ключей переводов
  • ошибкам поиска сообщений
  • дублированию записей в каталогах

Например, слово с диакритическим знаком может существовать в двух разных формах, которые визуально идентичны, но не равны при сравнении строк.

Emoji, суррогатные пары и разрывы последовательностей

Emoji в Unicode часто состоят из нескольких кодовых точек. Проблемы возникают при:

  • разрезании строки по индексу
  • ограничении длины сообщения
  • некорректной обработке substring

Суррогатная пара может быть разорвана:

\uD83D + \uDE00

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

В контексте FormatJS это особенно критично при форматировании сообщений с динамическими значениями, где эмодзи передаются как параметры.

Bidirectional текст и смешанные направления письма

При работе с языками RTL (арабский, иврит) возникают проблемы смешивания направлений текста. ICU поддерживает сложные структуры сообщений, но кодировка может искажать порядок отображения:

  • числа в RTL-тексте отображаются в LTR-порядке
  • вставка латинских строк нарушает визуальную структуру
  • символы форматирования могут менять направление сегментов

FormatJS корректно обрабатывает ICU-структуры, но любые ошибки кодировки приводят к потере управляющих символов, которые отвечают за направление текста.

Интерполяция переменных и типы данных

Проблемы кодировки часто проявляются не напрямую, а через интерполяцию значений:

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

ICU MessageFormat ожидает строгие типы, и любое отклонение приводит к:

  • падению форматирования
  • выводы fallback-сообщений
  • частичной потере текста

Особенно часто ошибки возникают при передаче данных через REST API, где отсутствует единый стандарт сериализации локализованных значений.

Билды, транспиляция и потеря кодировки

Сборка фронтенд-приложений добавляет дополнительный слой риска:

  • минификаторы могут преобразовывать Unicode escape-последовательности
  • Babel-плагины изменяют строки сообщений
  • Webpack loaders пересобирают JSON каталоги

При неправильной конфигурации сборки возможны ситуации, когда:

  • исходный UTF-8 текст превращается в escape-последовательности
  • ICU-сообщения частично экранируются
  • символы кавычек теряют согласованность

Это особенно критично для больших проектов с множеством локалей.

Базы данных и хранение переводов

При хранении message catalogs в базе данных возникают дополнительные проблемы:

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

Даже при корректной UTF-8 настройке возможны проблемы с сортировкой и сравнением строк, особенно для языков с расширенными алфавитами или сложными диакритическими системами.

Потеря целостности сообщений при цепочке обработки

Наиболее сложные проблемы кодировки возникают не локально, а на стыке всех слоёв:

  • исходный перевод корректен
  • при сериализации JSON появляются escape-искажения
  • при транспортировке часть символов теряется
  • ICU-парсер получает неконсистентную строку

В результате FormatJS может:

  • не распознать сообщение
  • вернуть ключ вместо текста
  • выбросить ошибку парсинга ICU

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