Skip to content

🟑 pokemon-tamagotchi: i18n β€” Π³ΠΎΠ½ΠΊΠ° синхронного translate ΠΏΠΎ Π»Π΅Π½ΠΈΠ²ΠΎΠΌΡƒ scope, ΠΎΡΠΈΡ€ΠΎΡ‚Π΅Π²ΡˆΠΈΠ΅ ΠΊΠ»ΡŽΡ‡ΠΈ, дублированная эвристика ΠΊΠ»ΡŽΡ‡Π°Β #265

Description

@JsPowWow

ΠŸΠΎΡ‡Π΅ΠΌΡƒ создана

Аудит Ρ„ΠΈΡ‡ΠΈ pokemon-tamagotchi. НайдСны ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ i18n/Transloco: ΠΎΡΠΈΡ€ΠΎΡ‚Π΅Π²ΡˆΠΈΠ΅ ΠΈ ΠΌΡ‘Ρ€Ρ‚Π²ΠΎ-Π΄ΡƒΠ±Π»ΠΈΡ€ΡƒΡŽΡ‰ΠΈΠ΅ ΠΊΠ»ΡŽΡ‡ΠΈ, синхронный translate() ΠΏΠΎ Π»Π΅Π½ΠΈΠ²ΠΎ Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌΠΎΠΌΡƒ scope (Π³ΠΎΠ½ΠΊΠ°) ΠΈ хрупкая эвристика «строка с Ρ‚ΠΎΡ‡ΠΊΠΎΠΉ = ΠΊΠ»ΡŽΡ‡ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄Π°Β», продублированная Π² Π΄Π²ΡƒΡ… мСстах. ВсС ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½Ρ‹ двумя Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π°ΠΌΠΈ.

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°Ρ‚ΠΈΠΊΠ°

🟑 1. Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½Ρ‹ΠΉ translate() ΠΏΠΎ Π»Π΅Π½ΠΈΠ²ΠΎ Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌΠΎΠΌΡƒ scope Π² root-сСрвисС ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ

