From 528daeb23726d4ffb44de280d6702b3635a0acb8 Mon Sep 17 00:00:00 2001 From: Adolfo Delorenzo Date: Wed, 25 Mar 2026 16:07:50 -0600 Subject: [PATCH] docs(07): create phase 7 multilanguage plan --- .planning/ROADMAP.md | 11 +- .../phases/07-multilanguage/07-01-PLAN.md | 259 +++++++++++++++ .../phases/07-multilanguage/07-02-PLAN.md | 284 +++++++++++++++++ .../phases/07-multilanguage/07-03-PLAN.md | 294 ++++++++++++++++++ .../phases/07-multilanguage/07-04-PLAN.md | 138 ++++++++ 5 files changed, 982 insertions(+), 4 deletions(-) create mode 100644 .planning/phases/07-multilanguage/07-01-PLAN.md create mode 100644 .planning/phases/07-multilanguage/07-02-PLAN.md create mode 100644 .planning/phases/07-multilanguage/07-03-PLAN.md create mode 100644 .planning/phases/07-multilanguage/07-04-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 9e79626..745a098 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -141,7 +141,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 | 4. RBAC | 3/3 | Complete | 2026-03-24 | | 5. Employee Design | 4/4 | Complete | 2026-03-25 | | 6. Web Chat | 3/3 | Complete | 2026-03-25 | -| 7. Multilanguage | 0/0 | Not started | - | +| 7. Multilanguage | 0/4 | Not started | - | --- @@ -160,11 +160,14 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 4. Agent templates, wizard steps, and onboarding flow are all fully translated 5. Error messages, validation text, and system notifications are localized 6. Adding a new language in the future requires only adding translation files, not code changes -**Plans**: 0 plans +**Plans**: 4 plans Plans: -- [ ] TBD (run /gsd:plan-phase 7 to break down) +- [ ] 07-01-PLAN.md — Backend i18n: migration 009 (language column + translations JSONB), system prompt language instruction, localized emails, locale-aware templates API +- [ ] 07-02-PLAN.md — Frontend i18n infrastructure: next-intl setup, complete en/es/pt message files, language switcher, Auth.js JWT language sync +- [ ] 07-03-PLAN.md — Frontend string extraction: replace all hardcoded English strings with useTranslations() calls across all pages and components +- [ ] 07-04-PLAN.md — Human verification: multilanguage testing across all pages, language switcher, AI Employee language response --- *Roadmap created: 2026-03-23* -*Coverage: 25/25 v1 requirements + 6 RBAC requirements + 5 Employee Design requirements + 5 Web Chat requirements mapped* +*Coverage: 25/25 v1 requirements + 6 RBAC requirements + 5 Employee Design requirements + 5 Web Chat requirements + 6 Multilanguage requirements mapped* diff --git a/.planning/phases/07-multilanguage/07-01-PLAN.md b/.planning/phases/07-multilanguage/07-01-PLAN.md new file mode 100644 index 0000000..3920ca1 --- /dev/null +++ b/.planning/phases/07-multilanguage/07-01-PLAN.md @@ -0,0 +1,259 @@ +--- +phase: 07-multilanguage +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - migrations/versions/009_multilanguage.py + - packages/shared/shared/models/tenant.py + - packages/shared/shared/models/auth.py + - packages/shared/shared/prompts/system_prompt_builder.py + - packages/portal/lib/system-prompt-builder.ts + - packages/shared/shared/email.py + - packages/shared/shared/api/templates.py + - packages/shared/shared/api/portal.py + - tests/unit/test_system_prompt_builder.py +autonomous: true +requirements: + - I18N-03 + - I18N-04 + - I18N-05 + - I18N-06 + +must_haves: + truths: + - "AI Employees respond in the same language the user writes in" + - "Agent templates have Spanish and Portuguese translations stored in DB" + - "Invitation emails are sent in the inviting admin's language" + - "portal_users table has a language column defaulting to 'en'" + - "Templates API returns translated fields when locale param is provided" + artifacts: + - path: "migrations/versions/009_multilanguage.py" + provides: "DB migration adding language to portal_users and translations JSONB to agent_templates" + contains: "portal_users" + - path: "packages/shared/shared/prompts/system_prompt_builder.py" + provides: "Language instruction appended to all system prompts" + contains: "LANGUAGE_INSTRUCTION" + - path: "packages/portal/lib/system-prompt-builder.ts" + provides: "TS mirror with language instruction" + contains: "LANGUAGE_INSTRUCTION" + - path: "packages/shared/shared/email.py" + provides: "Localized invitation emails in en/es/pt" + contains: "language" + - path: "packages/shared/shared/api/templates.py" + provides: "Locale-aware template list endpoint" + contains: "locale" + key_links: + - from: "packages/shared/shared/prompts/system_prompt_builder.py" + to: "AI Employee responses" + via: "LANGUAGE_INSTRUCTION appended in build_system_prompt()" + pattern: "LANGUAGE_INSTRUCTION" + - from: "packages/shared/shared/api/templates.py" + to: "agent_templates.translations" + via: "JSONB column merge on locale query param" + pattern: "translations" +--- + + +Backend multilanguage foundation: DB migration, system prompt language instruction, localized invitation emails, and locale-aware templates API. + +Purpose: Provides the backend data layer and AI language behavior that all frontend i18n depends on. Without this, there is no language column to persist, no template translations to display, and no agent language instruction. +Output: Migration 009, updated system prompt builder (Python + TS), localized email sender, locale-aware templates API, unit tests. + + + +@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md +@/home/adelorenzo/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-multilanguage/07-CONTEXT.md +@.planning/phases/07-multilanguage/07-RESEARCH.md + + + + +From packages/shared/shared/models/auth.py: +```python +class PortalUser(Base): + __tablename__ = "portal_users" + id: Mapped[uuid.UUID] + email: Mapped[str] + hashed_password: Mapped[str] + name: Mapped[str] + role: Mapped[str] + created_at: Mapped[datetime] + updated_at: Mapped[datetime] + # NEEDS: language: Mapped[str] = mapped_column(String(10), nullable=False, server_default='en') +``` + +From packages/shared/shared/models/tenant.py (AgentTemplate): +```python +class AgentTemplate(Base): + __tablename__ = "agent_templates" + # Existing columns: id, name, role, description, category, persona, system_prompt, + # model_preference, tool_assignments, escalation_rules, is_active, sort_order, created_at + # NEEDS: translations: Mapped[dict] = mapped_column(JSON, nullable=False, server_default='{}') +``` + +From packages/shared/shared/prompts/system_prompt_builder.py: +```python +AI_TRANSPARENCY_CLAUSE = "When directly asked if you are an AI, always disclose that you are an AI assistant." +def build_system_prompt(name, role, persona="", tool_assignments=None, escalation_rules=None) -> str +``` + +From packages/portal/lib/system-prompt-builder.ts: +```typescript +export interface SystemPromptInput { name: string; role: string; persona?: string; ... } +export function buildSystemPrompt(data: SystemPromptInput): string +``` + +From packages/shared/shared/email.py: +```python +def send_invite_email(to_email: str, invitee_name: str, tenant_name: str, invite_url: str) -> None +# NEEDS: language: str = "en" parameter added +``` + +From packages/shared/shared/api/templates.py: +```python +class TemplateResponse(BaseModel): + id: str; name: str; role: str; description: str; category: str; persona: str; ... + @classmethod + def from_orm(cls, tmpl: AgentTemplate) -> "TemplateResponse" + +@templates_router.get("/templates", response_model=list[TemplateResponse]) +async def list_templates(caller, session) -> list[TemplateResponse] +# NEEDS: locale: str = Query("en") parameter, merge translations before returning +``` + +From migrations/versions/ — latest is 008_web_chat.py, so next is 009. + + + + + + + Task 1: DB migration 009 + ORM updates + system prompt language instruction + + migrations/versions/009_multilanguage.py + packages/shared/shared/models/auth.py + packages/shared/shared/models/tenant.py + packages/shared/shared/prompts/system_prompt_builder.py + packages/portal/lib/system-prompt-builder.ts + tests/unit/test_system_prompt_builder.py + + + - Test: build_system_prompt("Mara", "Support Rep") output contains LANGUAGE_INSTRUCTION string + - Test: build_system_prompt with full args (persona, tools, escalation) contains LANGUAGE_INSTRUCTION + - Test: build_system_prompt with minimal args (name, role only) contains LANGUAGE_INSTRUCTION + - Test: LANGUAGE_INSTRUCTION appears after identity section and before AI_TRANSPARENCY_CLAUSE + + + 1. Create migration 009_multilanguage.py: + - Add `language` column (String(10), NOT NULL, server_default='en') to portal_users + - Add `translations` column (JSON, NOT NULL, server_default='{}') to agent_templates + - Backfill translations for all 7 existing seed templates with Spanish (es) and Portuguese (pt) translations for name, description, and persona fields. Use proper native business terminology — not literal machine translations. Each template gets a translations JSON object like: {"es": {"name": "...", "description": "...", "persona": "..."}, "pt": {"name": "...", "description": "...", "persona": "..."}} + - downgrade: drop both columns + + 2. Update ORM models: + - PortalUser: add `language: Mapped[str] = mapped_column(String(10), nullable=False, server_default='en')` + - AgentTemplate: add `translations: Mapped[dict] = mapped_column(JSON, nullable=False, server_default='{}')` + + 3. Add LANGUAGE_INSTRUCTION to system_prompt_builder.py: + ```python + LANGUAGE_INSTRUCTION = ( + "Detect the language of each user message and respond in that same language. " + "You support English, Spanish, and Portuguese." + ) + ``` + Append to sections list BEFORE AI_TRANSPARENCY_CLAUSE (transparency clause remains last). + + 4. Add LANGUAGE_INSTRUCTION to system-prompt-builder.ts (TS mirror): + ```typescript + const LANGUAGE_INSTRUCTION = "Detect the language of each user message and respond in that same language. You support English, Spanish, and Portuguese."; + ``` + Append before the AI transparency clause line. + + 5. Extend tests/unit/test_system_prompt_builder.py with TestLanguageInstruction class: + - test_language_instruction_present_in_default_prompt + - test_language_instruction_present_with_full_args + - test_language_instruction_before_transparency_clause + + + cd /home/adelorenzo/repos/konstruct && python -m pytest tests/unit/test_system_prompt_builder.py -x -v + + + - Migration 009 creates language column on portal_users and translations JSONB on agent_templates with es+pt seed data + - LANGUAGE_INSTRUCTION appears in all system prompts (Python and TS) + - All existing + new system prompt tests pass + + + + + Task 2: Localized invitation emails + locale-aware templates API + language preference endpoint + + packages/shared/shared/email.py + packages/shared/shared/api/templates.py + packages/shared/shared/api/portal.py + + + 1. Update send_invite_email() in email.py: + - Add `language: str = "en"` parameter + - Create localized subject lines dict: {"en": "You've been invited...", "es": "Has sido invitado...", "pt": "Voce foi convidado..."} + - Create localized text_body and html_body templates for all 3 languages + - Select the correct template based on language param, fallback to "en" + - Update the invitations API endpoint that calls send_invite_email to pass the inviter's language (read from portal_users.language or default "en") + + 2. Update templates API (templates.py): + - Add `locale: str = Query("en")` parameter to list_templates() and get_template() + - In TemplateResponse.from_orm(), add a `locale` parameter + - When locale != "en" and tmpl.translations has the locale key, merge translated name/description/persona over the English defaults before returning + - Keep English as the base — translations overlay, never replace the stored English values in DB + + 3. Add language preference PATCH endpoint in portal.py: + - PATCH /api/portal/users/me/language — accepts {"language": "es"} body + - Validates language is in ["en", "es", "pt"] + - Updates portal_users.language for the current user + - Returns {"language": "es"} on success + - Guard: any authenticated user (get_portal_caller) + + 4. Update the verify auth endpoint (/api/portal/auth/verify) to include `language` in its response so Auth.js JWT can carry it. + + + cd /home/adelorenzo/repos/konstruct && python -m pytest tests/unit -x -q + + + - send_invite_email() accepts language param and sends localized emails in en/es/pt + - GET /api/portal/templates?locale=es returns Spanish-translated template fields + - PATCH /api/portal/users/me/language persists language preference + - /api/portal/auth/verify response includes user's language field + + + + + + +- All existing unit tests pass: `pytest tests/unit -x -q` +- Migration 009 is syntactically valid (imports, upgrade/downgrade functions present) +- system_prompt_builder.py contains LANGUAGE_INSTRUCTION +- system-prompt-builder.ts contains LANGUAGE_INSTRUCTION +- email.py send_invite_email has language parameter +- templates.py list_templates has locale parameter + + + +- Migration 009 adds language to portal_users and translations JSONB to agent_templates +- All 7 seed templates have es+pt translations backfilled +- AI Employees will respond in the user's language via system prompt instruction +- Templates API merges translations by locale +- Language preference PATCH endpoint works +- All unit tests pass + + + +After completion, create `.planning/phases/07-multilanguage/07-01-SUMMARY.md` + diff --git a/.planning/phases/07-multilanguage/07-02-PLAN.md b/.planning/phases/07-multilanguage/07-02-PLAN.md new file mode 100644 index 0000000..bf2305b --- /dev/null +++ b/.planning/phases/07-multilanguage/07-02-PLAN.md @@ -0,0 +1,284 @@ +--- +phase: 07-multilanguage +plan: 02 +type: execute +wave: 1 +depends_on: [] +files_modified: + - packages/portal/package.json + - packages/portal/next.config.ts + - packages/portal/i18n/request.ts + - packages/portal/messages/en.json + - packages/portal/messages/es.json + - packages/portal/messages/pt.json + - packages/portal/app/layout.tsx + - packages/portal/components/language-switcher.tsx + - packages/portal/components/nav.tsx + - packages/portal/lib/auth.ts + - packages/portal/components/session-sync.tsx +autonomous: true +requirements: + - I18N-01 + - I18N-02 + - I18N-06 + +must_haves: + truths: + - "next-intl is installed and configured with cookie-based locale (no URL routing)" + - "NextIntlClientProvider wraps the app in root layout.tsx" + - "Language switcher is visible in the sidebar near the user avatar" + - "Language selection persists via cookie (pre-auth) and DB (post-auth)" + - "Login page detects browser locale and shows a language switcher" + - "Adding a new language requires only a new JSON file in messages/" + artifacts: + - path: "packages/portal/i18n/request.ts" + provides: "next-intl server config reading locale from cookie" + contains: "getRequestConfig" + - path: "packages/portal/messages/en.json" + provides: "English translation source of truth" + contains: "nav" + - path: "packages/portal/messages/es.json" + provides: "Spanish translations" + contains: "nav" + - path: "packages/portal/messages/pt.json" + provides: "Portuguese translations" + contains: "nav" + - path: "packages/portal/components/language-switcher.tsx" + provides: "Language picker component with EN/ES/PT options" + contains: "LanguageSwitcher" + key_links: + - from: "packages/portal/app/layout.tsx" + to: "packages/portal/i18n/request.ts" + via: "NextIntlClientProvider reads locale + messages from server config" + pattern: "NextIntlClientProvider" + - from: "packages/portal/components/language-switcher.tsx" + to: "/api/portal/users/me/language" + via: "PATCH request to persist language preference" + pattern: "fetch.*language" + - from: "packages/portal/components/nav.tsx" + to: "packages/portal/components/language-switcher.tsx" + via: "LanguageSwitcher rendered in user section of sidebar" + pattern: "LanguageSwitcher" +--- + + +Frontend i18n infrastructure: install next-intl, create message files with complete translation keys for all portal pages, configure root layout provider, build language switcher, and integrate with Auth.js JWT for language persistence. + +Purpose: Establishes the i18n framework so all portal components can use `useTranslations()`. Creates the complete en/es/pt message files with all translation keys for every page and component. This plan does the infrastructure setup AND the translation file authoring, while Plan 03 does the actual string extraction (replacing hardcoded strings with `t()` calls). +Output: Working next-intl setup, complete message files in 3 languages, language switcher in sidebar and login page. + + + +@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md +@/home/adelorenzo/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-multilanguage/07-CONTEXT.md +@.planning/phases/07-multilanguage/07-RESEARCH.md + + + +From packages/portal/app/layout.tsx: +```typescript +// Currently: static html lang="en", no i18n provider +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} +``` + + +From packages/portal/components/nav.tsx: +```typescript +'use client'; +// navItems array has hardcoded labels: "Dashboard", "Employees", "Chat", etc. +// User section at bottom with sign out button +export function Nav() { ... } +``` + + +From packages/portal/lib/auth.ts: +```typescript +// JWT callback with trigger="update" pattern used for active_tenant_id +// Same pattern needed for language field +``` + + +From packages/portal/next.config.ts: +```typescript +const nextConfig: NextConfig = { output: "standalone" }; +export default nextConfig; +// MUST become: export default withNextIntl(nextConfig); +``` + + +From packages/portal/app/(auth)/login/page.tsx — Client Component with email/password form + + +From packages/portal/components/session-sync.tsx + + + + + + + Task 1: Install next-intl, configure i18n infrastructure, create complete message files + + packages/portal/package.json + packages/portal/next.config.ts + packages/portal/i18n/request.ts + packages/portal/messages/en.json + packages/portal/messages/es.json + packages/portal/messages/pt.json + packages/portal/app/layout.tsx + + + 1. Install dependencies in packages/portal/: + ```bash + cd packages/portal && npm install next-intl @formatjs/intl-localematcher negotiator && npm install --save-dev @types/negotiator + ``` + + 2. Create i18n/request.ts (next-intl server config WITHOUT URL routing): + - Define SUPPORTED_LOCALES = ['en', 'es', 'pt'] as const + - Export type Locale, isValidLocale helper, LOCALE_COOKIE constant ('konstruct_locale') + - getRequestConfig reads locale from cookie (LOCALE_COOKIE), falls back to 'en' + - Dynamic import of messages/{locale}.json + - Follow Pattern 1 from RESEARCH.md exactly + + 3. Update next.config.ts: + - Import createNextIntlPlugin from 'next-intl/plugin' + - Wrap config: const withNextIntl = createNextIntlPlugin('./i18n/request.ts') + - export default withNextIntl({ output: 'standalone' }) + + 4. Create complete messages/en.json with ALL translation keys for every page and component: + - nav: dashboard, employees, chat, usage, billing, apiKeys, users, platform, signOut + - login: title, subtitle, emailLabel, passwordLabel, submitButton, invalidCredentials, signingIn + - dashboard: title, welcome, agentCount, tenantCount, recentActivity, noActivity + - agents: pageTitle, newEmployee, noAgents, noAgentsDescription, createFirst, active, inactive, editButton, deleteButton, confirmDelete, agent fields (name, role, persona, systemPrompt, modelPreference, tools, escalation) + - agentDesigner: title, fields (jobDescription, statementOfWork, persona, systemPrompt, toolAssignments, escalationRules), saveButton, addTool, addRule, condition, action + - agentNew: title, subtitle, options (template, wizard, advanced), descriptions for each + - templates: title, subtitle, deploy, deploying, deployed, recommended, noTemplates, category labels + - wizard: steps (role, persona, tools, channels, escalation, review), labels for each field, next, back, deploy, deployingAgent + - onboarding: title, steps (connectChannel, configureAgent, testMessage), descriptions, buttons, completion message + - chat: title, newConversation, noConversations, noMessages, typeMessage, send, selectAgent, conversations + - billing: title, currentPlan, subscribe, manage, cancelSubscription, upgrade, invoiceHistory + - usage: title, selectTenant, tokenUsage, costBreakdown, timeRange, day, week, month, noBudget + - apiKeys: title, addKey, provider, keyHint, deleteKey, confirmDelete, noKeys + - users: title, inviteUser, name, email, role, status, actions, pending, accepted, revokeInvite + - adminUsers: title, allUsers, platformUsers + - tenants: title, newTenant, name, slug, plan, actions, editTenant, deleteTenant, confirmDelete, noTenants + - tenantForm: nameLabel, slugLabel, planLabel, createButton, updateButton, creating, updating + - common: loading, error, save, cancel, delete, confirm, search, noResults, retry, back, close + - impersonation: banner text, stopButton + - tenantSwitcher: selectTenant, allTenants, currentTenant + - validation: required, invalidEmail, minLength, maxLength, invalidFormat + - language: switcherLabel, en, es, pt + + 5. Create messages/es.json — complete Spanish translation of ALL keys from en.json. Use proper Latin American Spanish business terminology. Not literal translations — natural phrasing. Example: "Employees" -> "Empleados", "Dashboard" -> "Panel", "Sign out" -> "Cerrar sesion", "AI Workforce" -> "Fuerza laboral IA". + + 6. Create messages/pt.json — complete Brazilian Portuguese translation of ALL keys from en.json. Use proper Brazilian Portuguese business terminology. Example: "Employees" -> "Funcionarios", "Dashboard" -> "Painel", "Sign out" -> "Sair", "AI Workforce" -> "Forca de trabalho IA". + + 7. Update app/layout.tsx: + - Keep as Server Component (NO 'use client') + - Import NextIntlClientProvider from 'next-intl' + - Import getLocale, getMessages from 'next-intl/server' + - Make function async + - Call const locale = await getLocale(); const messages = await getMessages(); + - Set html lang={locale} (dynamic, not hardcoded "en") + - Wrap body children with + - Keep all existing font and class logic unchanged + + + cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20 + + + - next-intl installed and configured + - i18n/request.ts reads locale from cookie + - next.config.ts wrapped with withNextIntl + - Complete en.json, es.json, pt.json message files with keys for every page/component + - Root layout wraps app with NextIntlClientProvider + - Portal builds successfully + + + + + Task 2: Language switcher component + Auth.js JWT language sync + login page locale detection + + packages/portal/components/language-switcher.tsx + packages/portal/components/nav.tsx + packages/portal/lib/auth.ts + packages/portal/lib/auth-types.ts + packages/portal/components/session-sync.tsx + packages/portal/app/(auth)/login/page.tsx + + + 1. Create components/language-switcher.tsx: + - 'use client' component + - Three clickable buttons: EN / ES / PT (compact, inline) + - Current locale highlighted (derived from cookie or session) + - On click: (a) set document.cookie with konstruct_locale={locale}, path=/, max-age=31536000; (b) if authenticated, PATCH /api/portal/users/me/language with the locale; (c) call update({ language: locale }) on Auth.js session; (d) router.refresh() to re-render with new locale + - Style: compact row of 3 buttons, fits in sidebar user section. Use sidebar color tokens. Active locale has subtle highlight. + - Accept optional `isPreAuth` prop — when true, skip the DB PATCH and session update (for login page) + + 2. Add LanguageSwitcher to nav.tsx: + - Import and render between the user info section and the sign-out button + - Keep existing nav structure and styling intact + + 3. Update Auth.js config in lib/auth.ts: + - In the JWT callback: read user.language from the verify endpoint response and add to token + - Handle trigger="update" case for language: if (trigger === "update" && session?.language) token.language = session.language + - In the session callback: expose token.language on session.user.language + - Follow the exact same pattern already used for active_tenant_id + + 4. Update auth-types.ts if needed to include language in the session/token types. + + 5. Update session-sync.tsx: + - After login, sync the locale cookie from session.user.language (if the cookie differs from the session value, update the cookie so i18n/request.ts reads the DB-authoritative value) + + 6. Update login page (app/(auth)/login/page.tsx): + - On mount (useEffect), detect browser locale via navigator.language.slice(0, 2), check if supported ['en', 'es', 'pt'], set konstruct_locale cookie if no cookie exists yet + - Add near the form (below the sign-in button or in the page header) + - Use useTranslations('login') for all login form strings (title, labels, button, error message) + + + cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20 + + + - LanguageSwitcher component renders EN/ES/PT buttons with active highlight + - Sidebar shows language switcher near user avatar + - Changing language updates cookie + DB + JWT + triggers re-render + - Login page detects browser locale and shows language switcher + - Login form strings use useTranslations('login') + - Portal builds successfully + + + + + + +- Portal builds: `cd packages/portal && npx next build` +- next-intl configured: i18n/request.ts exists, next.config.ts uses withNextIntl +- Message files exist: en.json, es.json, pt.json all have matching key structures +- LanguageSwitcher component exists and is rendered in Nav +- Login page uses useTranslations + + + +- next-intl v4 installed and configured without URL-based routing +- Complete en/es/pt message files covering all pages and components +- Language switcher in sidebar (post-auth) and login page (pre-auth) +- Language preference persists via cookie + DB + JWT +- Browser locale auto-detected on first visit +- Portal builds without errors + + + +After completion, create `.planning/phases/07-multilanguage/07-02-SUMMARY.md` + diff --git a/.planning/phases/07-multilanguage/07-03-PLAN.md b/.planning/phases/07-multilanguage/07-03-PLAN.md new file mode 100644 index 0000000..9ed81d4 --- /dev/null +++ b/.planning/phases/07-multilanguage/07-03-PLAN.md @@ -0,0 +1,294 @@ +--- +phase: 07-multilanguage +plan: 03 +type: execute +wave: 2 +depends_on: + - "07-01" + - "07-02" +files_modified: + - packages/portal/components/nav.tsx + - packages/portal/components/agent-designer.tsx + - packages/portal/components/billing-status.tsx + - packages/portal/components/budget-alert-badge.tsx + - packages/portal/components/chat-message.tsx + - packages/portal/components/chat-sidebar.tsx + - packages/portal/components/chat-window.tsx + - packages/portal/components/employee-wizard.tsx + - packages/portal/components/impersonation-banner.tsx + - packages/portal/components/message-volume-chart.tsx + - packages/portal/components/onboarding-stepper.tsx + - packages/portal/components/provider-cost-chart.tsx + - packages/portal/components/subscription-card.tsx + - packages/portal/components/template-gallery.tsx + - packages/portal/components/tenant-form.tsx + - packages/portal/components/tenant-switcher.tsx + - packages/portal/app/(dashboard)/dashboard/page.tsx + - packages/portal/app/(dashboard)/agents/page.tsx + - packages/portal/app/(dashboard)/agents/[id]/page.tsx + - packages/portal/app/(dashboard)/agents/new/page.tsx + - packages/portal/app/(dashboard)/agents/new/templates/page.tsx + - packages/portal/app/(dashboard)/agents/new/wizard/page.tsx + - packages/portal/app/(dashboard)/agents/new/advanced/page.tsx + - packages/portal/app/(dashboard)/chat/page.tsx + - packages/portal/app/(dashboard)/billing/page.tsx + - packages/portal/app/(dashboard)/usage/page.tsx + - packages/portal/app/(dashboard)/usage/[tenantId]/page.tsx + - packages/portal/app/(dashboard)/settings/api-keys/page.tsx + - packages/portal/app/(dashboard)/users/page.tsx + - packages/portal/app/(dashboard)/admin/users/page.tsx + - packages/portal/app/(dashboard)/tenants/page.tsx + - packages/portal/app/(dashboard)/tenants/new/page.tsx + - packages/portal/app/(dashboard)/tenants/[id]/page.tsx + - packages/portal/app/(dashboard)/onboarding/page.tsx + - packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx + - packages/portal/app/(dashboard)/onboarding/steps/configure-agent.tsx + - packages/portal/app/(dashboard)/onboarding/steps/test-message.tsx + - packages/portal/app/invite/[token]/page.tsx + - packages/portal/components/wizard-steps/step-role.tsx + - packages/portal/components/wizard-steps/step-persona.tsx + - packages/portal/components/wizard-steps/step-tools.tsx + - packages/portal/components/wizard-steps/step-channels.tsx + - packages/portal/components/wizard-steps/step-escalation.tsx + - packages/portal/components/wizard-steps/step-review.tsx +autonomous: true +requirements: + - I18N-01 + - I18N-04 + - I18N-05 + +must_haves: + truths: + - "Every user-visible string in the portal uses useTranslations() instead of hardcoded English" + - "Navigation labels render in the selected language" + - "Agent designer, wizard, and template gallery are fully translated" + - "Onboarding flow steps are fully translated" + - "Error messages and validation text render in the selected language" + - "Chat UI, billing, usage, and all other pages are translated" + artifacts: + - path: "packages/portal/components/nav.tsx" + provides: "Translated navigation labels" + contains: "useTranslations" + - path: "packages/portal/components/employee-wizard.tsx" + provides: "Translated wizard UI" + contains: "useTranslations" + - path: "packages/portal/components/template-gallery.tsx" + provides: "Translated template cards with locale-aware API calls" + contains: "useTranslations" + - path: "packages/portal/app/(dashboard)/chat/page.tsx" + provides: "Translated chat interface" + contains: "useTranslations" + key_links: + - from: "All portal components" + to: "packages/portal/messages/{locale}.json" + via: "useTranslations() hook reading from NextIntlClientProvider context" + pattern: "useTranslations" + - from: "packages/portal/components/template-gallery.tsx" + to: "/api/portal/templates?locale=" + via: "Locale query param passed to templates API" + pattern: "locale" +--- + + +Extract all hardcoded English strings from every portal page and component, replacing them with `useTranslations()` calls that read from the en/es/pt message files created in Plan 02. + +Purpose: This is the core localization work. Every user-visible string in every TSX file must be replaced with a `t('key')` call. Without this, the message files and i18n infrastructure from Plan 02 have no effect. +Output: All 40+ portal TSX files updated with useTranslations() calls. Zero hardcoded English strings remain in user-visible UI. + + + +@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md +@/home/adelorenzo/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-multilanguage/07-CONTEXT.md +@.planning/phases/07-multilanguage/07-RESEARCH.md +@.planning/phases/07-multilanguage/07-01-SUMMARY.md +@.planning/phases/07-multilanguage/07-02-SUMMARY.md + + + +```typescript +// In Client Components ('use client'): +import { useTranslations } from 'next-intl'; +const t = useTranslations('namespace'); +// Then:

