Files
konstruct/.planning/phases/07-multilanguage/07-VERIFICATION.md

16 KiB
Raw Permalink Blame History

phase, verified, status, score, re_verification, human_verification
phase verified status score re_verification human_verification
07-multilanguage 2026-03-25T23:30:00Z human_needed 11/11 automated must-haves verified false
test expected why_human
Language switcher changes portal language end-to-end Clicking ES in the sidebar switches all nav labels, page titles, and content to Spanish with no untranslated strings visible Cannot verify visual rendering, translation quality, or that all 40+ files produce correct output in a browser
test expected why_human
Language preference persists across sessions After selecting PT, logging out, and logging back in, the portal still displays in Portuguese Requires real Auth.js JWT round-trip and DB persistence through login flow
test expected why_human
Login page browser locale detection On first visit with no cookie, a browser configured for Spanish automatically shows the login form in Spanish Requires a real browser with locale set; cannot simulate navigator.language in static analysis
test expected why_human
AI Employee responds in the user's language Sending a Spanish message to an agent results in a Spanish reply; sending Portuguese yields Portuguese; English yields English Requires live LLM inference — cannot verify LANGUAGE_INSTRUCTION produces correct multilingual behavior without a running agent
test expected why_human
Agent templates display translated content in template gallery When language is set to ES, the template gallery shows Spanish template names and descriptions (e.g., 'Representante de Soporte al Cliente' instead of 'Customer Support Rep') Requires running portal with real DB data and translations JSONB populated by migration 009
test expected why_human
Invitation emails sent in correct language When an admin whose language preference is 'es' invites a user, the invitation email arrives with a Spanish subject line and body Requires SMTP infrastructure and real email delivery to verify; cannot simulate email sending in static analysis

Phase 7: Multilanguage Verification Report

Phase Goal: The entire platform supports English, Spanish, and Portuguese — the portal UI is fully localized with a language switcher, and AI Employees respond in the user's language Verified: 2026-03-25T23:30:00Z Status: human_needed Re-verification: No — initial verification

Note on Git Structure

packages/portal is a separate nested git repository. Commits e33eac6, 6be47ae, 20f4c5b, and c499029 claimed in Plan 02 and 03 summaries all exist and are verified in packages/portal's git history. The parent repository sees packages/portal as a submodule reference, not individual file commits. All 6 task commits across all 4 plans are real and traceable.

Goal Achievement

Observable Truths

# Truth Status Evidence
1 AI Employees respond in the same language the user writes in VERIFIED LANGUAGE_INSTRUCTION constant present in system_prompt_builder.py (line 2023), appended before AI_TRANSPARENCY_CLAUSE in build_system_prompt() (line 74). TS mirror in system-prompt-builder.ts (line 1819, 46).
2 Agent templates have Spanish and Portuguese translations stored in DB VERIFIED Migration 009_multilanguage.py adds translations JSONB column to agent_templates with server_default='{}' and backfills all 7 seed templates with native-quality es+pt translations (lines 37278). AgentTemplate ORM updated (line 256).
3 Invitation emails are sent in the inviting admin's language VERIFIED email.py has _SUPPORTED_LANGUAGES, _SUBJECTS, _TEXT_BODIES, _HTML_BODIES dicts for en/es/pt. send_invite_email() accepts language param with fallback to 'en'. Portal API passes caller.language when creating invitations.
4 portal_users table has a language column defaulting to 'en' VERIFIED Migration adds language VARCHAR(10) NOT NULL DEFAULT 'en' (lines 285294). PortalUser ORM maps language: Mapped[str] with server_default='en' (line 6468).
5 Templates API returns translated fields when locale param is provided VERIFIED list_templates() and get_template() accept locale: str = Query("en") (lines 115, 141). TemplateResponse.from_orm(locale=) merges translated fields from JSONB at serialization time (lines 6381).
6 next-intl installed and configured with cookie-based locale VERIFIED next-intl@^4.8.3 in package.json. i18n/request.ts reads konstruct_locale cookie via getRequestConfig. next.config.ts wrapped with createNextIntlPlugin.
7 NextIntlClientProvider wraps the app in root layout.tsx VERIFIED app/layout.tsx imports NextIntlClientProvider from 'next-intl' (line 3), wraps body children (line 38). getLocale() and getMessages() called server-side for dynamic locale.
8 Language switcher is visible in the sidebar near the user avatar VERIFIED components/language-switcher.tsx is a substantive Client Component rendering EN/ES/PT buttons. nav.tsx imports and renders <LanguageSwitcher /> (lines 25, 126).
9 Language selection persists via cookie (pre-auth) and DB (post-auth) VERIFIED LanguageSwitcher sets konstruct_locale cookie, PATCHes /api/portal/users/me/language, calls update({ language }) on Auth.js session. isPreAuth prop skips DB/session update on login page. session-sync.tsx reconciles cookie from session.user.language post-login.
10 Every user-visible string in the portal uses useTranslations() VERIFIED All 22 component files and all 22 page files confirmed to contain useTranslations or getTranslations calls. 26 translation namespaces present in all three message files with zero missing keys (en/es/pt parity confirmed programmatically).
11 Adding a new language requires only a new JSON file in messages/ VERIFIED SUPPORTED_LOCALES in i18n/locales.ts is the single code change needed. All message files use identical key structures. No language code is hardcoded in components.