TamagotchiNotificationService (@Injectable({ providedIn:'root' })) ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ΠΈΡ‚ ΠΊΠ»ΡŽΡ‡ΠΈ тостов синхронно: this.transloco.translate(\${NOTIFICATION_SCOPE}.${key}`)(notification.service.ts:83-85). ScopepokemonTamagotchi β€” **Π»Π΅Π½ΠΈΠ²ΠΎ Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌΡ‹ΠΉ** (provideTranslocoScopeΠ² routes.ts:17 + HTTP-Ρ„Π΅Ρ‚Ρ‡ JSON ΠΏΡ€ΠΈ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Ρ‹*transloco). Но публикация ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ запускаСтся эффСктами фасада нСзависимо ΠΎΡ‚ готовности scope: эффСкт Β«evolution readyΒ» (facade.ts:136-146) срабатываСт сразу послС bootstrapFromProfile, Ссли восстановлСнноС состояниС ΡƒΠΆΠ΅ удовлСтворяСт критСриям; ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ Ρ‚ΠΈΠΊ Ρ‚Π°ΠΉΠΌΠ΅Ρ€Π° (facade.ts:284) ΠΌΠΎΠΆΠ΅Ρ‚ принСсти alerts послС ΠΎΡ„Π»Π°ΠΉΠ½-decay. Если JSON scope Π΅Ρ‰Ρ‘ Π½Π΅ Π΄ΠΎΠ΅Ρ…Π°Π», translate()Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ **сам ΠΊΠ»ΡŽΡ‡** β€” Taiga-тост ΠΏΠΎΠΊΠ°ΠΆΠ΅Ρ‚pokemonTamagotchi.notifications.evolution.readyTitle` вмСсто тСкста. Π’ΠΎ ΠΆΠ΅ ΠΎΠΊΠ½ΠΎ β€” ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ языка.

Нюанс: само отступлСниС ΠΎΡ‚ ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½Π° Β«Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Π° Π² шаблонС» здСсь ΠΎΠΏΡ€Π°Π²Π΄Π°Π½ΠΎ β€” тостам Ρ‡Π΅Ρ€Π΅Π· AppNotificationService Π½ΡƒΠΆΠ½Π° готовая строка, Π° история Π² шаблонС ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Ρ€Π΅-пСрСводится Ρ‡Π΅Ρ€Π΅Π· t(). ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° ΠΈΠΌΠ΅Π½Π½ΠΎ Π² синхронности Π΄ΠΎ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ scope.

πŸ«₯ 2. ΠžΡΠΈΡ€ΠΎΡ‚Π΅Π²ΡˆΠΈΠ΅ i18n-ΠΊΠ»ΡŽΡ‡ΠΈ: disabledSleeping ΠΈ мёртвая Π²Π΅Ρ‚ΠΊΠ° alerts.evolutionReady

  • actions.disabledSleeping (Β«PokΓ©mon is sleepingΒ») Π΅ΡΡ‚ΡŒ Π² ΠΎΠ±ΠΎΠΈΡ… JSON, Π½ΠΎ Π½Π΅ упоминаСтся Π½ΠΈΠ³Π΄Π΅ Π² src: ΠΊΠΎΠ³Π΄Π° ΠΏΠΎΠΊΠ΅ΠΌΠΎΠ½ спит, awake-ΠΊΠ½ΠΎΠΏΠΊΠΈ Π½Π΅ рСндСрятся, Π° ΠΊΠ½ΠΎΠΏΠΊΠ΅ sleep/wakeUp назначаСтся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ disabledTraining.
  • notifications.alerts.evolutionReady.title/message (en/ru.json:89-92) достиТимы Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‡Π΅Ρ€Π΅Π· ALERT_TEMPLATES.evolutionReady, Π½ΠΎ Π½ΠΈ ΠΎΠ΄ΠΈΠ½ ΠΏΡ€ΠΎΠ΄-ΠΏΡƒΡ‚ΡŒ Π½Π΅ ΠΏΠΎΡ€ΠΎΠΆΠ΄Π°Π΅Ρ‚ StatusAlertType 'evolutionReady': STAT_ALERT_CONFIG ΠΏΠΎΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ hunger/mood/energy/hydration, Π° СдинствСнный ΠΌΠ΅Ρ‚ΠΎΠ΄, способный Π΄ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΉ alert (notifyStatusAlerts), зовётся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ· спСка. Π–ΠΈΠ²ΠΎΠΉ ΠΏΡƒΡ‚ΡŒ Β«Π³ΠΎΡ‚ΠΎΠ² ΠΊ ΡΠ²ΠΎΠ»ΡŽΡ†ΠΈΠΈΒ» ΠΈΠ΄Ρ‘Ρ‚ Ρ‡Π΅Ρ€Π΅Π· notifyEvolutionReady β†’ ΠΊΠ»ΡŽΡ‡ notifications.evolution.readyTitle. Π˜Ρ‚ΠΎΠ³: Π΄Π²Π΅ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½Ρ‹Π΅ ΠΏΠ°Ρ€Ρ‹ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ΠΎΠ² ΠΎΠ΄Π½ΠΎΠ³ΠΎ события, ΠΎΠ΄Π½Π° мёртвая β€” мусор Π² ΠΎΠ±Π΅ΠΈΡ… локалях ΠΈ Π»ΠΎΠ²ΡƒΡˆΠΊΠ° для ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄Ρ‡ΠΈΠΊΠ°.

πŸ«₯ 3. Π₯рупкая эвристика «содСрТит Ρ‚ΠΎΡ‡ΠΊΡƒ = ΠΊΠ»ΡŽΡ‡ ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄Π°Β», продублированная Π² Π΄Π²ΡƒΡ… мСстах

NotificationModel.title/message нСсут Π»ΠΈΠ±ΠΎ i18n-ΠΊΠ»ΡŽΡ‡, Π»ΠΈΠ±ΠΎ сырой тСкст (имя ΠΏΠΎΠΊΠ΅ΠΌΠΎΠ½Π° Π² notificationFromEvolutionReady), Π° Ρ€Π°Π·Π»ΠΈΡ‡Π°ΡŽΡ‚ΡΡ эвристикой value.includes('.'), скопированной Π² Π΄Π²Π° нСзависимых мСста: resolveText (notification.service.ts:75-81) ΠΈ isTranslationKey (notification.component.ts:24-26). ΠšΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ Π½ΠΈΠ³Π΄Π΅ Π½Π΅ зафиксирован Ρ‚ΠΈΠΏΠΎΠΌ: любоС Π±ΡƒΠ΄ΡƒΡ‰Π΅Π΅ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ с сырым тСкстом, содСрТащим Ρ‚ΠΎΡ‡ΠΊΡƒ (Β«Progress saved.Β», имя Β«Mr. MimeΒ»), ΠΌΠΎΠ»Ρ‡Π° ΡƒΠΉΠ΄Ρ‘Ρ‚ Π² transloco.translate β€” Π² истории ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ ΡƒΠ²ΠΈΠ΄ΠΈΡ‚ pokemonTamagotchi.notifications.Progress saved.. Ошибка Π½Π΅ ловится компилятором/Π»ΠΈΠ½Ρ‚ΠΎΠΌ, Ρ‡ΠΈΠ½ΠΈΡ‚ΡŒ придётся Π² Π΄Π²ΡƒΡ… Ρ„Π°ΠΉΠ»Π°Ρ….

Π“Π΄Π΅

  • src/app/features/pokemon-tamagotchi/ui/services/notification.service.ts:84 β€” синхронный translate()
  • src/app/features/pokemon-tamagotchi/data/facades/tamagotchi.facade.ts:141 β€” публикация Π΄ΠΎ готовности scope (evolution-ready)
  • src/app/features/pokemon-tamagotchi/data/facades/tamagotchi.facade.ts:284 β€” публикация Π½Π° ΠΏΠ΅Ρ€Π²ΠΎΠΌ Ρ‚ΠΈΠΊΠ΅ (alerts)
  • public/i18n/pokemonTamagotchi/en.json:47 β€” ΠΎΡΠΈΡ€ΠΎΡ‚Π΅Π²ΡˆΠΈΠΉ actions.disabledSleeping
  • public/i18n/pokemonTamagotchi/ru.json:47 β€” Ρ‚ΠΎ ΠΆΠ΅
  • public/i18n/pokemonTamagotchi/en.json:89 β€” мёртвая Π²Π΅Ρ‚ΠΊΠ° alerts.evolutionReady
  • public/i18n/pokemonTamagotchi/ru.json:89 β€” Ρ‚ΠΎ ΠΆΠ΅
  • src/app/features/pokemon-tamagotchi/data/helpers/notification-factory.helper.ts:24 β€” ALERT_TEMPLATES.evolutionReady
  • src/app/features/pokemon-tamagotchi/ui/services/notification.service.ts:76 β€” эвристика .includes('.') (копия 1)
  • src/app/features/pokemon-tamagotchi/ui/components/notifications/notification.component.ts:25 β€” эвристика .includes('.') (копия 2)

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ

  1. Асинхронный ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ тостов (Π±Π°Π³ 1). Π—Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ синхронный translate Π½Π° selectTranslate(key, params, 'pokemonTamagotchi') (ΠΆΠ΄Ρ‘Ρ‚ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ scope; ΠΏΠ΅Ρ€Π²ΡƒΡŽ эмиссию Ρ‡Π΅Ρ€Π΅Π· firstValueFrom/take(1)) ΠΈ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ тост с Π³ΠΎΡ‚ΠΎΠ²ΠΎΠΉ строкой. ΠΠ»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π° β€” ΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Ρ‚ΡŒ тосты Ρ‚ΠΎΠ»ΡŒΠΊΠΎ послС transloco.events$ (scope loaded).
  2. Π£Π±Ρ€Π°Ρ‚ΡŒ ΠΎΡΠΈΡ€ΠΎΡ‚Π΅Π²ΡˆΠΈΠ΅ ΠΊΠ»ΡŽΡ‡ΠΈ (Π±Π°Π³ 2). Π£Π΄Π°Π»ΠΈΡ‚ΡŒ actions.disabledSleeping, Π±Π»ΠΎΠΊ alerts.evolutionReady ΠΈΠ· en/ru.json, запись evolutionReady ΠΈΠ· ALERT_TEMPLATES, Ρ‡Π»Π΅Π½ 'evolutionReady' ΠΈΠ· StatusAlertType ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄ notifyStatusAlerts. Π›ΠΈΠ±ΠΎ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎ Π·Π°Π΄Π΅ΠΉΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ (ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ disabledSleeping ΠΊΠ°ΠΊ hint Ρƒ wakeUp, ΡΠ»Π°Ρ‚ΡŒ evolutionReady Ρ‡Π΅Ρ€Π΅Π· processStatusAlerts) β€” Π½ΠΎ Ρ‚ΠΎΠ³Π΄Π° ΡƒΠ±Ρ€Π°Ρ‚ΡŒ Π΄ΡƒΠ±Π»ΡŒ notifications.evolution.readyTitle.
  3. Π—Π°Ρ„ΠΈΠΊΡΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ Π² ΠΌΠΎΠ΄Π΅Π»ΠΈ (Π±Π°Π³ 3). Π Π°Π·Π΄Π΅Π»ΠΈΡ‚ΡŒ поля (titleKey: string | null + titleText: string | null, Π»ΠΈΠ±ΠΎ дискриминатор isTranslated: boolean Π² NotificationModel) ΠΈ ΡƒΠ±Ρ€Π°Ρ‚ΡŒ ΠΎΠ±Π΅ эвристики β€” сСрвис ΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Ρ‡ΠΈΡ‚Π°ΡŽΡ‚ явный ΠΏΡ€ΠΈΠ·Π½Π°ΠΊ.

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

Π‘Π°Π³ 1 β€” самый Π²Π°ΠΆΠ½Ρ‹ΠΉ (user-facing: сырой ΠΊΠ»ΡŽΡ‡ Π² тостС ΠΏΡ€ΠΈ ΠΎΡ„Π»Π°ΠΉΠ½-Π²ΠΎΠ·Π²Ρ€Π°Ρ‚Π΅ ΠΈ смСнС языка), Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ 1 (selectTranslate). Π‘Π°Π³ 3 β€” устраняСт ΠΊΠΎΡ€Π΅Π½ΡŒ (ΡƒΠ³Π°Π΄Ρ‹Π²Π°Π½ΠΈΠ΅ ΠΏΠΎ Ρ‚ΠΎΡ‡ΠΊΠ΅) ΠΈ Π·Π°ΠΎΠ΄Π½ΠΎ снимаСт Π΄ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ β€” Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ 3, Π΄Π΅Π»Π°Ρ‚ΡŒ вмСстС с Π±Π°Π³ΠΎΠΌ 2 (ΠΎΠ±Π° ΠΏΡ€ΠΎ модСль ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ). Π‘Π°Π³ 2 β€” Π²Ρ‹Ρ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ мусор ΠΈΠ· Π»ΠΎΠΊΠ°Π»Π΅ΠΉ (Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ Β«ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒΒ», Ρ‚.ΠΊ. ΠΆΠΈΠ²ΠΎΠΉ ΠΏΡƒΡ‚ΡŒ ΡΠ²ΠΎΠ»ΡŽΡ†ΠΈΠΈ ΡƒΠΆΠ΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Ρ‡Π΅Ρ€Π΅Π· ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡). ВсС ΠΏΡ€Π°Π²ΠΊΠΈ Π² data/ui-слоС ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ + JSON.

ΠŸΡ€Π°Π²ΠΈΠ»Π°: CLAUDE.md (i18n β€” Transloco), скилл angular-best-practices-transloco (Π»Π΅Π½ΠΈΠ²Ρ‹Π΅ scope, selectTranslate); Π±Π°Π³ΠΈ 2-3 β€” ΠΊΠΎΠ½ΡΠΈΡΡ‚Π΅Π½Ρ‚Π½ΠΎΡΡ‚ΡŒ/ΡƒΡΡ‚ΠΎΠΉΡ‡ΠΈΠ²ΠΎΡΡ‚ΡŒ.

Metadata

Metadata

Labels

AI TechDebtTechnical debt surfaced by the AI codebase auditscope:pokemon-tamagotchiAudit finding located in the pokemon-tamagotchi feature/layer

Type

No type

Fields

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