Files
konstruct/.planning/phases/07-multilanguage/07-02-SUMMARY.md
Adolfo Delorenzo 1018269f82 docs(07-02): complete frontend i18n infrastructure plan
- Create 07-02-SUMMARY.md with full execution details
- Update STATE.md with position, decisions, metrics
- Update ROADMAP.md progress (Phase 7: 2/4 plans complete)
- Mark requirements I18N-01, I18N-02 complete in REQUIREMENTS.md
2026-03-25 16:30:37 -06:00

8.2 KiB

phase: 07-multilanguage plan: "02" subsystem: ui tags: [next-intl, i18n, react, cookie, auth-jwt] # Dependency graph requires: - phase: 07-multilanguage provides: Phase 07-01 language system prompt builder for backend provides: - next-intl v4 cookie-based i18n infrastructure with no URL routing - complete en/es/pt message files covering all portal pages and components - LanguageSwitcher component rendered in sidebar and login page - Auth.js JWT language field — persists language across sessions - Browser locale auto-detection on first visit (login page) - locale cookie synced from DB-authoritative session in SessionSync affects: [07-03-multilanguage, portal-components] # Tech tracking tech-stack: added: [next-intl@4.8.3, @formatjs/intl-localematcher, negotiator, @types/negotiator] patterns: - cookie-based locale detection via getRequestConfig reading konstruct_locale cookie - i18n/locales.ts separates shared constants from server-only request.ts - NextIntlClientProvider wraps app in async Server Component root layout - LanguageSwitcher uses isPreAuth prop to skip DB/JWT update on login page key-files: created: - packages/portal/i18n/request.ts - packages/portal/i18n/locales.ts - packages/portal/messages/en.json - packages/portal/messages/es.json - packages/portal/messages/pt.json - packages/portal/components/language-switcher.tsx modified: - packages/portal/next.config.ts - packages/portal/app/layout.tsx - packages/portal/components/nav.tsx - packages/portal/components/session-sync.tsx - packages/portal/lib/auth.ts - packages/portal/lib/auth-types.ts - packages/portal/lib/api.ts - packages/portal/app/(auth)/login/page.tsx key-decisions: - "i18n/locales.ts created to hold shared constants (SUPPORTED_LOCALES, LOCALE_COOKIE, isValidLocale) — client components cannot import i18n/request.ts because it imports next/headers (server-only)" - "LOCALE_COOKIE = 'konstruct_locale' — cookie-based locale with no URL routing avoids App Router [locale] segment pattern entirely" - "LanguageSwitcher isPreAuth prop — skips DB PATCH and session.update() on login page, sets cookie only" - "api.patch() added to api client — language switcher uses existing RBAC-header-aware fetch wrapper" - "SessionSync reconciles locale cookie from session.user.language — ensures DB-authoritative value wins after login" patterns-established: - "Server-only i18n: i18n/request.ts imports next/headers; shared constants in i18n/locales.ts for client use" - "Auth.js JWT language pattern: trigger=update with session.language updates token.language (same as active_tenant_id)" - "Cookie-first locale: setLocaleCookie + router.refresh() gives instant locale switch without full page reload" requirements-completed: [I18N-01, I18N-02, I18N-06] # Metrics duration: 9min completed: 2026-03-25

Phase 7 Plan 02: Frontend i18n Infrastructure Summary

next-intl v4 cookie-based i18n with EN/ES/PT message files, sidebar LanguageSwitcher, and Auth.js JWT language persistence

Performance

  • Duration: 9 min
  • Started: 2026-03-25T22:00:07Z
  • Completed: 2026-03-25T22:08:54Z
  • Tasks: 2
  • Files modified: 14

Accomplishments

  • next-intl v4.8.3 installed and configured with cookie-based locale detection (no URL routing) — reads konstruct_locale cookie in i18n/request.ts
  • Complete message files created for all portal pages and components in 3 languages: en.json, es.json (Latin American Spanish), pt.json (Brazilian Portuguese)
  • LanguageSwitcher component renders compact EN/ES/PT buttons — sets cookie, PATCHes DB, updates Auth.js JWT, calls router.refresh()
  • Login page uses useTranslations('login') for all form strings and includes pre-auth LanguageSwitcher with browser locale auto-detection on first visit
  • Auth.js JWT extended with language field following the exact trigger="update" pattern already used for active_tenant_id
  • SessionSync reconciles locale cookie from session.user.language to ensure DB-authoritative value is applied after login

