Skip to content

👺 shared-game: движок pokemon-battle зашивает английский UI-текст в контракт (мимо Transloco) #237

Description

@JsPowWow

Почему создана

При аудите области shared-game (ветка develop) найдено, что BattleEngine строит готовые англоязычные пользовательские строки и кладёт их в поле BattleEvent.message shared-контракта. Клиент (pokemon-battle-arena.facade.ts:153) проталкивает их в лог под ключом key: 'raw', а шаблон (pokemon-battle-arena.component.html:97) рендерит log.params?.['message'] напрямую — в обход Transloco.

Проблематика

Нарушены два правила проекта сразу:

  • i18n. CLAUDE.md (раздел «i18n — Transloco»): «Любой пользовательский текст ... через Transloco. Хардкод строк в шаблонах запрещён.» Проект поддерживает ru/en, но зашитые строки не переключаются языком — лог боя всегда на английском.
  • theme/locale-agnostic контракт. CLAUDE.md («Сервер держим pokemon/theme-agnostic»): презентация не должна протекать в shared-game — для frenzy это уже сделано (движок эмитит структурные события, текст резолвит клиент). pokemon-battle идёт против этого: BattleEvent.payload уже несёт ВСЕ структурные данные (attackerId, moveName, targetId, damage, hpBefore/After, winner), а поле message дублирует их готовой презентацией и делает локализацию структурно невозможной — фасад вынужден прокидывать key:'raw' вместо реального ключа перевода.

Вред: лог боя неинтернационализируем; презентация в shared-слое (нарушение границ); шов key:'raw' существует только из-за этого поля.

Где

  • shared-game/pokemon-battle/types.ts:69 — поле message: string в BattleEvent (презентация в контракте)
  • shared-game/pokemon-battle/battle-engine.ts:114`${attacker.name} used ${cmd.moveName}!`
  • shared-game/pokemon-battle/battle-engine.ts:139`${defender.name} took ${damage} damage!`
  • shared-game/pokemon-battle/battle-engine.ts:152`${defender.name} fainted!`
  • shared-game/pokemon-battle/battle-engine.ts:171`Battle over! Winner is ${winner}!`

Место рендера-обхода (для контекста, фикс — в shared-game): src/app/features/pokemon-battle/data/facades/pokemon-battle-arena.facade.ts:153 (key:'raw') + .../pokemon-battle-arena/pokemon-battle-arena.component.html:97.

Варианты решения

  1. Убрать message из контракта, резолвить текст на клиенте по type + payload — фасад строит ключ Transloco из event.type (battle.log.useMove, battle.log.damage, battle.log.faint, battle.log.battleOver) и подставляет параметры из payload. Плюсы: полноценная локализация, контракт снова theme/locale-agnostic, исчезает шов key:'raw'; ровно тот подход, что уже принят для frenzy. Минусы: нужно завести i18n-ключи (en/ru) под лог боя и переписать сборку лога в фасаде.
  2. Слать i18nKey рядом с message — оставить message как fallback, но добавить ключ перевода. Плюсы: меньше ломает текущий код. Минусы: дублирование презентации в контракте остаётся; полумера, key:'raw' живёт дальше.

Рекомендация

Вариант 1. Это выравнивает pokemon-battle с уже реализованным theme-agnostic подходом frenzy (движок — структурные события, клиент — текст через Transloco) и закрывает нарушение Transloco-правила разом. Объём — M (i18n-ключи en/ru + сборка ключа в фасаде из type/payload + удаление message из контракта и движка).

Связано с #120 (хардкод текста мимо Transloco), но это отдельный класс: там — строки в .html-шаблонах (фикс = обернуть в t(...)), здесь — презентация, зашитая в shared-контракт движка (фикс = реструктурировать контракт события). Слой и способ исправления разные, поэтому отдельный issue, а не пункт в #120.

[CLAUDE.md — «i18n — Transloco», «Сервер держим pokemon/theme-agnostic»; shared-game/frenzy + src/app/features/frenzy как образец: движок без презентации, текст на клиенте через Transloco]

Metadata

Metadata

Assignees

No one assigned

    Labels

    AI TechDebtTechnical debt surfaced by the AI codebase auditscope:shared-gameAudit finding located in the shared-game feature/layer

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions