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

62 KiB

Scratchpad — MSP i18n Batches 2b-10/11/12/16: Clients, Contacts, Assets, Onboarding

  • Plan slug: 2026-03-20-msp-i18n-clients-assets-onboarding
  • Created: 2026-03-20

Decisions

  • (2026-03-20) String estimates carry uncertainty: Automated scan reported ~7,450 total but previous batches showed 1.5-2.5x overestimation. Lower bound (~3,300) is more realistic. Exact counts during implementation.
  • (2026-03-20) Onboarding scope: Only wizard steps + OnboardingWizard.tsx. Dashboard onboarding (DashboardOnboardingSection, OnboardingChecklist) already translated in batch 2b-2.
  • (2026-03-20) Execution order: Clients → Contacts → Assets → Onboarding. Clients/contacts are daily-use features. Assets is large but self-contained. Onboarding is used once per tenant but is important for first impressions.
  • (2026-03-24) Post-rebase plan update: Rebased on origin/main after merges of client-owned-contracts-simplification, board-specific-statuses, and 4 new i18n batches (dispatch, reports, admin, time-entry). Updated file counts, LOC, and string estimates. No structural changes to the plan — all 4 batches remain valid.

Discoveries / Constraints

Clients (2b-10)

  • (2026-03-20) 31 files in packages/clients/src/components/clients/ (+panels/ subdir with ClientNotesPanel.tsx)
  • (2026-03-20) ClientDetails.tsx (1,805 LOC) is the largest — full client detail page with multiple sections
  • (2026-03-20) Heavy billing integration: BillingConfiguration, ClientContractAssignment, ClientContractLineDashboard, ClientBillingSchedule, TaxSettingsForm — may share terminology with msp/contracts namespace. Cross-check translations.
  • (2026-03-24) Post-rebase update: 6 client files modified by client-owned-contracts-simplification merge. BillingConfiguration.tsx (661→701 LOC), ClientBillingSchedule.tsx (~387→504 LOC, significant growth), ClientContractAssignment.tsx (430→423 LOC, refactored for assignment-explicit semantics), ClientLocations.tsx (1,021→1,038 LOC), ContractLines.tsx (+55 LOC), ClientContractLineDisambiguationGuide.tsx (+14 LOC). Billing terminology changed — must cross-check against msp/contracts namespace.
  • (2026-03-24) ClientBillingSchedule.tsx mixes local UI copy with cadenceContext strings returned from shared billing helpers. The local shell/actions/labels can move to msp/clients now, but full localization of changeScopeDescription, scheduleDescription, and previewDescription likely needs an upstream shared-data change later.
  • (2026-03-20) ClientsImportDialog.tsx (697 LOC) has CSV column mapping — translate UI labels but keep CSV header names in English
  • (2026-03-20) ClientLanguagePreference.tsx (118 LOC) — this component likely already has i18n awareness. Check during implementation.
  • (2026-03-24) PlanPickerDialog.tsx and ClientPlanDisambiguationGuide.tsx are thin re-export shims (ContractLinePickerDialog / ClientContractLineDisambiguationGuide) rather than direct UI surfaces. No separate namespace wiring is needed there; translation work belongs in the underlying implementation files.
  • (2026-03-20) 2 empty stub files (PlanPickerDialog.tsx, ClientPlanDisambiguationGuide.tsx) — 1 LOC each, skip
  • (2026-03-24) ClientLocations.tsx already contains partial clients.locations.* key usage. Extend that structure instead of replacing it with unrelated key names.
  • (2026-03-24) Keep stable ids/values untranslated in clients batch: client detail tab ids (details, tickets, assets, billing, billing-dashboard, contacts, documents, tax-settings, additional-info, notes, interactions), billing tab ids (general, plans, taxRates, overlaps), guide tabs (overview, bestPractices, scenarios, troubleshooting), import steps (upload, mapping, preview, importing, complete, unassigned), and list/filter values (grid, list, all, active, inactive, company, individual).

Contacts (2b-11)

  • (2026-03-24) 12 files in packages/clients/src/components/contacts/ (was 13 at plan time; ContactNotes.tsx removed or merged)
  • (2026-03-20) Same package as clients (@alga-psa/clients) — can share import of useTranslation
  • (2026-03-20) ContactPortalTab.tsx (652 LOC) — manages client portal access for contacts. May need to coordinate with client-portal translations.
  • (2026-03-20) ContactPhoneNumbersEditor.tsx (755 LOC) — complex phone number CRUD with format hints
  • (2026-03-24) There is also packages/clients/src/components/contacts/panels/ContactNotesPanel.tsx with user-facing copy (Notes & Quick Info, Initial Note, load/save error UI). The plan checklist omits it, so treat it as required remaining contact work rather than leaving the panel untranslated.

Assets (2b-12)

  • (2026-03-24) 41 files in packages/assets/src/components/ (was 39 at plan time; +1 test file, +1 new component)
  • (2026-03-20) Well-organized with subdirectories: tabs/, panels/, shared/
  • (2026-03-20) StatusBadge.tsx (102 LOC, ~60 strings) — high density, many status label variations
  • (2026-03-20) RMM integration components (RmmStatusIndicator, RmmVitalsPanel) — technical labels, some may stay English
  • (2026-03-20) AssetCommandPalette.tsx (256 LOC) — search/command interface, needs accessible translations
  • (2026-03-20) index.ts (13 LOC) — just exports, skip
  • (2026-03-24) Asset wrapper-only files need lighter handling in the namespace scaffold: AssetDashboard.tsx, AssetFormClient.tsx, AssetAlertsSection.tsx, AssetPatchStatusSection.tsx, and AssetSoftwareInventory.tsx are mostly wrappers/dynamic imports, while AssetDetailDrawer.types.ts, index.ts, and the component test files are non-UI sources that should stay out of locale coverage checks.
  • (2026-03-25) F022 audit showed AssetDetailDrawerClient.tsx, AssociatedAssets.tsx, and QuickAddAsset.tsx were already wired to useTranslation('msp/assets') on the branch. A Babel AST audit found 93 + 33 + 45 unique t(...) keys across those files with missing=0 against server/public/locales/en/msp/assets.json, so the checklist item could be flipped without further code edits.
  • (2026-03-25) CreateMaintenanceScheduleDialog.tsx still contains its own untranslated form/help copy even though it lives under components/tabs/. Kept it out of F023 because the plan item explicitly names only the 6 tab surfaces; treat the dialog as F025 remaining asset-file work.
  • (2026-03-25) AssetInfoPanel.tsx previously generated its copy-button id from the visible field label. That would have made automation ids locale-dependent once translated, so F024 switches the serial-number copy action to a stable explicit id (copy-serial-number) while localizing the tooltip text.
  • (2026-03-25) F025 wrapper audit: AssetAlertsSection.tsx, AssetPatchStatusSection.tsx, AssetSoftwareInventory.tsx, AssetDashboard.tsx, AssetDashboardGrid.tsx, AssetDocuments.tsx, and RemoteAccessButton.tsx do not render local copy in this package, so they were intentionally left out of namespace wiring. AssetFormClient.tsx looked like a thin wrapper too, but its dynamic loading skeleton rendered "Edit Asset", so that fallback was translated as part of the catch-all sweep.
  • (2026-03-25) F026 translation execution split cleanly across two workers: de/es/fr and it/nl/pl. That kept write scopes disjoint and avoided a giant manual locale edit in the main thread. Pseudo-locales for msp/assets were generated locally with the targeted transform from the runbook instead of re-running the global pseudo-locale script, so this feature commit only touches the new namespace.