Task Commits

Each task was committed atomically:

  1. Task 1: Install next-intl, configure i18n infrastructure, create complete message files - e33eac6 (feat)
  2. Task 2: Language switcher component + Auth.js JWT language sync + login page locale detection - 6be47ae (feat)

Plan metadata: (to be committed with SUMMARY.md)

Files Created/Modified

  • packages/portal/i18n/locales.ts - Shared locale constants safe for Client Components (SUPPORTED_LOCALES, LOCALE_COOKIE, isValidLocale)
  • packages/portal/i18n/request.ts - next-intl server config reading locale from cookie; re-exports from locales.ts
  • packages/portal/messages/en.json - Complete English translation source (nav, login, dashboard, agents, templates, wizard, onboarding, chat, billing, usage, apiKeys, users, tenants, common, impersonation, tenantSwitcher, validation, language)
  • packages/portal/messages/es.json - Complete Latin American Spanish translations
  • packages/portal/messages/pt.json - Complete Brazilian Portuguese translations
  • packages/portal/components/language-switcher.tsx - EN/ES/PT switcher with isPreAuth prop
  • packages/portal/next.config.ts - Wrapped with createNextIntlPlugin
  • packages/portal/app/layout.tsx - Async Server Component with NextIntlClientProvider
  • packages/portal/components/nav.tsx - Added LanguageSwitcher in user section
  • packages/portal/components/session-sync.tsx - Added locale cookie sync from session
  • packages/portal/lib/auth.ts - language field in JWT callback and session callback
  • packages/portal/lib/auth-types.ts - language added to User, Session, JWT types
  • packages/portal/lib/api.ts - Added api.patch() method
  • packages/portal/app/(auth)/login/page.tsx - useTranslations, browser locale detection, LanguageSwitcher

Decisions Made

  • i18n/locales.ts split from i18n/request.ts: Client Components importing from i18n/request.ts caused a build error because that file imports next/headers (server-only). Creating i18n/locales.ts for shared constants (SUPPORTED_LOCALES, LOCALE_COOKIE, isValidLocale) resolved this — client components import from locales.ts, server config imports from request.ts.
  • api.patch() added to api client: The existing api object had get/post/put/delete but not patch. Added it to keep consistent RBAC-header-aware fetch pattern.
  • Cookie name konstruct_locale: Short, namespaced to avoid conflicts with other cookies on the same domain.

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] Separated server-only imports from client-safe constants

  • Found during: Task 2 (LanguageSwitcher importing from i18n/request.ts)
  • Issue: Build error: i18n/request.ts imports next/headers which is server-only; Client Component language-switcher.tsx was importing from it
  • Fix: Created i18n/locales.ts with shared constants only; updated all client imports to use @/i18n/locales; i18n/request.ts re-exports from locales.ts for server callers
  • Files modified: i18n/locales.ts (created), i18n/request.ts, components/language-switcher.tsx, components/session-sync.tsx, app/(auth)/login/page.tsx
  • Verification: Portal builds cleanly with no errors
  • Committed in: 6be47ae (Task 2 commit)

Total deviations: 1 auto-fixed (1 blocking build error) Impact on plan: Required split of server-only config from shared constants. No scope creep — this is a standard next-intl + App Router pattern.

Issues Encountered

None beyond the deviation documented above.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

  • next-intl infrastructure complete — Plan 03 can begin replacing hardcoded strings with t() calls across all portal components
  • All three languages have complete message files — no translation gaps to block Plan 03
  • Adding a new language requires only a new JSON file in messages/ and adding the locale to SUPPORTED_LOCALES in i18n/locales.ts
  • LanguageSwitcher is live in sidebar and login page — language preference flows: cookie → DB → JWT → session

Self-Check: PASSED

All files confirmed present. All commits confirmed in git history.


Phase: 07-multilanguage Completed: 2026-03-25