Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

18 KiB

SCRATCHPAD — MSP i18n Infrastructure Sprint (Phase 0)

Key Discoveries

Current State (Pre-Phase 0)

  • I18N_CONFIG.ns is ['common', 'client-portal', 'msp'] — ALL namespaces loaded eagerly on every page
  • msp.json exists for all 7 languages (en, fr, es, de, nl, it, pl) — ~73 lines, ~60 keys
  • msp/ directory does NOT yet exist under any locale folder
  • Zero MSP components currently call useTranslation('msp') — the namespace exists but is unused in component code
  • Phase 1 test file at server/src/test/unit/i18n/mspI18nPhase1.test.ts references 'msp' namespace (lines 280, 284, 285) — needs update to 'msp/core'
  • I18nWrapper currently does NOT use usePathname() — it only resolves locale, not route-specific namespaces
  • I18nProvider does NOT accept a namespaces prop — no lazy loading mechanism exists yet
  • packages/core/src/lib/i18n/config.ts is a near-exact copy of packages/ui/src/lib/i18n/config.ts (exists to break circular dependency: ui → analytics → tenancy → ui)
  • http-backend loadPath /locales/{{lng}}/{{ns}}.json naturally resolves nested paths (e.g., msp/core/locales/en/msp/core.json) — no config change needed for nested namespaces

Key Infrastructure Already in Place

Component Path Status
I18nProvider packages/ui/src/lib/i18n/client.tsx Exists, needs namespaces prop
I18nWrapper packages/tenancy/src/components/i18n/I18nWrapper.tsx Exists, needs usePathname() + namespace passing
i18n config (ui) packages/ui/src/lib/i18n/config.ts Exists, needs ROUTE_NAMESPACES + getNamespacesForRoute()
i18n config (core) packages/core/src/lib/i18n/config.ts Exists, needs sync with ui config
useFormatters() packages/ui/src/lib/i18n/client.tsx:214-257 Exists, locale-aware (uses useI18n() context)
Feature flag msp-i18n-enabled Exists, gates MSP i18n
Phase 1 tests server/src/test/unit/i18n/mspI18nPhase1.test.ts Exists, needs 'msp' → 'msp/core' update
MSP layout (standard) server/src/app/msp/layout.tsx + MspLayoutClient.tsx Exists, conditionally wraps I18nWrapper
MSP layout (EE) ee/server/src/app/msp/layout.tsx + MspLayoutClient.tsx Exists, conditionally wraps I18nWrapper
Translation files server/public/locales/{lang}/msp.json 7 files, to be renamed to msp/core.json

useTranslation('msp') References

Grep found zero references in actual source code — only in documentation/plan files:

  • docs/plans/2026-02-19-msp-i18n-infrastructure-sprint/features.json
  • docs/plans/2026-02-19-msp-i18n-infrastructure-sprint/PRD.md
  • docs/plans/2026-02-18-msp-i18n-full-translation-plan.md
  • docs/plans/2026-02-12-msp-i18n-phase1/SCRATCHPAD.md

This means F017 ("Update all useTranslation('msp') references") only affects the Phase 1 test file (lines 280, 284, 285) and the config ns array. No component code references 'msp' namespace directly.

Decisions

Decision Rationale
ns: ['common'] — only common on init Future-proof; as 12+ MSP namespaces are added, eager loading would be 20+ HTTP requests per page
Route-based lazy loading via ROUTE_NAMESPACES Deterministic — developer adds route entry, namespaces auto-load. No guessing.
getNamespacesForRoute() uses exact → longest prefix → fallback Handles both exact routes (/msp/tickets) and sub-routes (/msp/tickets/123)
Pseudo-locales xx/yy available in all environments Visual QA for extraction completeness; useful in staging/production for verification
Pseudo-locale files NOT committed to git They're generated artifacts; add server/public/locales/xx/ and yy/ to .gitignore
msp.jsonmsp/core.json rename now Establishes the msp/ directory convention before batch 1 adds msp/settings.json
I18nWrapper uses usePathname() from next/navigation Standard Next.js hook; re-renders on route change, triggering namespace re-resolution

Implementation Order

Recommended order to minimize breakage:

  1. Rename msp.json → msp/core.json (F016) — file system change, update test references (F017, F023)
  2. Add ROUTE_NAMESPACES + getNamespacesForRoute() (F002, F003, F018) — config additions, no behavioral change yet
  3. Change I18N_CONFIG.ns to ['common'] (F001) — behavioral change, client portal will need route-based loading from this point
  4. Update I18nProvider (F004, F005) — accept namespaces prop, add loadNamespaces() effect
  5. Update I18nWrapper (F006, F007, F008) — usePathname() + pass namespaces to I18nProvider
  6. Sync core config (F019) — copy changes to packages/core
  7. Add pseudo-locale support (F009-F015) — script + config
  8. Verify (F020-F024) — client portal, MSP flag off, MSP flag on, tests, build

Key File Paths

Purpose Path
i18n config (ui) packages/ui/src/lib/i18n/config.ts
i18n config (core) packages/core/src/lib/i18n/config.ts
I18nProvider packages/ui/src/lib/i18n/client.tsx
I18nWrapper packages/tenancy/src/components/i18n/I18nWrapper.tsx
Phase 1 tests server/src/test/unit/i18n/mspI18nPhase1.test.ts
MSP layout (standard) server/src/app/msp/layout.tsx
MSP layout client (standard) server/src/app/msp/MspLayoutClient.tsx
MSP layout (EE) ee/server/src/app/msp/layout.tsx
MSP layout client (EE) ee/server/src/app/msp/MspLayoutClient.tsx
Translation files server/public/locales/{lang}/msp.json (current) → server/public/locales/{lang}/msp/core.json (target)
Pseudo-locale script scripts/generate-pseudo-locale.ts (new)
Feature flags server/src/lib/feature-flags/featureFlags.ts

Gotchas

  • Config duplication: packages/ui/src/lib/i18n/config.ts and packages/core/src/lib/i18n/config.ts MUST stay in sync. The core copy exists to break a circular dependency (ui → analytics → tenancy → ui).
  • http-backend loadPath: /locales/{{lng}}/{{ns}}.json already resolves nested paths — msp/core becomes /locales/en/msp/core.json. No backend config change needed.
  • Client portal must keep working: After changing ns: ['common'], the client portal relies on I18nWrappergetNamespacesForRoute() to load client-portal namespace. Test this carefully.
  • Build memory: Phase 1 build needed NODE_OPTIONS=--max-old-space-size=8192. Expect the same here.
  • Pseudo-locale .gitignore: Remember to add server/public/locales/xx/ and server/public/locales/yy/ to .gitignore.
  • i18next.loadNamespaces(): This is an async operation. The I18nProvider effect needs to handle the promise and potentially show a loading state while namespaces are being fetched.
  • usePathname() requires 'use client': I18nWrapper is already a client component, so this is fine.
  • ROUTE_NAMESPACES entries for future routes: Include entries for routes that don't have feature namespace files yet (e.g., /msp/settings with just ['common', 'msp/core']). Missing namespace files simply won't be fetched — i18next http-backend handles 404s gracefully.

Commands

# Run Phase 1 tests (to verify msp/core rename doesn't break them)
npx jest server/src/test/unit/i18n/mspI18nPhase1.test.ts

# Build with enough memory
NODE_OPTIONS=--max-old-space-size=8192 npm run build

# Generate pseudo-locale (after script is created)
npx ts-node scripts/generate-pseudo-locale.ts --locale xx --fill 1111
npx ts-node scripts/generate-pseudo-locale.ts --locale yy --fill 5555

# Check for remaining 'msp' namespace references (should be zero after rename)
grep -r "useTranslation('msp')" --include="*.ts" --include="*.tsx" server/ packages/ ee/
grep -r "'msp'" server/src/test/unit/i18n/

# Verify msp/core.json exists for all languages
ls server/public/locales/*/msp/core.json

# Verify old msp.json removed
ls server/public/locales/*/msp.json  # should fail (files removed)

Updates

  • 2026-02-20: Updated I18N_CONFIG.ns in packages/ui/src/lib/i18n/config.ts to ['common'] for lazy namespace loading (F001).
  • 2026-02-20: Added ROUTE_NAMESPACES mapping in packages/ui/src/lib/i18n/config.ts for client portal and MSP routes (F002).
  • 2026-02-20: Added getNamespacesForRoute() helper with exact + longest prefix matching in packages/ui/src/lib/i18n/config.ts (F003).
  • 2026-02-20: Added optional namespaces prop to I18nProvider in packages/ui/src/lib/i18n/client.tsx (F004).
  • 2026-02-20: I18nProvider now loads missing namespaces on route changes via i18next.loadNamespaces() (F005).
  • 2026-02-20: I18nWrapper now reads the current route via usePathname() (F006).
  • 2026-02-20: I18nWrapper now resolves namespaces with getNamespacesForRoute() and passes them to I18nProvider (F007).
  • 2026-02-20: I18nWrapper memoizes namespace resolution to update on pathname changes (F008).
  • 2026-02-20: Added scripts/generate-pseudo-locale.ts with CLI args parsing for pseudo-locale generation (F009).
  • 2026-02-20: Pseudo-locale script now walks all English JSON namespaces recursively (including nested features/ and msp/) (F010).
  • 2026-02-20: Pseudo-locale output preserves directory structure under server/public/locales/<locale>/ (F011).
  • 2026-02-20: Pseudo-locale generator replaces all leaf string values with the fill token (F012).
  • 2026-02-20: Pseudo-locale generator preserves {{variables}} in transformed strings (F013).
  • 2026-02-20: Pseudo-locale generator preserves JSON key structure (nested objects/arrays) (F014).
  • 2026-02-20: Added pseudo-locales xx and yy to LOCALE_CONFIG.supportedLocales in UI config (F015).
  • 2026-02-20: Renamed server/public/locales/{lang}/msp.json to server/public/locales/{lang}/msp/core.json for all locales (F016).
  • 2026-02-20: Confirmed no useTranslation('msp') references in code; remaining namespace updates handled in Phase 1 tests (F017).
  • 2026-02-20: ROUTE_NAMESPACES entries use msp/core for MSP routes (F018).
  • 2026-02-20: Synced packages/core/src/lib/i18n/config.ts with UI config (namespaces, route mapping, pseudo-locales) (F019).
  • 2026-02-20: Updated Phase 1 i18n tests to use msp/core namespace and file paths (F023).
  • 2026-02-20: Added tracking items F025/T053 to ensure pseudo-locale outputs are gitignored per PRD rollout guidance.
  • 2026-02-20: Ignored pseudo-locale outputs in .gitignore (server/public/locales/xx/, server/public/locales/yy/) (F025).
  • 2026-02-20: Added Phase 0 i18n test suite and made pseudo-locale generator ESM-safe; validated UI I18N_CONFIG.ns via tests (T001).
  • 2026-02-20: Verified core I18N_CONFIG.ns via Phase 0 test coverage (T002).
  • 2026-02-20: Confirmed ROUTE_NAMESPACES export via Phase 0 tests (T003).
  • 2026-02-20: Verified /client-portal route namespace mapping in Phase 0 tests (T004).
  • 2026-02-20: Completed T005 checklist entry — ROUTE_NAMESPACES maps '/client-portal/tickets' to ['common', 'client-portal', 'features/tickets']
  • 2026-02-20: Completed T006 checklist entry — ROUTE_NAMESPACES maps '/msp' to ['common', 'msp/core'] (at minimum)
  • 2026-02-20: Completed T007 checklist entry — ROUTE_NAMESPACES maps '/msp/tickets' to ['common', 'msp/core', 'features/tickets']
  • 2026-02-20: Completed T008 checklist entry — ROUTE_NAMESPACES maps '/msp/settings' to ['common', 'msp/core']
  • 2026-02-20: Completed T009 checklist entry — All ROUTE_NAMESPACES MSP entries use 'msp/core' (not 'msp')
  • 2026-02-20: Completed T010 checklist entry — getNamespacesForRoute is exported from packages/ui/src/lib/i18n/config.ts
  • 2026-02-20: Completed T011 checklist entry — getNamespacesForRoute('/msp/tickets') returns namespaces including 'msp/core' and 'features/tickets'
  • 2026-02-20: Completed T012 checklist entry — getNamespacesForRoute('/client-portal/billing') returns namespaces including 'client-portal' and 'features/billing'
  • 2026-02-20: Completed T013 checklist entry — getNamespacesForRoute exact match takes priority over prefix match (e.g., '/msp/tickets' exact entry wins over '/msp' prefix)
  • 2026-02-20: Completed T014 checklist entry — getNamespacesForRoute uses longest prefix match when no exact match (e.g., '/msp/tickets/123' matches '/msp/tickets' entry)
  • 2026-02-20: Completed T015 checklist entry — getNamespacesForRoute falls back to ['common'] for unknown routes
  • 2026-02-20: Completed T016 checklist entry — I18nProvider accepts optional namespaces prop (string[])
  • 2026-02-20: Completed T017 checklist entry — I18nProvider calls i18next.loadNamespaces() for namespaces not yet loaded when namespaces prop is provided
  • 2026-02-20: Completed T018 checklist entry — I18nProvider loads new namespaces when namespaces prop changes (simulating route navigation)
  • 2026-02-20: Completed T019 checklist entry — I18nProvider does not re-load namespaces that are already loaded
  • 2026-02-20: Completed T020 checklist entry — I18nWrapper uses usePathname() to read the current route
  • 2026-02-20: Completed T021 checklist entry — I18nWrapper calls getNamespacesForRoute(pathname) and passes result as namespaces prop to I18nProvider
  • 2026-02-20: Completed T022 checklist entry — I18nWrapper re-resolves namespaces when pathname changes (route navigation triggers new namespace resolution)
  • 2026-02-20: Completed T023 checklist entry — scripts/generate-pseudo-locale.ts exists and is runnable
  • 2026-02-20: Completed T024 checklist entry — Pseudo-locale script accepts --locale and --fill CLI arguments
  • 2026-02-20: Completed T025 checklist entry — Pseudo-locale script with --locale xx --fill 1111 generates files under server/public/locales/xx/
  • 2026-02-20: Completed T026 checklist entry — Pseudo-locale script reads all English namespace JSON files including nested features/ and msp/ directories
  • 2026-02-20: Completed T027 checklist entry — Pseudo-locale script output preserves directory structure (e.g., features/tickets.json -> xx/features/tickets.json)
  • 2026-02-20: Completed T028 checklist entry — Pseudo-locale script replaces all leaf string values with the fill string
  • 2026-02-20: Completed T029 checklist entry — Pseudo-locale script preserves {{variables}} within fill strings (e.g., '1111 {{name}} 1111' for a value containing {{name}})
  • 2026-02-20: Completed T030 checklist entry — Pseudo-locale script preserves multiple {{variables}} in a single value (e.g., 'Page {{current}} of {{total}}')
  • 2026-02-20: Completed T031 checklist entry — Pseudo-locale script preserves JSON key structure exactly (nested objects maintained, no flattening)
  • 2026-02-20: Completed T032 checklist entry — Pseudo-locale output for common.json has same key structure as en/common.json but all leaf values replaced
  • 2026-02-20: Completed T033 checklist entry — Pseudo-locales xx and yy are added to LOCALE_CONFIG.supportedLocales
  • 2026-02-20: Completed T034 checklist entry — Pseudo-locales xx and yy are present in LOCALE_CONFIG.supportedLocales in all environments
  • 2026-02-20: Completed T035 checklist entry — Pseudo-locales xx and yy are added to I18N_CONFIG.supportedLngs
  • 2026-02-20: Completed T036 checklist entry — server/public/locales/en/msp/core.json exists with same content as old msp.json
  • 2026-02-20: Completed T037 checklist entry — server/public/locales/{lang}/msp/core.json exists for all 7 languages (en, fr, es, de, nl, it, pl)
  • 2026-02-20: Completed T038 checklist entry — Old server/public/locales/{lang}/msp.json files are removed for all 7 languages
  • 2026-02-20: Completed T039 checklist entry — No useTranslation('msp') references remain in source code (only 'msp/core')
  • 2026-02-20: Completed T040 checklist entry — Phase 1 test file references updated from 'msp' to 'msp/core' namespace
  • 2026-02-20: Completed T041 checklist entry — i18next.t('nav.home', { ns: 'msp/core' }) returns correct English translation after namespace rename
  • 2026-02-20: Completed T042 checklist entry — packages/core/src/lib/i18n/config.ts I18N_CONFIG.ns is synced to ['common']
  • 2026-02-20: Completed T043 checklist entry — packages/core/src/lib/i18n/config.ts LOCALE_CONFIG matches packages/ui/src/lib/i18n/config.ts (including any pseudo-locale changes)
  • 2026-02-20: Completed T044 checklist entry — Client portal at /client-portal loads common and client-portal namespaces correctly
  • 2026-02-20: Completed T045 checklist entry — Client portal at /client-portal/tickets loads features/tickets namespace in addition to common and client-portal
  • 2026-02-20: Completed T046 checklist entry — Client portal translations continue displaying correctly after config changes (no regressions)
  • 2026-02-20: Completed T047 checklist entry — MSP portal with flag OFF: no I18nWrapper rendered, no namespace HTTP requests for msp/core
  • 2026-02-20: Completed T048 checklist entry — MSP portal with flag OFF: layout renders identically to before changes (zero behavior change)
  • 2026-02-20: Completed T049 checklist entry — MSP portal with flag ON: msp/core namespace loads correctly via lazy loading
  • 2026-02-20: Completed T050 checklist entry — MSP portal with flag ON: navigating to /msp/tickets loads features/tickets namespace in addition to msp/core
  • 2026-02-20: Aligned client-portal and appointments locale keys across languages and updated Phase 1 i18n expectations; Phase 1 vitest now passes (T051).
  • 2026-02-20: Added pseudo-locale fallback mappings to date-fns locale utilities and confirmed npm run build succeeds (T052).
  • 2026-02-20: Verified pseudo-locale output directories are gitignored (T053).
  • 2026-02-20: Validated client portal namespace routing via Phase 0 tests (F020).
  • 2026-02-20: Confirmed MSP flag-off behavior remains unchanged via Phase 1 layout checks (F021).
  • 2026-02-20: Verified MSP flag-on namespace routing loads msp/core and feature namespaces (F022).
  • 2026-02-20: Build completed successfully with pseudo-locale support included (F024).