{t('title')}

+ +// In Server Components: +import { useTranslations } from 'next-intl'; +const t = useTranslations('namespace'); +// Same API — works in both + +// Message file structure (from Plan 02): +// messages/en.json has nested keys: nav.dashboard, agents.pageTitle, etc. +// useTranslations('nav') gives t('dashboard') -> "Dashboard" +``` + + +```typescript +// Plan 01 adds ?locale= param to templates API +// Template gallery must pass current locale when fetching: +// GET /api/portal/templates?locale=es +``` +
+
+ + + + + Task 1: Extract strings from all components (nav, sidebar, forms, wizards, chat) + + packages/portal/components/nav.tsx + packages/portal/components/agent-designer.tsx + packages/portal/components/billing-status.tsx + packages/portal/components/budget-alert-badge.tsx + packages/portal/components/chat-message.tsx + packages/portal/components/chat-sidebar.tsx + packages/portal/components/chat-window.tsx + packages/portal/components/employee-wizard.tsx + packages/portal/components/impersonation-banner.tsx + packages/portal/components/message-volume-chart.tsx + packages/portal/components/onboarding-stepper.tsx + packages/portal/components/provider-cost-chart.tsx + packages/portal/components/subscription-card.tsx + packages/portal/components/template-gallery.tsx + packages/portal/components/tenant-form.tsx + packages/portal/components/tenant-switcher.tsx + packages/portal/components/wizard-steps/step-role.tsx + packages/portal/components/wizard-steps/step-persona.tsx + packages/portal/components/wizard-steps/step-tools.tsx + packages/portal/components/wizard-steps/step-channels.tsx + packages/portal/components/wizard-steps/step-escalation.tsx + packages/portal/components/wizard-steps/step-review.tsx + + + For EVERY component listed, apply this transformation: + + 1. Add `import { useTranslations } from 'next-intl';` (for 'use client' components) + 2. At the top of the component function, add `const t = useTranslations('namespace');` where namespace matches the message file key group (e.g., 'nav' for nav.tsx, 'wizard' for wizard steps, 'chat' for chat components) + 3. Replace every hardcoded English string with `t('keyName')` — use the exact keys from the en.json message file created in Plan 02 + 4. For strings with interpolation (e.g., "Welcome, {name}"), use `t('welcome', { name })` and ensure the message file uses ICU format: "Welcome, {name}" + 5. For nav.tsx specifically: replace the hardcoded label strings in the navItems array with t() calls. Since navItems is defined outside the component, move the labels inside the component function or use a computed items pattern. + + Specific component notes: + - nav.tsx: navItems labels ("Dashboard", "Employees", etc.) -> t('dashboard'), t('employees'), etc. "Sign out" -> t('signOut') + - template-gallery.tsx: Pass locale to templates API call: fetch(`/api/portal/templates?locale=${currentLocale}`). Get current locale from cookie or useLocale() from next-intl. + - employee-wizard.tsx: All step labels, button text, form labels + - onboarding-stepper.tsx: Step titles and descriptions + - agent-designer.tsx: Field labels, button text, placeholders + - chat-window.tsx: "Type a message", "Send", placeholder text + - chat-sidebar.tsx: "New Conversation", "No conversations" + - billing-status.tsx: Status labels, button text + - subscription-card.tsx: Plan names, subscribe/manage buttons + - tenant-form.tsx: Form labels, submit buttons + - tenant-switcher.tsx: "Select tenant", "All tenants" + - impersonation-banner.tsx: Banner text, stop button + - budget-alert-badge.tsx: "No limit set", budget alert text + + Do NOT translate: + - Component prop names or internal variable names + - CSS class strings + - API endpoint URLs + - Console.log messages + - aria-label values that are already descriptive (but DO translate user-visible aria-labels) + + + cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20 + + + - All 22 component files use useTranslations() for every user-visible string + - No hardcoded English strings remain in component files (except technical strings like URLs, class names) + - Template gallery passes locale to API + - Portal builds successfully + + + + + Task 2: Extract strings from all page files (dashboard, agents, chat, billing, usage, etc.) + + packages/portal/app/(dashboard)/dashboard/page.tsx + packages/portal/app/(dashboard)/agents/page.tsx + packages/portal/app/(dashboard)/agents/[id]/page.tsx + packages/portal/app/(dashboard)/agents/new/page.tsx + packages/portal/app/(dashboard)/agents/new/templates/page.tsx + packages/portal/app/(dashboard)/agents/new/wizard/page.tsx + packages/portal/app/(dashboard)/agents/new/advanced/page.tsx + packages/portal/app/(dashboard)/chat/page.tsx + packages/portal/app/(dashboard)/billing/page.tsx + packages/portal/app/(dashboard)/usage/page.tsx + packages/portal/app/(dashboard)/usage/[tenantId]/page.tsx + packages/portal/app/(dashboard)/settings/api-keys/page.tsx + packages/portal/app/(dashboard)/users/page.tsx + packages/portal/app/(dashboard)/admin/users/page.tsx + packages/portal/app/(dashboard)/tenants/page.tsx + packages/portal/app/(dashboard)/tenants/new/page.tsx + packages/portal/app/(dashboard)/tenants/[id]/page.tsx + packages/portal/app/(dashboard)/onboarding/page.tsx + packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx + packages/portal/app/(dashboard)/onboarding/steps/configure-agent.tsx + packages/portal/app/(dashboard)/onboarding/steps/test-message.tsx + packages/portal/app/invite/[token]/page.tsx + + + For EVERY page file listed, apply the same transformation pattern as Task 1: + + 1. Add `import { useTranslations } from 'next-intl';` + 2. Add `const t = useTranslations('namespace');` using the appropriate namespace + 3. Replace all hardcoded English strings with `t('key')` calls + + Page-specific notes: + - dashboard/page.tsx: "Dashboard", "Welcome back", stats labels -> t('dashboard.*') + - agents/page.tsx: "AI Employees", "New Employee", empty state text -> t('agents.*') + - agents/[id]/page.tsx: Agent detail labels, edit/delete buttons -> t('agentDesigner.*') + - agents/new/page.tsx: Three creation options text -> t('agentNew.*') + - agents/new/templates/page.tsx: Template gallery page title -> t('templates.*') + - agents/new/wizard/page.tsx: Wizard page wrapper -> t('wizard.*') + - agents/new/advanced/page.tsx: Advanced mode labels -> t('agentDesigner.*') + - chat/page.tsx: Chat page labels -> t('chat.*') + - billing/page.tsx: Billing page labels, plan info -> t('billing.*') + - usage/page.tsx & usage/[tenantId]/page.tsx: Usage labels, chart titles -> t('usage.*') + - settings/api-keys/page.tsx: API key management labels -> t('apiKeys.*') + - users/page.tsx: User management, invite labels -> t('users.*') + - admin/users/page.tsx: Platform admin user list -> t('adminUsers.*') + - tenants pages: Tenant management labels -> t('tenants.*') + - onboarding pages + steps: All onboarding UI -> t('onboarding.*') + - invite/[token]/page.tsx: Invitation acceptance page -> t('invite.*') (add invite namespace to message files if not already present) + + After all string extraction is complete, do a final review of messages/en.json, messages/es.json, and messages/pt.json to ensure every key used by t() exists in all three files. Add any missing keys discovered during extraction. + + IMPORTANT: If any page is a Server Component (no 'use client'), useTranslations still works the same way in next-intl v4 — it reads from the server context set up by i18n/request.ts. No change needed. + + + cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20 + + + - All 22 page files use useTranslations() for every user-visible string + - No hardcoded English strings remain in any page file + - All translation keys used in t() calls exist in en.json, es.json, and pt.json + - Portal builds successfully with zero errors + + + + + + +- Portal builds: `cd packages/portal && npx next build` +- Grep for remaining hardcoded strings: search for obvious English strings that should be translated +- All message file keys are consistent across en.json, es.json, pt.json + + + +- Every user-visible string in the portal uses useTranslations() +- All three message files (en/es/pt) have matching key structures +- Template gallery passes locale to API for translated template content +- Portal builds without errors +- Zero hardcoded English strings remain in user-facing UI + + + +After completion, create `.planning/phases/07-multilanguage/07-03-SUMMARY.md` + diff --git a/.planning/phases/07-multilanguage/07-04-PLAN.md b/.planning/phases/07-multilanguage/07-04-PLAN.md new file mode 100644 index 0000000..33d76d0 --- /dev/null +++ b/.planning/phases/07-multilanguage/07-04-PLAN.md @@ -0,0 +1,138 @@ +--- +phase: 07-multilanguage +plan: 04 +type: execute +wave: 3 +depends_on: + - "07-03" +files_modified: [] +autonomous: false +requirements: + - I18N-01 + - I18N-02 + - I18N-03 + - I18N-04 + - I18N-05 + - I18N-06 + +must_haves: + truths: + - "Portal renders correctly in English, Spanish, and Portuguese" + - "Language switcher works in sidebar and on login page" + - "Language preference persists across sessions" + - "AI Employee responds in the user's language" + - "Agent templates display in the selected language" + - "Error messages and validation text are localized" + artifacts: [] + key_links: [] +--- + + +Human verification of the complete multilanguage implementation across all portal pages and AI Employee behavior. + +Purpose: Verify that translations look correct, the language switcher works end-to-end, and AI Employees respond in the correct language. Automated builds confirm code compiles but cannot verify translation quality or visual correctness. +Output: Confirmation that all I18N requirements are met. + + + +@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md +@/home/adelorenzo/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/phases/07-multilanguage/07-CONTEXT.md +@.planning/phases/07-multilanguage/07-01-SUMMARY.md +@.planning/phases/07-multilanguage/07-02-SUMMARY.md +@.planning/phases/07-multilanguage/07-03-SUMMARY.md + + + + + + Task 1: Verify complete multilanguage implementation + + + Present the user with the verification checklist below. No code changes needed — this is a human review checkpoint. + + + cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -5 + + User confirms all I18N requirements are met by typing "approved" + + Complete multilanguage support across the portal and AI Employee behavior: + - All portal pages and components localized in English, Spanish, and Portuguese + - Language switcher in sidebar (post-auth) and login page (pre-auth) + - Language preference persisted to DB and JWT + - Browser locale auto-detection on first visit + - AI Employees respond in the user's language via system prompt instruction + - Agent templates display translated names/descriptions in the selected language + - Invitation emails sent in the inviting admin's language + + + Start the dev environment and open the portal: + + **1. Login page language detection (I18N-02)** + - Open the login page in a fresh browser/incognito + - If your browser is set to Spanish, the form should show Spanish labels + - Click the ES / PT language buttons to verify the form changes language + - Log in + + **2. Language switcher persistence (I18N-02)** + - In the sidebar, find the EN / ES / PT switcher near your avatar + - Click "ES" — all navigation labels and page content should switch to Spanish + - Navigate to different pages (Dashboard, Employees, Chat, Billing) — all should be in Spanish + - Click "PT" — verify Portuguese translations appear + - Log out and log back in — verify the language preference persists (should still be Portuguese) + + **3. Portal pages in Spanish (I18N-01, I18N-04, I18N-05)** + - Switch to Spanish and visit each major page: + - Dashboard: verify title, stats labels, welcome message + - Employees: verify page title, "New Employee" button, empty state text + - New Employee options: verify template/wizard/advanced descriptions + - Template gallery: verify template names and descriptions are in Spanish + - Employee wizard: verify all 5 step labels and form fields + - Chat: verify sidebar, message input placeholder, conversation labels + - Billing: verify plan names, button labels, status badges + - Usage: verify chart labels, time range options, budget text + - API Keys: verify page title, add/delete labels + - Users: verify invite form labels, role names, status badges + - Onboarding: verify all 3 step titles and descriptions + - Trigger a validation error (e.g., submit an empty form) — verify error message is in Spanish + + **4. Portal pages in Portuguese (I18N-01)** + - Switch to Portuguese and spot-check 3-4 pages for correct translations + + **5. AI Employee language response (I18N-03)** + - Open the Chat page + - Start a conversation with any agent + - Send a message in Spanish (e.g., "Hola, como puedo ayudarte?") + - Verify the agent responds in Spanish + - Send a message in Portuguese (e.g., "Ola, como posso ajudar?") + - Verify the agent responds in Portuguese + - Send a message in English — verify English response + + **6. Extensibility check (I18N-06)** + - Verify that messages/en.json, messages/es.json, and messages/pt.json exist + - Confirm the file structure means adding a 4th language is just a new JSON file + + Type "approved" if all checks pass, or describe any issues found + + + + + +Human verification covers all 6 I18N requirements through manual testing in the browser. + + + +- All 6 verification steps pass +- No untranslated strings visible when using Spanish or Portuguese +- Language preference persists across sessions +- AI Employee responds in the correct language + + + +After completion, create `.planning/phases/07-multilanguage/07-04-SUMMARY.md` +