Text Resources And Encoding

Проблема

В кодовой базе много строковых констант находится прямо в C++ файлах. Часть файлов исторически живет в CP1251, часть уже в UTF-8, часть может иметь смешанные окончания строк.

Это создает проблемы:

  • сложнее работать из современных инструментов;
  • легко сломать кодировку при правке;
  • сложно переиспользовать тексты в web-клиенте;
  • невозможно нормально локализовать UI;
  • один и тот же текст приходится искать по коду.

Цель

Все пользовательские тексты должны постепенно переехать из C++ в отдельные resource-файлы.

C++ должен оперировать ключами:

tr("menu.balance.caption")

А не строками:

_T("Баланс")

Кодировки

Правило для новых файлов:

  • документация: UTF-8;
  • JSON web protocol: UTF-8;
  • новые resource-файлы: UTF-8;
  • старые CP1251 файлы не перекодируются массово без отдельной задачи;
  • точечные правки в старых CP1251 файлах делаются без изменения кодировки файла.

Пока допускается, что legacy runtime может читать CP1251-ресурсы. Но целевая модель для web/headless - UTF-8.

Resource Files

Предлагаемый формат для новых текстов:

resources/
  ru/
    menu.json
    controls.json
    errors.json

Пример resources/ru/menu.json:

{
  "menu.balance.caption": "Баланс",
  "menu.cashflow.caption": "ДДС",
  "menu.profitloss.caption": "ОПиУ",
  "menu.settings.caption": "Настройки"
}

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

string text = textResources.get("menu.balance.caption");

Для web-клиента тот же ключ может уходить в JSON формы:

{
  "id": "Balance",
  "captionKey": "menu.balance.caption",
  "caption": "Баланс"
}

Что выносить первым

В первую очередь выносим:

  • подписи menu item-ов;
  • подписи кнопок;
  • заголовки форм;
  • тексты ошибок;
  • подсказки и hint-ы;
  • тексты системных popup-меню.

Не выносим в первую очередь:

  • технические имена классов;
  • XML tag names;
  • внутренние enum/string protocol values;
  • имена обработчиков типа OnBalanceClick.

XML может временно содержать готовый caption:

<Item Caption="Баланс" Name="Balance"/>

Целевой вариант:

<Item CaptionKey="menu.balance.caption" Name="Balance"/>

Runtime:

CaptionKey -> TextResources -> Caption

Web:

{
  "type": "menu.item",
  "id": "Balance",
  "captionKey": "menu.balance.caption",
  "caption": "Баланс"
}

Compatibility Plan

Переход должен быть постепенным:

  1. Добавить TextResources service.
  2. Научить menu читать CaptionKey.
  3. Если CaptionKey задан, брать текст из ресурсов.
  4. Если CaptionKey не задан, использовать legacy Caption.
  5. Для новых форм использовать только CaptionKey.
  6. Старые CP1251 C++ строки убирать по мере касания файлов.

Development Rules

Для новых изменений:

  • Не добавлять новые пользовательские тексты прямо в C++ без причины.
  • Если текст нужен Win32 и Web, он должен быть resource key.
  • Если правится CP1251 файл, не перекодировать весь файл.
  • Markdown-документацию писать в UTF-8.
  • JSON-ресурсы писать в UTF-8.

Open Questions

  • Нужен ли один общий namespace ключей или namespace по модулям.
  • Где хранить клиентские переопределения текстов.
  • Нужна ли hot reload загрузка ресурсов без перезапуска сервера.
  • Как версионировать resource-файлы вместе с приложениями.