Score: 11/11 truths verified (automated)

Required Artifacts

Artifact Provided Status Details
migrations/versions/009_multilanguage.py DB migration: language col + translations JSONB + es/pt seed VERIFIED Substantive: 331 lines, full upgrade/downgrade, 7 template translations
packages/shared/shared/prompts/system_prompt_builder.py LANGUAGE_INSTRUCTION in system prompts VERIFIED LANGUAGE_INSTRUCTION constant defined and appended before AI_TRANSPARENCY_CLAUSE
packages/portal/lib/system-prompt-builder.ts TS mirror with LANGUAGE_INSTRUCTION VERIFIED Constant defined at line 18, appended at line 46
packages/shared/shared/email.py Localized invitation emails VERIFIED Contains language param, three full en/es/pt templates
packages/shared/shared/api/templates.py Locale-aware template list endpoint VERIFIED locale query param on both list and detail endpoints, overlay merge logic present
packages/shared/shared/api/portal.py PATCH /users/me/language endpoint + language in AuthVerifyResponse VERIFIED Endpoint at line 864; AuthVerifyResponse includes language at line 50
packages/portal/i18n/request.ts next-intl server config reading locale from cookie VERIFIED getRequestConfig reads konstruct_locale cookie, falls back to 'en'
packages/portal/i18n/locales.ts Shared locale constants (client-safe) VERIFIED SUPPORTED_LOCALES, LOCALE_COOKIE, DEFAULT_LOCALE, isValidLocale
packages/portal/messages/en.json English source of truth VERIFIED 26 namespaces covering all pages and components
packages/portal/messages/es.json Spanish translations VERIFIED 26 namespaces, zero missing keys vs. en.json
packages/portal/messages/pt.json Portuguese translations VERIFIED 26 namespaces, zero missing keys vs. en.json
packages/portal/components/language-switcher.tsx EN/ES/PT switcher component VERIFIED Substantive: cookie + DB PATCH + JWT update + router.refresh()
packages/portal/lib/auth.ts language field in JWT callback VERIFIED JWT reads user.language, handles trigger=update for language, exposes on session.user.language
packages/portal/lib/auth-types.ts language in session/token types VERIFIED language present in User, Session, and JWT type declarations
packages/portal/components/session-sync.tsx Locale cookie sync from DB-authoritative session VERIFIED Reconciles konstruct_locale cookie with session.user.language post-login
packages/portal/app/(auth)/login/page.tsx useTranslations + browser locale detection + LanguageSwitcher VERIFIED useTranslations('login'), navigator.language detection, <LanguageSwitcher isPreAuth />
tests/integration/test_language_preference.py Integration tests for PATCH language endpoint VERIFIED 4 tests: valid patch, invalid locale, persistence, unauthenticated
tests/integration/test_templates_i18n.py Integration tests for locale-aware templates VERIFIED 5 tests: default locale, Spanish, Portuguese, unsupported locale fallback, overlay check
From To Via Status Details
system_prompt_builder.py AI Employee responses LANGUAGE_INSTRUCTION appended in build_system_prompt() WIRED Line 74: sections.append(LANGUAGE_INSTRUCTION) before AI_TRANSPARENCY_CLAUSE
templates.py agent_templates.translations JSONB column merge on locale query param WIRED TemplateResponse.from_orm(locale=locale) merges on lines 7581
app/layout.tsx i18n/request.ts NextIntlClientProvider reads locale + messages WIRED getLocale()/getMessages() called server-side, passed to provider at line 38
language-switcher.tsx /api/portal/users/me/language PATCH request via api.patch() WIRED await api.patch('/api/portal/users/me/language', { language: locale }) at line 49
nav.tsx language-switcher.tsx LanguageSwitcher rendered in sidebar WIRED Imported at line 25, rendered at line 126
template-gallery.tsx /api/portal/templates?locale= useTemplates(locale) passes locale query param WIRED useLocale() at line 203, useTemplates(locale) at line 204; queries.ts builds URL with ?locale=
All portal components messages/{locale}.json useTranslations() hook via NextIntlClientProvider WIRED 48 total useTranslations/getTranslations usages across components and pages

Requirements Coverage