Onboarding (2b-16)

  • (2026-03-24) TicketingConfigStep.tsx (3,040 LOC, was 2,920) is MASSIVE — larger than most entire components. Has ticketing board setup, status configuration, priority settings, category management, SLA configuration. This is effectively a mini settings page embedded in the wizard. Updated: +120 LOC for board-scoped status configuration (board-specific-statuses merge).
  • (2026-03-24) BillingSetupStep.tsx (610 LOC, was 582) — billing mode decoupled from service type (+28 LOC).
  • (2026-03-20) Dashboard components are EXCLUDED (already in msp/dashboard.json):
    • DashboardOnboardingSection.tsx — translated
    • OnboardingChecklist.tsx — translated
    • DashboardOnboardingSlot.tsx — no strings
    • DashboardOnboardingSkeleton.tsx — no strings
  • (2026-03-24) Wizard-only onboarding form/help/alert text should stay in the new msp/onboarding namespace rather than extending msp/dashboard.json. Existing dashboard overlap remains under onboarding.* keys in msp/dashboard.json, but wizard steps do not reuse that namespace today.
  • (2026-03-24) Keep stable onboarding ids/values untranslated: OnboardingStepId, step.id, substep.id, data_import, checklist status enums (complete, in_progress, not_started, blocked), role values (admin, technician, manager, user), billing mode values (fixed, hourly, usage), USD, and ticketing sentinels like none, board, category, status, priority.
  • (2026-03-25) msp/onboarding locale generation needed one post-translation fix in French: billingSetupStep.serviceTypes.import.success.description initially duplicated {{suffix}}, so validate-translations.cjs caught it even after the overall key structure was correct. Keep interpolation-token parity checks in the F034/F091 validations.
  • (2026-03-25) ROUTE_NAMESPACES lives in packages/core/src/lib/i18n/config.ts (re-exported via packages/ui/src/lib/i18n/config.ts). Adding the four new route prefixes there is sufficient for exact-path loads and nested detail routes because getNamespacesForRoute() already falls back to the longest matching prefix.
  • (2026-03-25) Follow-up after T042/T045 sweep: server/public/locales/{xx,yy}/msp/{contacts,onboarding}.json still had legacy pseudo-locale output in a few interpolation-heavy leaves (fill per word instead of the canonical fill + variables + fill shape from scripts/generate-pseudo-locales.cjs). Normalize those four files before closing the plan so the repo matches the documented pseudo-generator contract.
  • (2026-03-25) F030 scaffold created server/public/locales/en/msp/onboarding.json with shared common.actions / common.states plus component-scoped roots for OnboardingProvider, OnboardingWizard, and the six in-scope wizard steps. This mirrors the earlier batch scaffolds: enough stable section names for follow-up wiring, without pretending the full key inventory is known before the large TicketingConfigStep.tsx migration happens.
  • (2026-03-25) TicketingConfigStep.tsx now depends on explicit ticketingConfigStep.statuses.types.{open,closed} keys because the board-scoped status type label is resolved from a computed key rather than a direct string-literal t('...') call. Keep those two keys in mind during later locale generation/audits because simple direct-key scans will not discover them automatically.
  • (2026-03-25) OnboardingWizard.tsx cannot fully localize the shell by itself because packages/ui/src/components/onboarding/WizardNavigation.tsx owns the Back/Skip/Saving/Completing labels. F032 expands that shared component with label props instead of leaving the wizard half-translated.
  • (2026-03-25) OnboardingProvider.tsx is mostly logic, but it does render the blocking "Loading..." state while onboarding status checks are in flight. Treat that spinner copy as in-scope onboarding UI even though the component is otherwise a route gate.

Progress Log

  • (2026-03-24) Completed F001: created server/public/locales/en/msp/clients.json with an initial component-scoped English namespace covering the clients list, client details, quick-add, locations, billing/contracts, tax settings, import flow, and notes panel. This seeds the new msp/clients file so follow-up wiring can land on stable keys instead of inventing ad hoc paths mid-edit. Validation: node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/clients.json','utf8')); console.log('ok')" returned ok.
  • (2026-03-24) Completed F002: wired packages/clients/src/components/clients/Clients.tsx and packages/clients/src/components/clients/ClientDetails.tsx to useTranslation('msp/clients'). The list/detail chrome, main filter controls, view labels, bulk-delete dialogs, core detail tab labels, major field labels, save/delete/reactivate toasts, and deactivate/reactivate confirmations now use t(..., { defaultValue }) while preserving stable tab ids/query-param values. Expanded server/public/locales/en/msp/clients.json with the new list/detail keys needed by those two files. Validation:
    • npx eslint packages/clients/src/components/clients/Clients.tsx packages/clients/src/components/clients/ClientDetails.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-24) Completed F003: wired packages/clients/src/components/clients/QuickAddClient.tsx, packages/clients/src/components/clients/ClientLocations.tsx, and packages/clients/src/components/clients/ClientsImportDialog.tsx to msp/clients. QuickAddClient now translates its section headings, primary labels/placeholders, CTA buttons, and metadata-loading errors; ClientLocations now resolves its existing clients.locations.* keys from the new namespace instead of common; ClientsImportDialog now translates the main import flow headings, action buttons, and confirmation copy. Expanded server/public/locales/en/msp/clients.json with the additional import-flow keys used by these surfaces. Validation:
    • npx eslint packages/clients/src/components/clients/QuickAddClient.tsx packages/clients/src/components/clients/ClientLocations.tsx packages/clients/src/components/clients/ClientsImportDialog.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-24) Completed F004: wired packages/clients/src/components/clients/BillingConfiguration.tsx, packages/clients/src/components/clients/ClientContractAssignment.tsx, packages/clients/src/components/clients/ClientContractLineDashboard.tsx, and packages/clients/src/components/clients/ClientBillingSchedule.tsx to msp/clients. Billing toasts/dialog chrome/tab labels, contract-assignment table/actions/status labels, dashboard card/table headings, and billing-schedule controls/summaries now use t(..., { defaultValue }) while preserving stable tab ids, contract ids, billing cycle enum values, and assignment semantics from the recent refactor. Expanded server/public/locales/en/msp/clients.json with billing, contract-assignment, dashboard, and schedule keys including cycle/month/weekday labels and schedule summary templates. Validation:
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/clients.json','utf8')); console.log('json ok')" returned json ok
    • npx eslint packages/clients/src/components/clients/BillingConfiguration.tsx packages/clients/src/components/clients/ClientContractAssignment.tsx packages/clients/src/components/clients/ClientContractLineDashboard.tsx packages/clients/src/components/clients/ClientBillingSchedule.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-24) Completed F005: wired the remaining non-stub client components to msp/clients, including grid/list surfaces (ClientGridCard, ClientsGrid, ClientsList, ClientCreatedDialog, ClientQuickView, ClientSideDetails) and the billing/settings/helper surfaces (BillingConfigForm, ClientContractDialog, ClientContractLineDisambiguationGuide, ClientCreditExpirationSettings, ClientLanguagePreference, ClientServiceOverlapMatrix, ClientTaxRates, ClientZeroDollarInvoiceSettings, ContractLinePickerDialog, ContractLines, ServiceCatalog, TaxRateCreateForm, TaxSettingsForm, panels/ClientNotesPanel). After the code wiring landed, generated a source-to-locale sync with the Babel AST parser to backfill every still-missing English key referenced by client components into server/public/locales/en/msp/clients.json. Re-export shims PlanPickerDialog.tsx and ClientPlanDisambiguationGuide.tsx were intentionally skipped because they delegate to already-translated implementation files. Validation:
    • AST key audit against all client component t(...) calls reported missing=0 for server/public/locales/en/msp/clients.json
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/clients.json','utf8')); console.log('json ok')" returned json ok
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-24) Completed F006: created server/public/locales/{de,es,fr,it,nl,pl}/msp/clients.json with translated msp/clients content based on the finalized English namespace, then generated server/public/locales/{xx,yy}/msp/clients.json via node scripts/generate-pseudo-locales.cjs (xx=11111, yy=55555). Restored unrelated pseudo-locale noise from previously existing MSP namespaces so this feature commit stays scoped to the new clients namespace. Validation:
    • for f in server/public/locales/{de,es,fr,it,nl,pl,xx,yy}/msp/clients.json; do node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'))" "$f" || exit 1; done returned json-ok
    • node scripts/validate-translations.cjsPASSED (Errors: 0, Warnings: 0)
  • (2026-03-24) Completed F007: ran a targeted Italian accent audit on server/public/locales/it/msp/clients.json using rg -n '\\b(puo|gia|verra|funzionalita|perche|cosi|piu|e necessario|e possibile|e richiesto|e richiesta|e configurato|e configurata)\\b' server/public/locales/it/msp/clients.json. The audit returned no matches, and spot checks of higher-risk translated strings (renewal defaults help, language preference success copy, tax-source help) confirmed accented forms were preserved correctly.
  • (2026-03-24) Completed T001: node scripts/validate-translations.cjs passed after adding the clients namespace across {en,de,es,fr,it,nl,pl,xx,yy}. Summary reported Errors: 0, Warnings: 0, confirming msp/clients key parity across all 9 locale variants.
  • (2026-03-24) Completed T002: cd server && npx tsc -p tsconfig.json --noEmit --pretty false passed after wiring all client component surfaces. The remaining PlanPickerDialog.tsx and ClientPlanDisambiguationGuide.tsx files were confirmed to be re-export shims, so the compile check covers the actual implementation files that render client UI.
  • (2026-03-24) Completed T003: the targeted Italian accent audit for server/public/locales/it/msp/clients.json returned zero matches for the known dropped-accent patterns (puo, gia, verra, funzionalita, e necessario, etc.), so the clients namespace passed the post-translation accent check.
  • (2026-03-25) Completed T004: added server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts as the durable batch-coverage harness for the remaining clients/contacts/assets/onboarding locale QA. For the clients visual-QA checklist item, the new T004 assertion verifies the xx pseudo-locale resolves representative client list/detail/quick-add/import/billing keys to 11111, which gives a repeatable proxy for the manual pseudo-locale smoke test. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts
  • (2026-03-25) Completed T010: exercised the T010 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which checks msp/contacts key parity against English across all 7 production locales plus the 2 pseudo-locales and verifies interpolation variables stay aligned for the production translations. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts -t "T010"
  • (2026-03-25) Completed T011: reran the server TypeScript compile after the contact batch wiring was complete. This is the compile gate for the translated contact list/detail/import flows, the phone-number editor, portal tab, quick-add form, and the notes panel. Validation:
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed T012: exercised the T012 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which enforces the contact Italian accent audit by rejecting the known dropped-accent anti-patterns and checking for representative accented forms. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts -t "T012"
  • (2026-03-25) Completed T013: exercised the T013 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which checks the xx pseudo-locale renders representative contact list/detail/quick-add/phone-editor/portal-tab keys as 11111 for repeatable pseudo-locale QA coverage. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T013"
  • (2026-03-25) Completed T020: exercised the T020 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which checks msp/assets key parity against English across all 7 production locales plus the 2 pseudo-locales and verifies interpolation variables stay aligned for the production translations. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T020"
  • (2026-03-25) Completed T021: reran the server TypeScript compile after the asset batch wiring was complete. This is the compile gate for the translated asset dashboard/detail/form surfaces, drawer, tabs, panels, status/command widgets, and related helpers. Validation:
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed T022: exercised the T022 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which enforces the asset Italian accent audit by rejecting the known dropped-accent anti-patterns and checking for representative accented forms. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T022"
  • (2026-03-25) Completed T023: exercised the T023 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which checks the xx pseudo-locale renders representative asset dashboard/form/detail-drawer/tab/panel keys as 11111 for repeatable pseudo-locale QA coverage. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T023"
  • (2026-03-25) Completed T030: exercised the T030 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which checks msp/onboarding key parity against English across all 7 production locales plus the 2 pseudo-locales and verifies interpolation variables stay aligned for the production translations. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T030"
  • (2026-03-25) Completed T031: reran the server TypeScript compile after the onboarding batch wiring was complete. This is the compile gate for the translated onboarding wizard shell, all six scoped step components, and the onboarding provider wrapper. Validation:
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed T032: exercised the T032 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which enforces the onboarding Italian accent audit by rejecting the known dropped-accent anti-patterns and checking for representative accented forms. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T032"
  • (2026-03-25) Completed T033: exercised the T033 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which checks the xx pseudo-locale renders representative onboarding wizard/step keys as 11111 for repeatable pseudo-locale QA coverage. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T033"
  • (2026-03-25) Completed T034: exercised the T034 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which confirms the finalized msp/onboarding English leaf-key set has zero overlap with msp/dashboard so wizard-specific copy does not collide with the pre-existing dashboard onboarding namespace. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T034"
  • (2026-03-25) Completed T040: exercised the T040 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which verifies ROUTE_NAMESPACES now maps /msp/clients, /msp/contacts, /msp/assets, and /msp/onboarding to their new feature namespaces. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T040"
  • (2026-03-25) Completed T041: reran the repo translation validator after all four namespaces and their locale bundles were registered. The validator reported zero errors and zero warnings across the 6 production locales plus the 2 pseudo-locales. Validation:
    • node scripts/validate-translations.cjsPASSED (Errors: 0, Warnings: 0)
  • (2026-03-25) Completed T042: exercised the T042 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which confirms the xx/yy pseudo-locale files for msp/clients, msp/contacts, msp/assets, and msp/onboarding mirror the English key structure and still expose representative 11111/55555 fill values. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T042"
  • (2026-03-25) Completed T043: reran the full repo production build after the cross-batch locale/test changes were in place. The build completed successfully again, including the AssemblyScript invoice templates, Next.js production build, TypeScript, page-data collection, static page generation, and trace collection. Existing webpack warnings from unrelated dependencies remained non-fatal. Validation:
    • npm run build
  • (2026-03-25) Completed T044: exercised the T044 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which checks the /msp layout still gates locale loading behind the msp-i18n-enabled feature flag and that representative clients/contacts/assets/onboarding components keep defaultValue fallbacks in their t(...) calls so English remains the OFF-state baseline. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T044"
  • (2026-03-25) Completed T045: exercised the T045 vitest assertion in server/src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts, which enforces practical German length thresholds on the specific overflow-sensitive surfaces called out in the PRD: client forms/billing labels, the contact phone-editor/search prompt, asset dashboard/detail labels, and onboarding wizard controls. Validation:
    • cd server && npx vitest run src/test/unit/i18n/mspClientsContactsAssetsOnboardingBatch.test.ts --coverage.enabled=false -t "T045"
  • (2026-03-24) Completed F010: created server/public/locales/en/msp/contacts.json with an initial component-scoped English scaffold for the contacts list, contact detail, import flow, phone editor, portal tab, quick-add form, edit/view helpers, client-embedded list, route shell, loading skeleton, avatar upload, and notes panel. This matches the existing msp/clients namespace style so follow-up wiring can land on stable section names instead of ad hoc keys. Validation: node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/contacts.json','utf8')); console.log('ok')" returned ok.
  • (2026-03-24) Completed F011: wired packages/clients/src/components/contacts/ContactDetails.tsx, packages/clients/src/components/contacts/Contacts.tsx, and packages/clients/src/components/contacts/ContactsImportDialog.tsx to useTranslation('msp/contacts'). The contact list now translates its heading, filter/search chrome, table columns, action menus, delete/inactive flows, and last-phone-type confirmation; the detail view now translates its tab labels, field labels, save/delete/deactivate toasts, back/open actions, and inbound-destination helper text while preserving stable route/tab ids; the import dialog now translates its flow headings, mapping UI, tooltips, confirmation copy, and upload helper text while keeping CSV headers/state ids (upload, mapping, preview, importing, results, complete, unassigned) stable. Expanded server/public/locales/en/msp/contacts.json with the list/detail/import keys used by these files. Validation:
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/contacts.json','utf8')); console.log('contacts json ok')" returned contacts json ok
    • npx eslint packages/clients/src/components/contacts/Contacts.tsx packages/clients/src/components/contacts/ContactDetails.tsx packages/clients/src/components/contacts/ContactsImportDialog.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
    • Perl/Node audit over t('...') calls in the three files reported contacts keys ok for server/public/locales/en/msp/contacts.json
  • (2026-03-24) Completed F012: wired packages/clients/src/components/contacts/ContactPhoneNumbersEditor.tsx, packages/clients/src/components/contacts/ContactPortalTab.tsx, and packages/clients/src/components/contacts/QuickAddContact.tsx to msp/contacts. The phone editor now translates its headings, row actions, phone-type labels, custom-type search prompts, last-usage confirmation, and inline validation copy; the portal tab now translates the client-portal access shell, admin/role/status/invitation history controls, toast copy, and invitation badge labels while keeping invitation status enum values stable; the quick-add contact flow now translates dialog chrome, field labels/placeholders, validation summary/errors, status toggle copy, and success/error toasts, and it now routes phone validation messages through the translated phone-editor helper for consistent copy. Expanded server/public/locales/en/msp/contacts.json with phone-editor, portal-tab, and quick-add keys including dynamic phone-type labels and invitation-status badge labels. Validation:
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/contacts.json','utf8')); console.log('contacts json ok')" returned contacts json ok
    • npx eslint packages/clients/src/components/contacts/ContactPhoneNumbersEditor.tsx packages/clients/src/components/contacts/ContactPortalTab.tsx packages/clients/src/components/contacts/QuickAddContact.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
    • Perl/Node audit over t('...') calls in those three files plus explicit checks for contactPhoneNumbersEditor.phoneTypes.* and contactPortalTab.history.status.{pending,used,expired,revoked} reported contacts F012 keys ok
  • (2026-03-24) Completed F013: wired the remaining contact detail/list surfaces to msp/contacts. packages/clients/src/components/contacts/ContactDetailsEdit.tsx now translates its reflection labels, field labels/placeholders, client/status/help text, save/cancel actions, and validation/save errors while routing phone validation through translateContactPhoneValidationErrors(...); packages/clients/src/components/contacts/ContactDetailsView.tsx now translates its shell actions, field labels, empty states, phone-type labels, status values, documents heading, and client-loading/update errors; packages/clients/src/components/contacts/ClientContactsList.tsx now translates the embedded table headers, filter labels/options, action menu labels, add-contact CTA, and load/retry errors; packages/clients/src/components/contacts/panels/ContactNotesPanel.tsx now translates the notes panel title, save/retry actions, legacy-note heading, load-error title, unknown-error fallback, and last-updated timestamp. ContactsLayout.tsx, ContactAvatarUpload.tsx, and ContactsSkeleton.tsx remain unchanged because they are pure wrappers/placeholders with no local user-facing copy. Expanded server/public/locales/en/msp/contacts.json with the detail/list/panel keys used by these files and corrected scaffold defaults like contactPhoneNumbersEditor.title (Phone Numbers) and contactNotesPanel.title (Notes & Quick Info) to match the rendered UI. Validation:
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/contacts.json','utf8')); console.log('contacts json ok')" returned contacts json ok
    • npx eslint packages/clients/src/components/contacts/ClientContactsList.tsx packages/clients/src/components/contacts/ContactDetailsEdit.tsx packages/clients/src/components/contacts/ContactDetailsView.tsx packages/clients/src/components/contacts/panels/ContactNotesPanel.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
    • key-presence audit over the four files against server/public/locales/en/msp/contacts.json reported contacts F013 keys ok
  • (2026-03-24) Completed F014: created server/public/locales/{de,es,fr,it,nl,pl}/msp/contacts.json with translated msp/contacts content based on the finalized English namespace, then generated server/public/locales/{xx,yy}/msp/contacts.json using the pseudo-locale replacement logic that preserves {{variables}}. The six production locale files were added via parallel worker passes split across {de,es,fr} and {it,nl,pl}; afterwards generated the two pseudo locale files locally so the contact batch has the full 9-locale set required by the PRD. Validation:
    • for f in server/public/locales/{de,es,fr,it,nl,pl}/msp/contacts.json; do node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')); console.log(process.argv[1]+': ok')" "$f" || exit 1; done reported all six locale files as ok
    • node scripts/validate-translations.cjsPASSED (Errors: 0, Warnings: 0)
    • node -e spot check over server/public/locales/{xx,yy}/msp/contacts.json confirmed representative contact keys resolve to 11111 / 55555
  • (2026-03-24) Completed F015: ran a targeted Italian accent audit on server/public/locales/it/msp/contacts.json. The strict dropped-accent scan (pou/gia/verra/funzionalita/perche/cosi/piu, plus the common e necessario-style phrases) returned no matches, and representative spot checks confirmed accented forms are present in the locale file (, è, più). Validation:
    • rg -n '\b(puo|gia|verra|funzionalita|perche|cosi|piu)\b| e necessario| e possibile| e richiesto| e richiesta| e configurato| e configurata' server/public/locales/it/msp/contacts.json returned no matches
    • rg -n 'Sì| è |più|funzionalità' server/public/locales/it/msp/contacts.json returned representative accented strings
  • (2026-03-24) Completed F020: created server/public/locales/en/msp/assets.json as the initial English scaffold for the asset batch. Added shared common.actions / common.states keys plus component-scoped title / description entries for the main asset list/detail/form surfaces, related panels/tabs/shared widgets, and the thin wrapper components that will still hang later t(...) calls off the msp/assets namespace. Intentionally excluded packages/assets/src/components/index.ts, packages/assets/src/components/AssetDetailDrawer.types.ts, and the local asset test files because they are not user-facing locale sources. Validation:
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/assets.json','utf8')); console.log('assets json ok')" returned assets json ok
  • (2026-03-24) Completed F021: wired the three largest asset surfaces to msp/assets. packages/assets/src/components/AssetForm.tsx now translates the edit-form heading, client/location shell, field labels, type-specific form labels, option labels, loading/errors, and save/cancel actions while preserving stable status/type values and route/query parameters. packages/assets/src/components/AssetDashboardClient.tsx now translates the dashboard shell, KPI cards, filter bar, active-filter chips, table headers, row actions, selection banner, drawer error copy, and empty detail fallbacks, with explicit key coverage for asset statuses, types, agent statuses, and column labels. packages/assets/src/components/AssetDetails.tsx now translates the detail-page reflection label, tab labels, field labels, maintenance summary cards, related-asset copy, loading state, and badge/value labels, again keeping internal ids and route segments unchanged. Expanded server/public/locales/en/msp/assets.json with the new assetForm.*, assetDashboardClient.*, and assetDetails.* keys plus enum-backed families (statuses, types, deviceTypes, agentStatuses, columns, relationshipTypes). Validation:
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/assets.json','utf8')); console.log('assets json ok')" returned assets json ok
    • npx eslint packages/assets/src/components/AssetForm.tsx packages/assets/src/components/AssetDashboardClient.tsx packages/assets/src/components/AssetDetails.tsx (3 existing no-explicit-any warnings in AssetDashboardClient.tsx, 0 errors)
    • key-presence audit over the three files against server/public/locales/en/msp/assets.json reported assets F021 keys ok
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F031: wired packages/onboarding/src/components/steps/TicketingConfigStep.tsx to useTranslation('msp/onboarding') and translated the full wizard-step shell, board/category/status/priority/SLA setup copy, action labels, errors, alerts, support-email helper, and ITIL modal while preserving stable ticketing ids and sentinel values. Added ticketingConfigStep.statuses.types.{open,closed} to server/public/locales/en/msp/onboarding.json for the computed status-type label helper. Validation:
    • direct-key audit over TicketingConfigStep.tsx plus manual verification of ticketingConfigStep.statuses.types.{open,closed} reported missing 0
    • npx eslint packages/onboarding/src/components/steps/TicketingConfigStep.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F032: wired packages/onboarding/src/components/steps/BillingSetupStep.tsx, packages/onboarding/src/components/steps/TeamMembersStep.tsx, and packages/onboarding/src/components/OnboardingWizard.tsx to msp/onboarding. Billing setup now translates its service-catalog management/import flows, billing-mode labels, create/delete toasts, and action-required alert; team-members now translates license summaries, validation errors, card labels, and role names; the wizard shell now translates step labels, navigation CTA labels, dialog chrome, and fallback submit/save errors. Extended packages/ui/src/components/onboarding/WizardNavigation.tsx with optional label props so the shell buttons are localizable without changing stable step ids. Validation:
    • direct-key audit over BillingSetupStep.tsx, TeamMembersStep.tsx, OnboardingWizard.tsx, and WizardNavigation.tsx plus manual verification of teamMembersStep.roles.{admin,technician,manager,user} reported missing 0
    • npx eslint packages/onboarding/src/components/steps/BillingSetupStep.tsx packages/onboarding/src/components/steps/TeamMembersStep.tsx packages/onboarding/src/components/OnboardingWizard.tsx packages/ui/src/components/onboarding/WizardNavigation.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F033: wired packages/onboarding/src/components/steps/ClientInfoStep.tsx, packages/onboarding/src/components/steps/AddClientStep.tsx, packages/onboarding/src/components/steps/ClientContactStep.tsx, and packages/onboarding/src/components/OnboardingProvider.tsx to msp/onboarding. The client-info/contact/add-client steps now translate their headings, fields, validation/help copy, optional alerts, and created-state summaries; OnboardingProvider now resolves its loading spinner from msp/onboarding. Added email-validation mapping keys for the client-info step so validator messages still localize cleanly. Validation:
    • direct-key audit over ClientInfoStep.tsx, AddClientStep.tsx, ClientContactStep.tsx, and OnboardingProvider.tsx reported missing 0
    • npx eslint packages/onboarding/src/components/steps/ClientInfoStep.tsx packages/onboarding/src/components/steps/AddClientStep.tsx packages/onboarding/src/components/steps/ClientContactStep.tsx packages/onboarding/src/components/OnboardingProvider.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F034: generated server/public/locales/{de,es,fr,it,nl,pl}/msp/onboarding.json from the finalized English namespace and added pseudo-locales at server/public/locales/{xx,yy}/msp/onboarding.json. A first French pass had correct key structure but duplicated {{suffix}} in billingSetupStep.serviceTypes.import.success.description; after correcting that token mismatch, all six production locales and both pseudo-locales validated cleanly. Validation:
    • key-structure audit against server/public/locales/en/msp/onboarding.json reported missing 0 / extra 0 for de, es, fr, it, nl, and pl
    • for f in server/public/locales/{de,es,fr,it,nl,pl,xx,yy}/msp/onboarding.json; do node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')); console.log(process.argv[1]+': ok')" "$f" || exit 1; done reported all eight files as ok
    • node scripts/validate-translations.cjsPASSED (Errors: 0, Warnings: 0)
  • (2026-03-25) Completed F035: ran the targeted Italian accent audit on server/public/locales/it/msp/onboarding.json. The dropped-accent scan returned no matches, and spot checks confirmed accented forms are present in the translated copy (, è, più, verrà, funzionalità). Validation:
    • rg -n '\b(puo|gia|verra|funzionalita|perche|cosi|piu)\b| e necessario| e possibile| e richiesto| e richiesta| e configurato| e configurata' server/public/locales/it/msp/onboarding.json returned no matches
    • rg -n 'Sì| è |più|funzionalità|perché|così|verrà|già' server/public/locales/it/msp/onboarding.json returned representative accented strings
  • (2026-03-25) Completed F090: updated packages/core/src/lib/i18n/config.ts so /msp/clients, /msp/contacts, /msp/assets, and /msp/onboarding load their new feature namespaces in addition to common and msp/core. This also covers nested detail/edit routes via the existing longest-prefix matcher in getNamespacesForRoute(). Validation:
    • source audit confirmed the four new route prefixes point at msp/clients, msp/contacts, msp/assets, and msp/onboarding
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F091: ran the repo translation validator after all four namespaces (msp/clients, msp/contacts, msp/assets, msp/onboarding) and their locale bundles were in place. This confirmed the cross-batch acceptance criterion of full key parity with no interpolation mismatches across the 6 production locales and 2 pseudo-locales. Validation:
    • node scripts/validate-translations.cjsPASSED (Errors: 0, Warnings: 0)
  • (2026-03-25) Completed F092: reran pseudo-locale generation for msp/clients, msp/contacts, msp/assets, and msp/onboarding using the same replacement rules as scripts/generate-pseudo-locales.cjs, preserving {{variables}} while filling leaf strings with 11111 / 55555. The regenerated files were already in sync, so the pass produced no net diff outside the runbook/plan updates. Validation:
    • targeted generation pass rewrote server/public/locales/{xx,yy}/msp/{clients,contacts,assets,onboarding}.json
    • representative spot checks confirmed xx / yy still resolve those namespaces to 11111 / 55555
  • (2026-03-25) Completed F093: ran the full repo build from the project root. The build completed successfully, including the AssemblyScript invoice templates, server Next.js production build, TypeScript, page-data collection, static page generation, and build traces. Existing webpack warnings from unrelated dependencies remained non-fatal and did not block the build. Validation:
    • npm run build
  • (2026-03-25) Completed F094: normalized server/public/locales/{xx,yy}/msp/{contacts,onboarding}.json to the canonical pseudo-locale generator format used by scripts/generate-pseudo-locales.cjs. This follow-up removes the last legacy fill-per-word remnants in interpolation-heavy strings so pseudo locales now consistently use the repo-standard fill + {{variables}} + fill shape. Validation:
    • node scripts/validate-translations.cjsPASSED (Errors: 0, Warnings: 0)
  • (2026-03-25) Completed F022: audited packages/assets/src/components/AssetDetailDrawerClient.tsx, packages/assets/src/components/AssociatedAssets.tsx, and packages/assets/src/components/QuickAddAsset.tsx and confirmed the branch already had the planned msp/assets wiring. AssetDetailDrawerClient covers drawer tabs, overview/maintenance/ticket/configuration copy, relative-time strings, and type-detail labels; AssociatedAssets covers relationship management, drawer-loading errors, search/add flows, and status/type labels; QuickAddAsset covers dialog chrome, field labels/placeholders, validation copy, and type-specific option labels. Validation:
    • Babel AST key audit over the three files reported AssetDetailDrawerClient=93, AssociatedAssets=33, QuickAddAsset=45, missing=0 against server/public/locales/en/msp/assets.json
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F023: wired the six named asset tab surfaces to msp/assets: packages/assets/src/components/tabs/MaintenanceSchedulesTab.tsx, packages/assets/src/components/tabs/RelatedAssetsTab.tsx, packages/assets/src/components/tabs/ServiceHistoryTab.tsx, packages/assets/src/components/tabs/SoftwareInventoryTab.tsx, packages/assets/src/components/tabs/AuditLogTab.tsx, and packages/assets/src/components/tabs/DocumentsPasswordsTab.tsx. The maintenance tab now translates summary cards, schedule/history tables, status badges, empty states, and delete-confirmation copy; the related-assets tab now translates table/dialog chrome, success/error toasts, and relationship labels; the service-history tab now translates the ticket table shell and empty state; the software-inventory tab now translates search/filter/table chrome plus unknown-value fallbacks; the audit-log tab now translates change-type headings and actor labels; the documents/passwords tab now translates the placeholder secrets panel. Expanded server/public/locales/en/msp/assets.json with the new tab-specific keys and adjusted the existing scaffolded tab title values from placeholder "* Tab" strings to the actual UI labels/templates used at runtime. Validation:
    • AST key audit over the 6 tab files reported totalKeys=85, missing=0 against server/public/locales/en/msp/assets.json
    • npx eslint packages/assets/src/components/tabs/MaintenanceSchedulesTab.tsx packages/assets/src/components/tabs/RelatedAssetsTab.tsx packages/assets/src/components/tabs/ServiceHistoryTab.tsx packages/assets/src/components/tabs/SoftwareInventoryTab.tsx packages/assets/src/components/tabs/AuditLogTab.tsx packages/assets/src/components/tabs/DocumentsPasswordsTab.tsx (2 pre-existing no-explicit-any warnings in RelatedAssetsTab.tsx and SoftwareInventoryTab.tsx, 0 errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F024: wired the five asset panel components to msp/assets: packages/assets/src/components/panels/AssetInfoPanel.tsx, packages/assets/src/components/panels/RmmVitalsPanel.tsx, packages/assets/src/components/panels/HardwareSpecsPanel.tsx, packages/assets/src/components/panels/SecurityPatchingPanel.tsx, and packages/assets/src/components/panels/AssetNotesPanel.tsx. The asset-info panel now translates labels, fallback values, and copy-to-clipboard tooltip text while keeping the copy button id stable; the RMM vitals panel now translates its disconnected state, refresh action, field labels, uptime/network templates, and “never” fallback; the hardware panel now translates CPU/RAM/storage labels plus unknown/free-space templates; the security panel now translates antivirus, patch-status, and firewall copy; the notes panel now translates save/retry/error chrome and the last-updated stamp. Expanded server/public/locales/en/msp/assets.json with panel-specific field/action/value keys and replaced the scaffolded panel title values with the actual rendered headings. Validation:
    • AST key audit over the 5 panel files reported totalKeys=60, missing=0 against server/public/locales/en/msp/assets.json
    • npx eslint packages/assets/src/components/panels/AssetInfoPanel.tsx packages/assets/src/components/panels/RmmVitalsPanel.tsx packages/assets/src/components/panels/HardwareSpecsPanel.tsx packages/assets/src/components/panels/SecurityPatchingPanel.tsx packages/assets/src/components/panels/AssetNotesPanel.tsx (6 pre-existing no-explicit-any warnings across AssetInfoPanel.tsx, RmmVitalsPanel.tsx, and AssetNotesPanel.tsx, 0 errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F025: translated the remaining in-package asset UI surfaces under msp/assets, covering packages/assets/src/components/AssetCommandPalette.tsx, packages/assets/src/components/AssetDetailHeader.tsx, packages/assets/src/components/AssetDetailView.tsx, packages/assets/src/components/AssetDetailTabs.tsx, packages/assets/src/components/AssetMetricsBanner.tsx, packages/assets/src/components/AssetFormClient.tsx, packages/assets/src/components/CreateTicketFromAssetButton.tsx, packages/assets/src/components/DeleteAssetButton.tsx, packages/assets/src/components/RmmStatusIndicator.tsx, packages/assets/src/components/shared/CopyableField.tsx, packages/assets/src/components/shared/StatusBadge.tsx, packages/assets/src/components/shared/UtilizationBar.tsx, and packages/assets/src/components/tabs/CreateMaintenanceScheduleDialog.tsx. The command palette now translates quick-action metadata, headings, empty state, and hint copy; the detail shell now translates the back/nav/menu/tabs/loading/error/banner surfaces; the create-ticket and maintenance-schedule dialogs now translate their form fields, validation errors, and loading placeholders; the delete button, RMM indicator, copyable field, status badge, and utilization bar now translate their shared action/status/fallback text. Expanded server/public/locales/en/msp/assets.json with the corresponding root/shared namespaces (assetCommandPalette, assetDetailHeader, assetDetailTabs, assetDetailView, assetMetricsBanner, createTicketFromAssetButton, deleteAssetButton, rmmStatusIndicator, copyableField, statusBadge, createMaintenanceScheduleDialog) and updated existing scaffold title values where runtime UI now reads from those keys. Validation:
    • AST key audit over the 12 remaining asset files reported totalKeys=136, missing=0 against server/public/locales/en/msp/assets.json
    • npx eslint packages/assets/src/components/AssetFormClient.tsx packages/assets/src/components/AssetCommandPalette.tsx packages/assets/src/components/AssetDetailHeader.tsx packages/assets/src/components/AssetDetailView.tsx packages/assets/src/components/AssetDetailTabs.tsx packages/assets/src/components/AssetMetricsBanner.tsx packages/assets/src/components/CreateTicketFromAssetButton.tsx packages/assets/src/components/DeleteAssetButton.tsx packages/assets/src/components/RmmStatusIndicator.tsx packages/assets/src/components/shared/CopyableField.tsx packages/assets/src/components/shared/StatusBadge.tsx packages/assets/src/components/shared/UtilizationBar.tsx packages/assets/src/components/tabs/CreateMaintenanceScheduleDialog.tsx
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
    • final asset-string sweep (rg over component roots/shared/tabs/panels) only left the intentional ESC keyboard hint plus non-UI type signatures; no remaining user-facing English literals were found in the package-owned asset components
  • (2026-03-25) Completed F026: created server/public/locales/{de,es,fr,it,nl,pl}/msp/assets.json from the finalized English namespace using two parallel worker passes split across {de,es,fr} and {it,nl,pl}. Then generated server/public/locales/{xx,yy}/msp/assets.json locally from en/msp/assets.json with the targeted pseudo-locale transform that preserves {{variables}}. Validation:
    • for f in server/public/locales/{de,es,fr,it,nl,pl}/msp/assets.json; do node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')); console.log(process.argv[1]+': ok')" "$f" || exit 1; done reported all six production locale files as ok
    • spot check over server/public/locales/{xx,yy}/msp/assets.json confirmed representative keys resolve to 11111 / 55555 while preserving interpolation tokens (createTicketFromAssetButton.defaultTitle11111 {{name}} 11111, 55555 {{name}} 55555)
    • node scripts/validate-translations.cjsPASSED (Errors: 0, Warnings: 0)
  • (2026-03-25) Completed F027: ran the targeted Italian dropped-accent audit on server/public/locales/it/msp/assets.json. The strict scan for the known broken forms (puo, gia, verra, funzionalita, perche, cosi, piu, plus the common e necessario / e possibile phrases) returned no matches, and representative spot checks confirmed accented strings exist in the asset locale file (, è, già, obbligatorio copy with accents preserved where applicable). Validation:
    • rg -n '\b(puo|gia|verra|funzionalita|perche|cosi|piu)\b| e necessario| e possibile| e richiesto| e richiesta| e configurato| e configurata' server/public/locales/it/msp/assets.json returned no matches
    • rg -n 'Sì| è |più|funzionalità|perché|così|già|verrà' server/public/locales/it/msp/assets.json | head -n 20 returned representative accented strings
  • (2026-03-25) Completed F030: created server/public/locales/en/msp/onboarding.json as the initial English scaffold for the onboarding batch. Added shared common.actions / common.states keys plus component-scoped title / description entries for OnboardingProvider, OnboardingWizard, AddClientStep, BillingSetupStep, ClientContactStep, ClientInfoStep, TeamMembersStep, and TicketingConfigStep. Dashboard onboarding components remain intentionally excluded because they already live under msp/dashboard.json. Validation:
    • node -e "JSON.parse(require('fs').readFileSync('server/public/locales/en/msp/onboarding.json','utf8')); console.log('onboarding json ok')" returned onboarding json ok
  • (2026-03-25) Completed F031: wired packages/onboarding/src/components/steps/TicketingConfigStep.tsx to useTranslation('msp/onboarding'). The step now translates its loading/header shell, ticket-numbering fields, board/category/status/priority section chrome, import/add flows, default/delete toasts and fallback validation copy, the support-email requirement block, and the full ITIL standards modal including category lists and the priority matrix. Expanded server/public/locales/en/msp/onboarding.json with the step-specific English keys and added explicit ticketingConfigStep.statuses.types.{open,closed} entries for the computed board-scoped status type labels. Validation:
    • direct-key locale audit over TicketingConfigStep.tsx plus manual checks for ticketingConfigStep.statuses.types.{open,closed} reported missing=0 against server/public/locales/en/msp/onboarding.json
    • npx eslint packages/onboarding/src/components/steps/TicketingConfigStep.tsx (warnings only, no errors; existing any / hook-deps warnings remain)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F032: wired packages/onboarding/src/components/steps/BillingSetupStep.tsx, packages/onboarding/src/components/steps/TeamMembersStep.tsx, and packages/onboarding/src/components/OnboardingWizard.tsx to msp/onboarding, and extended packages/ui/src/components/onboarding/WizardNavigation.tsx with configurable label props so the wizard shell can translate Back/Skip/Next/Finish/Saving/Completing copy. BillingSetupStep now translates the service form, service-type management/import flows, billing-mode labels, delete/create toasts, and the action-required validation block; TeamMembersStep now translates the invite form, license/limit alerts, save validation errors, role labels, button states, and optional/unsaved warnings; OnboardingWizard now translates step names, shell titles, debug labels, and fallback error messages for each persisted step. Expanded server/public/locales/en/msp/onboarding.json with the billing/team-members/wizard keys plus explicit teamMembersStep.roles.{admin,technician,manager,user} entries for the computed role-label mapping. Validation:
    • direct-key locale audit across BillingSetupStep.tsx, TeamMembersStep.tsx, and OnboardingWizard.tsx plus manual checks for teamMembersStep.roles.{admin,technician,manager,user} reported missing=0 against server/public/locales/en/msp/onboarding.json
    • npx eslint packages/onboarding/src/components/steps/BillingSetupStep.tsx packages/onboarding/src/components/steps/TeamMembersStep.tsx packages/onboarding/src/components/OnboardingWizard.tsx packages/ui/src/components/onboarding/WizardNavigation.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false
  • (2026-03-25) Completed F033: wired the remaining onboarding surfaces to msp/onboarding: packages/onboarding/src/components/steps/ClientInfoStep.tsx, packages/onboarding/src/components/steps/AddClientStep.tsx, packages/onboarding/src/components/steps/ClientContactStep.tsx, and packages/onboarding/src/components/OnboardingProvider.tsx. ClientInfoStep now translates both the first-run and revisit shells, password reset/strength requirements, password match states, and maps shared email-validation messages into onboarding keys so the step does not show mixed-language validation copy; AddClientStep now translates its header, validation errors, field labels/placeholders, success state, and tax/optional helper note; ClientContactStep now translates its empty-state, success copy, field labels/placeholders, and optional helper alert; OnboardingProvider now translates the blocking loading spinner shown while tenant onboarding status is checked. Expanded server/public/locales/en/msp/onboarding.json with the remaining client-info/add-client/client-contact/provider keys. Validation:
    • direct-key locale audit across ClientInfoStep.tsx, AddClientStep.tsx, ClientContactStep.tsx, and OnboardingProvider.tsx reported missing=0 against server/public/locales/en/msp/onboarding.json
    • npx eslint packages/onboarding/src/components/steps/ClientInfoStep.tsx packages/onboarding/src/components/steps/AddClientStep.tsx packages/onboarding/src/components/steps/ClientContactStep.tsx packages/onboarding/src/components/OnboardingProvider.tsx (warnings only, no errors)
    • cd server && npx tsc -p tsconfig.json --noEmit --pretty false

Commands / Runbooks

Validation

node scripts/validate-translations.cjs
npm run build

Pseudo-locale generation

cat << 'SCRIPT' | node - server/public/locales/en/msp/<name>.json 11111
const fs = require("fs");
const fill = process.argv[3];
const transform = (obj) => {
  const out = {};
  for (const [k, v] of Object.entries(obj)) {
    out[k] = typeof v === "object" && v !== null ? transform(v) : fill;
  }
  return out;
};
const src = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
console.log(JSON.stringify(transform(src), null, 2));
SCRIPT

Italian accent audit

for ns in clients contacts assets onboarding; do
  echo "=== $ns ===";
  grep -n ' e [a-z]\| puo \| gia \| verra \| funzionalita\| necessario' server/public/locales/it/msp/$ns.json 2>/dev/null || echo "(file not found)";
done
  • Translation plan: .ai/translation/MSP_i18n_plan.md
  • Translation guide: .ai/translation/translation-guide.md
  • Previous plans: docs/plans/2026-03-19-msp-i18n-batch-2b1-core/, docs/plans/2026-03-20-msp-i18n-dispatch-reports-admin-time/

Key directories

Directory Files Sub-batch
packages/clients/src/components/clients/ 32 2b-10
packages/clients/src/components/contacts/ 13 2b-11
packages/assets/src/components/ 39 2b-12
packages/onboarding/src/components/steps/ 7 2b-16
packages/onboarding/src/components/OnboardingWizard.tsx 1 2b-16

Open Questions

  • Client portal overlap: Do any client/contact components render in the client portal? If so, those strings should go in features/*.json (shared) not msp/*.json.
  • Asset EE components: Are there EE-only asset features that live elsewhere?
  • TicketingConfigStep decomposition: At 2,920 LOC, should this be broken into sub-sections in the namespace, or is a flat structure fine?
  • Billing terminology sync: Client billing config uses contract/invoice/tax terms. Cross-check against msp/contracts and msp/invoicing namespaces for consistency.