- 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
8.2 KiB
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_localecookie ini18n/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
languagefield following the exact trigger="update" pattern already used foractive_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:
- Task 1: Install next-intl, configure i18n infrastructure, create complete message files -
e33eac6(feat) - 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.tspackages/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 translationspackages/portal/messages/pt.json- Complete Brazilian Portuguese translationspackages/portal/components/language-switcher.tsx- EN/ES/PT switcher with isPreAuth proppackages/portal/next.config.ts- Wrapped with createNextIntlPluginpackages/portal/app/layout.tsx- Async Server Component with NextIntlClientProviderpackages/portal/components/nav.tsx- Added LanguageSwitcher in user sectionpackages/portal/components/session-sync.tsx- Added locale cookie sync from sessionpackages/portal/lib/auth.ts- language field in JWT callback and session callbackpackages/portal/lib/auth-types.ts- language added to User, Session, JWT typespackages/portal/lib/api.ts- Added api.patch() methodpackages/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.tscaused a build error because that file importsnext/headers(server-only). Creatingi18n/locales.tsfor shared constants (SUPPORTED_LOCALES, LOCALE_COOKIE, isValidLocale) resolved this — client components import fromlocales.ts, server config imports fromrequest.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.tsimportsnext/headerswhich is server-only; Client Componentlanguage-switcher.tsxwas importing from it - Fix: Created
i18n/locales.tswith shared constants only; updated all client imports to use@/i18n/locales;i18n/request.tsre-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 toSUPPORTED_LOCALESini18n/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