Requirement Source Plan(s) Description Status Evidence
I18N-01 07-02, 07-03, 07-04 Portal UI fully localized in English, Spanish, and Portuguese (all pages, labels, buttons, error messages) VERIFIED (automated) All 44+ TSX files use useTranslations(). 26 namespaces in en/es/pt with full key parity.
I18N-02 07-01, 07-02, 07-04 Language switcher accessible from anywhere — selection persists across sessions VERIFIED (automated) LanguageSwitcher in nav + login. Cookie + DB PATCH + JWT update chain wired. session-sync.tsx reconciles on login.
I18N-03 07-01, 07-04 AI Employees detect user language and respond accordingly VERIFIED (automated), NEEDS HUMAN LANGUAGE_INSTRUCTION present in all system prompts (Python + TS). Live LLM behavior requires human test.
I18N-04 07-01, 07-03, 07-04 Agent templates, wizard steps, and onboarding fully translated VERIFIED (automated) Templates API serves JSONB translations by locale. All 6 wizard steps and 3 onboarding steps use useTranslations(). template-gallery.tsx passes locale to API.
I18N-05 07-01, 07-03, 07-04 Error messages, validation text, and system notifications localized VERIFIED (automated) validation namespace in all 3 message files. Portal components use t() for validation strings.
I18N-06 07-01, 07-02, 07-04 Adding a new language requires only translation files, not code changes VERIFIED (automated) SUPPORTED_LOCALES in i18n/locales.ts is the single code change. All message files are standalone JSON. Migration 009 seed data is locale-keyed JSONB.

All 6 I18N requirements are accounted for. Zero orphaned requirements.

Anti-Patterns Found

No anti-patterns detected in key Phase 07 files. No TODO/FIXME/PLACEHOLDER comments. No stub implementations. No empty handlers. No hardcoded English strings remaining in key UI files (spot-checked nav.tsx, chat-window.tsx, agents/page.tsx — all use t() calls).

Human Verification Required

1. Language Switcher Visual Correctness

Test: Start the portal dev environment. Switch to Spanish using the EN/ES/PT buttons in the sidebar. Navigate through Dashboard, Employees, Chat, Billing, and Usage. Expected: All page titles, labels, buttons, table headers, and empty states display in Spanish with no untranslated (English) strings visible. Why human: Automated checks confirm useTranslations() calls exist but cannot verify that every key is correctly mapped, that translation quality is natural (not machine-translated), or that no rendering path bypasses the translation layer.

2. Language Persistence Across Sessions

Test: Select Portuguese (PT). Log out. Log back in. Expected: The portal loads in Portuguese — the language preference survived the session boundary via DB + JWT. Why human: Requires a live Auth.js token round-trip and database read. Static analysis confirms the wiring is correct but cannot simulate the full login/logout flow.

3. Browser Locale Auto-Detection

Test: Clear all cookies. Open the login page in a browser configured for Spanish (navigator.language = 'es-*'). Expected: The login form automatically displays in Spanish without requiring manual selection. Why human: Requires a real browser with locale settings. The useEffect + navigator.language logic exists in the code (line 40 of login/page.tsx) but can only be tested in a browser.

4. AI Employee Language Response Behavior

Test: Open Chat. Send a message in Spanish: "Hola, necesito ayuda con mi cuenta." Then send one in Portuguese: "Ola, preciso de ajuda com minha conta." Expected: The agent responds in Spanish to the Spanish message and Portuguese to the Portuguese message. Why human: Requires a live LLM inference call. The LANGUAGE_INSTRUCTION is wired into system prompts but its effectiveness depends on the LLM's actual behavior, which cannot be verified statically.

Test: Switch to Spanish. Go to New Employee > Templates. Expected: Template cards display Spanish names and descriptions (e.g., "Representante de Soporte al Cliente", "Asistente de Ventas") instead of English. Why human: Requires the DB to have migration 009 applied (translating the JSONB data) and a live API call returning the translated fields. Confirms the full stack: DB migration → API overlay → React Query → template gallery render.

6. Localized Invitation Email

Test: As an admin with language preference 'es', invite a new user. Expected: The invitation email has a Spanish subject line: "Has sido invitado a unirte a {tenant_name} en Konstruct" Why human: Requires SMTP infrastructure and actual email delivery. The code path (reading caller.language, passing to send_invite_email(language=)) is wired but cannot be validated without a mail server.


Commit Verification

All commits confirmed present in their respective git repositories:

Parent repo (konstruct/):

  • 7a3a4f0 — feat(07-01): DB migration 009, ORM updates, LANGUAGE_INSTRUCTION
  • 9654982 — feat(07-01): localized emails, locale-aware templates API, language preference endpoint

Portal nested repo (packages/portal/):

  • e33eac6 — feat(07-02): install next-intl, configure i18n infrastructure, create message files
  • 6be47ae — feat(07-02): language switcher, Auth.js JWT language sync, login page locale detection
  • 20f4c5b — feat(07-03): extract i18n strings from portal components
  • c499029 — feat(07-03): extract i18n strings from portal pages

Note: packages/portal is a standalone git repository nested inside the monorepo. The parent repo's git log does not show individual portal commits, which is expected.


Verified: 2026-03-25T23:30:00Z Verifier: Claude (gsd-verifier)