---
phase: 07-multilanguage
plan: "03"
subsystem: portal-i18n
tags:
  - i18n
  - next-intl
  - portal
  - components
  - pages
dependency_graph:
  requires:
    - 07-01
    - 07-02
  provides:
    - fully-translated-portal-ui
  affects:
    - packages/portal/components
    - packages/portal/app
    - packages/portal/messages
tech_stack:
  added: []
  patterns:
    - next-intl useTranslations() in client components
    - next-intl getTranslations() in server components
    - ICU message format with named params
    - useTemplates(locale) locale-aware API call
key_files:
  created: []
  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/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
    - 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)/billing/page.tsx
    - packages/portal/app/(dashboard)/chat/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/lib/queries.ts
    - packages/portal/messages/en.json
    - packages/portal/messages/es.json
    - packages/portal/messages/pt.json
decisions:
  - "onboarding/page.tsx uses getTranslations() not useTranslations() — Server Component requires next-intl/server import"
  - "TIME_RANGE_OPTIONS moved inside component body — module-level constants cannot access t() hook"
  - "billing-status.tsx trialEnds key simplified to only {date} param — removed boolean hasDays ICU param that caused TypeScript error"
  - "WhatsApp credential step instructions stored as plain-text translation keys — avoids dangerouslySetInnerHTML for HTML-marked-up steps"
  - "useTemplates(locale?) accepts optional locale and passes as ?locale= query param — enables locale-aware template API calls"
metrics:
  duration: ~45min
  completed: 2026-03-25
  tasks_completed: 2
  files_modified: 48
---

# Phase 7 Plan 3: Portal i18n String Extraction Summary

All 44 portal TSX files now use next-intl `useTranslations()` for every user-visible string — zero hardcoded English in components or pages, with full EN/ES/PT translations for all keys including new namespaces `billingStatus`, `budgetAlert`, `subscriptionCard`, and `invite`.

## Tasks Completed

### Task 1: Extract strings from 22 component files

All component files migrated to `useTranslations()`:

- **Navigation**: `nav.tsx` — nav labels, sign out
- **Tenant management**: `tenant-form.tsx`, `tenant-switcher.tsx` — form labels, switcher UI
- **Billing**: `billing-status.tsx`, `budget-alert-badge.tsx`, `subscription-card.tsx` — all subscription states, budget thresholds, plan details
- **Templates**: `template-gallery.tsx` — category labels, deploy buttons, preview modal
- **Employee wizard**: `employee-wizard.tsx` + all 6 step components (role, persona, tools, channels, escalation, review)
- **Onboarding**: `onboarding-stepper.tsx`, `impersonation-banner.tsx`
- **Chat**: `chat-sidebar.tsx`, `chat-window.tsx`, `chat-message.tsx`, `message-volume-chart.tsx`, `provider-cost-chart.tsx`
- **Agent designer**: `agent-designer.tsx`

### Task 2: Extract strings from 22 page files

All page files migrated to `useTranslations()`:

- **Core pages**: dashboard, chat, billing, usage (list + detail)
- **Agent pages**: agents list, agent detail, new agent picker, templates, wizard, advanced designer
- **Settings**: api-keys
- **User management**: users, admin/users
- **Tenant management**: tenants list, tenant detail, new tenant
- **Onboarding**: onboarding page (Server Component with `getTranslations`), plus all 3 step components (connect-channel, configure-agent, test-message)
- **Public**: invite accept page

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Fixed TypeScript error in billing-status.tsx**
- **Found during:** Task 1
- **Issue:** `hasDays: days !== null` passed a boolean to an ICU message parameter typed as `string | number | Date` — TypeScript strict mode rejects this
- **Fix:** Removed `hasDays` parameter entirely; simplified `trialEnds` key to `"Trial ends {date}"` using only `{date}`
- **Files modified:** `components/billing-status.tsx`, `messages/en.json`, `messages/es.json`, `messages/pt.json`
- **Commit:** 20f4c5b

**2. [Rule 2 - Missing functionality] Added onboarding step translations not in plan scope**
- **Found during:** Task 2
- **Issue:** `onboarding/steps/connect-channel.tsx`, `configure-agent.tsx`, `test-message.tsx` contained hardcoded English; plan listed them in `files_modified` but original task breakdown only mentioned 22 pages without explicitly calling out the step components as separate
- **Fix:** Added ~60 new keys to the `onboarding` namespace in all three message files; rewrote all three step components with `useTranslations("onboarding")`
- **Files modified:** all 3 step files + 3 message files

**3. [Rule 1 - Bug] TIME_RANGE_OPTIONS moved inside component**
- **Found during:** Task 2
- **Issue:** `app/(dashboard)/usage/[tenantId]/page.tsx` had `TIME_RANGE_OPTIONS` defined at module level with hardcoded English strings, which cannot access the `t()` hook
- **Fix:** Moved array construction inside the component function body
- **Files modified:** `app/(dashboard)/usage/[tenantId]/page.tsx`

**4. [Rule 2 - Missing functionality] WhatsApp instructions as plain text**
- **Found during:** Task 1 (connect-channel.tsx)
- **Issue:** Original file used `<strong>` HTML inside `<li>` elements for emphasis in credential instructions; direct translation keys can't hold HTML safely
- **Fix:** Stored instructions as plain-text translation keys (no HTML); bold emphasis replaced with readable text
- **Files modified:** `components/wizard-steps/step-channels.tsx` was already in scope; `onboarding/steps/connect-channel.tsx` instructions simplified

## Verification

TypeScript type check (`npx tsc --noEmit`) passes with zero errors after all changes.

## Self-Check: PASSED

Files created/modified confirmed present. Commits verified:
- `20f4c5b` — feat(07-03): extract i18n strings from portal components
- `c499029` — feat(07-03): extract i18n strings from portal pages
