Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
14 KiB
PRD — MSP i18n Batch 2b-21b/c: Projects + Project Templates Migration
- Slug:
2026-04-05-msp-i18n-projects-migration - Date:
2026-04-05 - Status: Draft
- Parent plan:
.ai/translation/MSP_i18n_plan.md(Batches 2b-21b + 2b-21c) - Sibling plan:
ee/docs/plans/2026-04-05-msp-i18n-tickets-migration/(2b-21a)
Summary
Wire the existing features/projects namespace into ~60 unwired MSP components in
packages/projects/src/components/ (projects proper + project-templates subdirectory).
Infrastructure is already in place: features/projects.json (128 keys across 9 locales)
exists, templates.* subtree is live, and ROUTE_NAMESPACES['/msp/projects'] already
loads it. 5 of 65 production components are wired; ~60 remain. Consolidates what the
parent plan called 2b-21b (projects) and 2b-21c (project-templates) into one effort
because they share the same package, namespace, and patterns.
Problem
MSP users see hardcoded English across the entire project management module — the main
projects list, project detail page, task forms, kanban board, project templates, wizard
steps, and task dialogs. Only 5 of 65 production components (8%) are wired. Because
ROUTE_NAMESPACES['/msp/projects'] already loads features/projects, the translation
assets are downloaded but unused. Client-portal project views are 100% wired (9/9), so
this is the last major gap in project-related UI.
Project templates were originally considered for a separate features/project-templates
namespace, but analysis confirmed that the existing features/projects.json templates.*
subtree (17 keys) is already used by wired components (TemplateStatusColumnsStep,
TemplateStatusManager) and should be extended rather than split.
Goals
- Wire
useTranslation(['features/projects', 'common'])into all unwired production MSP project components (projects proper + project-templates) - Extend
features/projects.jsonwith MSP-specific keys missing from the current namespace (task form, task dependencies, project detail tabs, kanban, materials, export, template editor, template wizards, dialogs) - Regenerate pseudo-locales via script; validate all 9 locales pass the validator
- Preserve 100% test pass rate and zero user-facing regressions
- Measurable: MSP projects coverage 8% → 100%, project-templates coverage 12% → 100%
Non-goals
- Creating a new
features/project-templates.jsonnamespace — templates.* lives in features/projects.json per existing pattern - Retranslating existing 128 keys — 7-language coverage already exists
- Translating project/task content (names, descriptions, comments) — those are tenant data
- Wiring test files (
.test.tsx) or test helpers - Translating client-portal project views — already complete (9/9)
- Extending shared
features/projectsfor client-portal-specific needs not surfaced on MSP - Translating EE-only project components beyond what
packages/projectsexports
Users and Primary Flows
Primary user: MSP project managers, technicians, and dispatchers using non-English UI language (any of fr, es, de, nl, it, pl).
Primary flows affected:
/msp/projects— list page, filters, quick-add/msp/projects/[id]— project detail, phases, tasks, kanban, materials, task forms/msp/projects/templates— template list, categories filter/msp/projects/templates/[templateId]— template editor, task builder, phase manager/msp/projects/templates/create— template creation wizard (4-5 steps)/msp/settings/project-settings— ProjectSettings, ProjectStatusSettings, TenantProjectTaskStatusSettings, TaskPrioritySettings, AddStatusDialog- Quick-create dialog (global nav) —
ProjectQuickAddreused inlayout/QuickCreateDialog
UX / UI Notes
- No visual changes. Text replaced inline via
t('key', 'English fallback'). - Use multi-namespace form:
useTranslation(['features/projects', 'common'])— matches existing wired components (TemplateStatusColumnsStep,TemplateStatusManager,ProjectTaskStatusSettings). - Common strings (Cancel, Save, Delete, Confirm) come from
commonnamespace; scoped domain strings fromfeatures/projects. - Toast messages, inline validation, confirmations all translated.
- Task form is the largest single surface (~39 strings, 2024 LOC) — split mental model by tab (details, dependencies, documents, ticket links, comments, materials).
Requirements
Functional Requirements
Sub-batch A: Projects — core detail, task form, list (15 files, ~250 strings)
High-traffic daily-use components. Ship first.
| Component | LOC | Est. strings | Key content |
|---|---|---|---|
| ProjectDetail.tsx | 3,038 | ~50 | Project detail page tabs, phases, tasks, budget/hours, kanban controls |
| TaskForm.tsx | 2,024 | ~39 | Task create/edit form, fields, validation, toast messages |
| PhaseTaskImportDialog.tsx | 1,290 | ~21 | Task import dialog, CSV preview, validation errors |
| Projects.tsx | 980 | ~21 | Project list, filters (status/search/deadline), empty state |
| TaskDocumentsSimple.tsx | 856 | ~21 | Task documents panel, upload, attach, download |
| TaskTicketLinks.tsx | 816 | ~21 | Task-ticket linking UI, create/unlink, search |
| TaskDependencies.tsx | 668 | ~21 | Dependency graph, add/remove, blocking indicators |
| ProjectMaterialsDrawer.tsx | 439 | ~19 | Materials list, add/edit/remove, cost labels |
| ProjectTaskExportDialog.tsx | 283 | ~18 | Export format, column picker, toast messages |
| ProjectQuickAdd.tsx | 454 | ~15 | Quick-add project dialog (reused in QuickCreate) |
| ProjectDetailsEdit.tsx | 557 | ~13 | Project detail edit form |
| PrefillFromTicketDialog.tsx | 419 | ~10 | Prefill task from ticket, field mapping |
| TaskListView.tsx | 1,320 | ~9 | Task list table, columns, inline edit |
| TaskCard.tsx | 630 | ~7 | Kanban task card, quick-actions menu |
| PhaseQuickAdd.tsx | 141 | ~7 | Quick-add phase inline |
Sub-batch B: Project Templates (15 files, ~150 strings)
Template creation, editing, wizard steps, apply flow.
| Component | LOC | Est. strings | Key content |
|---|---|---|---|
| TemplateEditor.tsx | 2,315 | ~28 | Template editor, phases, tasks, save/publish |
| TemplateTaskForm.tsx | 1,015 | ~22 | Template task create/edit form |
| wizard-steps/TemplateTasksStep.tsx | 576 | ~20 | Wizard step 3 — tasks |
| ApplyTemplateDialog.tsx | 396 | ~20 | Apply template to project dialog |
| ProjectTemplatesList.tsx | 308 | ~12 | Template list page, columns, filters, delete confirm |
| CreateTemplateDialog.tsx | 267 | ~11 | Create template from project dialog |
| TemplateTaskListView.tsx | 953 | ~8 | Template task list view |
| wizard-steps/TemplateReviewStep.tsx | 290 | ~7 | Wizard review step |
| wizard-steps/TemplatePhasesStep.tsx | 399 | ~6 | Wizard phases step |
| CreateTemplateForm.tsx | 140 | ~6 | Template create form body |
| TemplateDetail.tsx | 308 | ~4 | Template detail sidebar |
| wizard-steps/TemplateClientPortalStep.tsx | 45 | ~2 | Client portal exposure toggle |
| TemplateCreationWizard.tsx | 360 | ~1 | Wizard shell |
| wizard-steps/TemplateBasicsStep.tsx | 81 | ~1 | Wizard basics step |
| AddTemplateDialog.tsx | 30 | 0 | Re-export shim |
Sub-batch C: Settings + small/utility components (30 files, ~80-100 strings)
Low-string-count cleanup pass.
| Component | LOC | Est. strings | Notes |
|---|---|---|---|
| settings/projects/TenantProjectTaskStatusSettings.tsx | 642 | ~11 | Tenant-level status defaults |
| settings/projects/ProjectStatusSettings.tsx | 442 | ~10 | Project status config |
| ProjectTaskStatusEditor.tsx | 350 | ~7 | Inline status editor |
| CreateTaskFromTicketDialog.tsx | 274 | ~5 | Create task from ticket |
| LinkTicketToTaskDialog.tsx | 221 | ~5 | Link ticket to task |
| DeadlineFilter.tsx | 165 | ~5 | Deadline filter dropdown |
| settings/ProjectSettings.tsx | 86 | ~5 | Top-level project settings page |
| ProjectTaskStatusSelector.tsx | 371 | ~4 | Status select dropdown |
| MoveTaskDialog.tsx | 126 | ~4 | Move task to phase dialog |
| ProjectInfo.tsx | 254 | ~3 | Project info card |
| DuplicateTaskDialog.tsx | 216 | ~3 | Duplicate task dialog |
| TicketLinkedTasksBadge.tsx | 160 | ~3 | Badge showing linked tasks |
| settings/projects/AddStatusDialog.tsx | 111 | ~2 | Add status dialog |
| ProjectPhases.tsx | 280 | ~1 | Phases section wrapper |
| TaskStatusSelect.tsx | 156 | ~1 | Task status select |
| TicketSelect.tsx | 136 | ~1 | Ticket select |
| TaskTypeSelector.tsx | 54 | ~1 | Task type selector |
| Remaining zero-string files (~13) | varies | 0 | Confirm zero strings: StatusColumn, TaskCommentThread, KanbanBoard, ClientPortalConfigEditor, ProjectPage, KanbanZoomControl, TaskCommentForm, DonutChart, TaskQuickAdd, TaskEdit, HoursProgressBar, ProjectActiveToggle, TaskPrioritySettings |
Namespace key gaps to fill (preliminary):
Current features/projects.json covers project list and basic detail. Likely MSP gaps:
taskForm.*— full task create/edit form (fields, validation, placeholders)taskDependencies.*— dependency UI, blocking indicators, cycle detectiontaskDocuments.*— attach/upload/download controls (may overlap withfeatures/documents)taskTicketLinks.*— link/unlink task-ticket, searchprojectDetail.*— tabs, budget/hours display, phase managementkanban.*— column headers, drag helpers, zoom controlmaterials.*— materials drawer, add/edit, costsexport.*— export dialog labels (reuse features/tickets export patterns if possible)quickAdd.*— project quick-add dialog labelsdialogs.*— move/duplicate/create-from-ticket dialogsimport.*— task import CSV flowtemplates.editor.*— template editor-specific stringstemplates.wizard.*— wizard step titles, nav buttons, review summarytemplates.apply.*— apply-template dialogtemplates.list.*— template list page (columns, filters, delete)templates.taskForm.*— template task form (may share withtaskForm.*)settings.statuses.*— already partially present; extend for tenant/project levelsfilters.deadline.*— deadline filter dropdown options
Final gap list determined during implementation — run the lang-pack loop to surface missing keys.
Non-functional Requirements
- No regressions: all existing project-related tests pass after migration
- Lang-pack validation: after every
en/features/projects.jsonedit, runnode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjsand commit only when green. Single unified check covers key parity, pseudo fill patterns, Italian accent preservation, and{{variable}}preservation. Never hand-editxx/oryy/pseudo-locale files. - Naming convention: follow existing
features/projects.jsonpatterns (camelCase, nested under semantic groups); reuse existing keys where possible before adding new ones - Fallback-safe: all
t()calls uset('key', 'English fallback')signature - Multi-namespace: use
useTranslation(['features/projects', 'common'])array form to match existing wired components; prefercommonfor generic actions (save/cancel/delete) - Shared with client portal: before adding a key, check if existing
features/projectskey covers it — client-portal uses the same namespace
Data / API / Integrations
- No database changes
- No API changes
- No new npm dependencies
- Reuses existing
useTranslationfromreact-i18next(as used in wired components) - Reuses existing i18next infrastructure loaded via
I18nWrapper(already in MSP layout)
Security / Permissions
No change. Translation is a pure presentation-layer concern.
Observability
N/A.
Rollout / Migration
- No feature-flag gating needed —
I18nWrapperforces English fallback whenmsp-i18n-enabledis off - Ship sub-batches A/B/C as independent PRs
- Translations are static JSON served from
server/public/locales/; no cache invalidation beyond standard Next.js static-asset rebuild - Each PR is independently revertable; components continue rendering English via
defaultValuefallbacks even if keys are reverted
Open Questions
ROUTE_NAMESPACESdoes not include/msp/projects/templatesor/msp/projects/templates/[templateId]or/msp/projects/templates/create. The best-match fallback to/msp/projectsshould loadfeatures/projectstransitively. Action: verify via integration test T108; if not loading, add explicit route entries.- Should export dialog (
ProjectTaskExportDialog) reusefeatures/ticketsexport keys or have its ownfeatures/projects.export.*? Tentative answer: own section underfeatures/projects— avoids cross-namespace coupling, patterns may diverge. TaskDocumentsSimple— should document strings live infeatures/documentsorfeatures/projects.taskDocuments.*? Tentative answer: reusefeatures/documentswhere keys match (already loaded for/msp/projectsvia transitive namespace? — verify), addfeatures/projects.taskDocuments.*only for project-task-specific copy.- Templates wizard has 5 steps with different content — should wizard strings be flat
under
templates.wizard.*or nested per-step (templates.wizard.basics.*,templates.wizard.tasks.*)? Tentative answer: nested per-step, matches existing onboarding wizard patterns inmsp/onboarding.json.
Acceptance Criteria (Definition of Done)
- All ~60 unwired production MSP project + project-template components either
(a) import
useTranslation(['features/projects', 'common'])and wrap all user-visible strings, or (b) are confirmed zero-string (re-exports, style-only files, kanban layout components) features/projects.jsoncontains all keys referenced by MSP project componentsnode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjsexits 0- All existing project-related unit/integration tests pass
- Visual smoke test:
/msp/projects,/msp/projects/[id],/msp/projects/templates,/msp/projects/templates/[templateId],/msp/projects/templates/create,/msp/settings/project-settingsrender correctly inenand at least one non-English locale (de or fr);xxpseudo-locale shows pseudo-text for every visible string (no bare English leakage) - Global quick-create dialog (
ProjectQuickAddreused inlayout/QuickCreateDialog) renders translated in non-English locale - Parent plan
.ai/translation/MSP_i18n_plan.mdupdated: sub-batches 2b-21b and 2b-21c marked ✅ with final string counts