Skip to content

feat(app): extract the user slice into i18n, en+ru (CLEAN-37) - #31

Merged
maksymhryzodub-prog merged 2 commits into
feat/CLEAN-33-i18n-strategyfrom
feat/CLEAN-37-i18n-user-slice
Aug 19, 2026
Merged

feat(app): extract the user slice into i18n, en+ru (CLEAN-37)#31
maksymhryzodub-prog merged 2 commits into
feat/CLEAN-33-i18n-strategyfrom
feat/CLEAN-37-i18n-user-slice

Conversation

@maksymhryzodub-prog

Copy link
Copy Markdown
Contributor

Implements CLEAN-37 (subtask of CLEAN-33): https://dreamvention.atlassian.net/browse/CLEAN-37

Stacked on #30 — targets feat/CLEAN-33-i18n-strategy, since the LOCALES constant, the sync script and the CI check all live there. GitHub retargets this to main once #30 merges.

The auth screens were fully hardcoded and the slice had no i18n block at all. This adds one, plus en/ru files, and moves all 39 strings out: the form, the marketing panel on the auth layout, and the closed-registration state. A re-scan finds zero hardcoded strings left in the slice.

The interesting part: copy that lives in script

This slice is the reason the ADR has the "script holds keys, not text" rule, and it exercises every shape of it:

  • Validationerrors.email = 'account.email_invalid' instead of "Enter a valid email"; the template renders {{ $t(errors.email!) }}.
  • State-dependent copytitleKey, subtitleKey, submitLabelKey are computeds returning keys ("Welcome back" vs "Create your account", "Signing in…" vs "Sign in").
  • Failure copy — both providers dropped their hardcoded English fallback. A provider now reports only whether the call failed (failed) and what the server said (errorMessage, English by design per the ADR); the form owns the fallback wording and picks the key by mode.

useI18n() appears nowhere in the slice — templates use the injected $t.

app_name is reused from the common slice rather than redefined: vue-i18n merges slice messages into one global scope, so brand tokens don't need duplicating.

Bug this uncovered

"jane@example.com" breaks the production build. vue-i18n reads a bare @ as a linked-message reference, so the message compiler fails — and CI would not have caught it, because CI runs typecheck / lint / test / i18n:check, not nuxt build.

Fixed on four fronts:

  • the placeholder is now the escaped literal jane{'@'}example.com;
  • i18n:check reports a bare @ or | on either side (en or ru) — so CI catches it from now on;
  • the translator prompt states the rule;
  • a model reply that breaks it is rejected before it reaches disk.

Verification

  • nuxt typecheck and nuxt build clean; the Russian auth strings appear in the built client chunks.
  • i18n:check green across 5 slice/locale pairs, and it was seen failing on the unescaped @ before the fix.
  • Translations generated by bun run i18n:sync (40 keys), including localized example data — the name and email placeholders become Иван Иванов / ivan{'@'}example.com.

🤖 Generated with Claude Code

maksymhryzodub-prog added a commit that referenced this pull request Aug 19, 2026
…N-37)

The i18n work lands as a stack: each subtask branches off the previous
one and the whole chain merges bottom-up at the end. With
`pull_request: branches: [main]` none of those PRs triggers CI — #31 has
zero workflow runs — so the first real check would happen at the final
merge, on four slices at once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
maksymhryzodub-prog and others added 2 commits August 20, 2026 00:07
The auth screens were fully hardcoded and the slice had no i18n block at
all. Adds one (registering the shared LOCALES) plus en/ru locale files,
and moves all 39 strings out of the templates: the form, the marketing
panel on the auth layout, and the closed-registration state.

Applies the "script holds keys, not text" rule from the ADR, which this
slice is the reason for. Field validation now assigns
'account.email_invalid' rather than "Enter a valid email"; the
state-dependent title, subtitle and submit label became *Key computeds
rendered with $t. Both providers lose their hardcoded English failure
fallback — the form owns that copy and picks the key by mode, so a
provider now reports only whether the call failed and what the server
said. useI18n() is not used anywhere in the slice.

Adds a guard to i18n-sync for the bug this uncovered: "jane@example.com"
compiles to a linked-message reference in vue-i18n and fails the build.
The escaped literal {'@'} is now required, i18n:check reports a bare @
or | on either side, the translator prompt states the rule, and a reply
that breaks it is rejected before it reaches disk. Worth catching there
because CI runs typecheck/lint/test and this script — not nuxt build —
so a broken message would otherwise surface in production.

Verified: nuxt typecheck and nuxt build clean, ru strings in the client
chunks, i18n:check green over 5 slice/locale pairs, and a re-scan finds
zero hardcoded strings left in the slice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…N-37)

The i18n work lands as a stack: each subtask branches off the previous
one and the whole chain merges bottom-up at the end. With
`pull_request: branches: [main]` none of those PRs triggers CI — #31 has
zero workflow runs — so the first real check would happen at the final
merge, on four slices at once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@maksymhryzodub-prog
maksymhryzodub-prog force-pushed the feat/CLEAN-37-i18n-user-slice branch from d07a1d3 to 1ced349 Compare August 19, 2026 21:07
@maksymhryzodub-prog
maksymhryzodub-prog merged commit 8de2070 into main Aug 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant