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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
18 KiB
18 KiB
SCRATCHPAD — MSP i18n Infrastructure Sprint (Phase 0)
Key Discoveries
Current State (Pre-Phase 0)
I18N_CONFIG.nsis['common', 'client-portal', 'msp']— ALL namespaces loaded eagerly on every pagemsp.jsonexists for all 7 languages (en, fr, es, de, nl, it, pl) — ~73 lines, ~60 keysmsp/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.tsreferences'msp'namespace (lines 280, 284, 285) — needs update to'msp/core' I18nWrappercurrently does NOT useusePathname()— it only resolves locale, not route-specific namespacesI18nProviderdoes NOT accept anamespacesprop — no lazy loading mechanism exists yetpackages/core/src/lib/i18n/config.tsis a near-exact copy ofpackages/ui/src/lib/i18n/config.ts(exists to break circular dependency: ui → analytics → tenancy → ui)- http-backend loadPath
/locales/{{lng}}/{{ns}}.jsonnaturally 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.jsondocs/plans/2026-02-19-msp-i18n-infrastructure-sprint/PRD.mddocs/plans/2026-02-18-msp-i18n-full-translation-plan.mddocs/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.json → msp/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:
- Rename msp.json → msp/core.json (F016) — file system change, update test references (F017, F023)
- Add ROUTE_NAMESPACES + getNamespacesForRoute() (F002, F003, F018) — config additions, no behavioral change yet
- Change I18N_CONFIG.ns to ['common'] (F001) — behavioral change, client portal will need route-based loading from this point
- Update I18nProvider (F004, F005) — accept
namespacesprop, addloadNamespaces()effect - Update I18nWrapper (F006, F007, F008) —
usePathname()+ pass namespaces to I18nProvider - Sync core config (F019) — copy changes to packages/core
- Add pseudo-locale support (F009-F015) — script + config
- 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.tsandpackages/core/src/lib/i18n/config.tsMUST stay in sync. The core copy exists to break a circular dependency (ui → analytics → tenancy → ui). - http-backend loadPath:
/locales/{{lng}}/{{ns}}.jsonalready resolves nested paths —msp/corebecomes/locales/en/msp/core.json. No backend config change needed. - Client portal must keep working: After changing
ns: ['common'], the client portal relies onI18nWrapper→getNamespacesForRoute()to loadclient-portalnamespace. 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/andserver/public/locales/yy/to.gitignore. - i18next.loadNamespaces(): This is an async operation. The
I18nProvidereffect needs to handle the promise and potentially show a loading state while namespaces are being fetched. - usePathname() requires 'use client':
I18nWrapperis 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/settingswith 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.nsinpackages/ui/src/lib/i18n/config.tsto['common']for lazy namespace loading (F001). - 2026-02-20: Added
ROUTE_NAMESPACESmapping inpackages/ui/src/lib/i18n/config.tsfor client portal and MSP routes (F002). - 2026-02-20: Added
getNamespacesForRoute()helper with exact + longest prefix matching inpackages/ui/src/lib/i18n/config.ts(F003). - 2026-02-20: Added optional
namespacesprop toI18nProviderinpackages/ui/src/lib/i18n/client.tsx(F004). - 2026-02-20:
I18nProvidernow loads missing namespaces on route changes viai18next.loadNamespaces()(F005). - 2026-02-20:
I18nWrappernow reads the current route viausePathname()(F006). - 2026-02-20:
I18nWrappernow resolves namespaces withgetNamespacesForRoute()and passes them toI18nProvider(F007). - 2026-02-20:
I18nWrappermemoizes namespace resolution to update on pathname changes (F008). - 2026-02-20: Added
scripts/generate-pseudo-locale.tswith CLI args parsing for pseudo-locale generation (F009). - 2026-02-20: Pseudo-locale script now walks all English JSON namespaces recursively (including nested
features/andmsp/) (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
xxandyytoLOCALE_CONFIG.supportedLocalesin UI config (F015). - 2026-02-20: Renamed
server/public/locales/{lang}/msp.jsontoserver/public/locales/{lang}/msp/core.jsonfor 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/corefor MSP routes (F018). - 2026-02-20: Synced
packages/core/src/lib/i18n/config.tswith UI config (namespaces, route mapping, pseudo-locales) (F019). - 2026-02-20: Updated Phase 1 i18n tests to use
msp/corenamespace 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.nsvia tests (T001). - 2026-02-20: Verified core
I18N_CONFIG.nsvia Phase 0 test coverage (T002). - 2026-02-20: Confirmed
ROUTE_NAMESPACESexport via Phase 0 tests (T003). - 2026-02-20: Verified
/client-portalroute 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 buildsucceeds (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/coreand feature namespaces (F022). - 2026-02-20: Build completed successfully with pseudo-locale support included (F024).