# Scratchpad — MSP i18n Batch 2b-21b/c: Projects + Project Templates Migration - Plan slug: `2026-04-05-msp-i18n-projects-migration` - Created: `2026-04-05` - **Status: ✅ COMPLETE** (2026-04-08) ## What This Is Mechanical wiring pass: ~60 unwired MSP project + project-template components × `useTranslation(['features/projects', 'common'])`. Shared namespace (128 keys, 9 locales) already exists and is already loaded by `ROUTE_NAMESPACES['/msp/projects']`. The `templates.*` subtree (17 keys) is live. Client-portal side is 100% wired (9/9) — this closes the MSP gap. Consolidates parent plan's 2b-21b (projects, 45 files) and 2b-21c (project-templates, 15 files) because they share package, namespace, and patterns. ## Decisions - **(2026-04-05)** Keep project-templates strings in existing `features/projects.json` under `templates.*` subtree rather than creating a new `features/project-templates.json` namespace. Rationale: already-wired components (`TemplateStatusColumnsStep`, `TemplateStatusManager`, `ProjectTaskStatusSettings`) use `features/projects` with `templates.*` keys and it's working. Splitting would fragment the namespace needlessly. Supersedes the parent plan's tentative "new `features/project-templates` namespace" idea. - **(2026-04-05)** Use array multi-namespace form: `useTranslation(['features/projects', 'common'])`. Matches all 5 already-wired components. Generic actions (Save/Cancel/Delete/Confirm) pull from `common` via `common:` prefix. - **(2026-04-05)** Ship sub-batches A (projects core, 15 files), B (project-templates, 15 files), C (settings + small, 30 files) as independent PRs. - **(2026-04-05)** Template wizard strings nested per-step (`templates.wizard.basics.*`, `templates.wizard.tasks.*`, etc.) rather than flat. Matches existing onboarding wizard pattern in `msp/onboarding.json`. - **(2026-04-05)** Task document strings — prefer reusing `features/documents` keys where they match (upload/download/attach/remove). Only add `features/projects.taskDocuments.*` for project-task-specific copy. Verify `features/documents` is loaded transitively on `/msp/projects/[id]` routes. - **(2026-04-05)** Translate toast messages, inline validation, and user-visible error strings. Do NOT translate `throw new Error('...')` strings caught by error boundaries or logged only. ## Discoveries / Constraints - **(2026-04-05)** `features/projects.json` top-level groups: title, subtitle, searchPlaceholder, allStatuses, resetFilters, active, completed, onHold, timeline, milestones, phasesAndTasks, kanbanView, listView, task, tasks (15), phases (7), settings (22), templates (17), documents (13), team, budget, fields (12), status (5), messages (5), backToProjects, invalidProjectData, plus ~16 leaf fields. Total: 128 keys. - **(2026-04-05)** `ROUTE_NAMESPACES` entries that load `features/projects`: - `/client-portal/projects` — already works - `/msp/projects` — loads `['common', 'msp/core', 'features/projects']` - `/msp/settings` — loads `['common', 'msp/core', 'msp/settings', 'msp/admin', 'msp/email-providers', 'features/projects']` (already includes it!) - `/msp/billing` — loads `['common', 'msp/core', 'features/billing', 'msp/reports']` (does not include projects — if any project component is rendered on billing, fix needed) - **(2026-04-05)** Templates routes NOT in ROUTE_NAMESPACES: - `/msp/projects/templates` - `/msp/projects/templates/[templateId]` - `/msp/projects/templates/create` Should match-best against `/msp/projects` and inherit its namespaces. Verify. - **(2026-04-05)** Already-wired MSP project components (reference patterns): - `PhaseListItem.tsx` → `useTranslation('features/projects')` (single-namespace form) - `TemplateStatusManager.tsx` → `useTranslation(['features/projects', 'common'])` (array form) - `TemplateStatusColumnsStep.tsx` → `useTranslation(['features/projects', 'common'])` - `ProjectTaskStatusSettings.tsx` → `useTranslation(['features/projects', 'common'])` - `TaskComment.tsx` → `useTranslation('common')` **Preferred: array form** — matches 3 of 5, supports `common:` prefix for shared keys. - **(2026-04-05)** Largest files dominate the string count: - `ProjectDetail.tsx` 3,038 LOC / ~50 strings - `TemplateEditor.tsx` 2,315 LOC / ~28 strings - `TaskForm.tsx` 2,024 LOC / ~39 strings - `TaskListView.tsx` 1,320 LOC / ~9 strings - `PhaseTaskImportDialog.tsx` 1,290 LOC / ~21 strings - `TemplateTaskForm.tsx` 1,015 LOC / ~22 strings - `Projects.tsx` 980 LOC / ~21 strings - `TemplateTaskListView.tsx` 953 LOC / ~8 strings These 8 files alone are ~12,900 LOC and ~198 strings of the estimated ~450 total. - **(2026-04-05)** Rough string estimates (heuristic undercount): - Sub-batch A (projects core): 15 files, ~250 strings - Sub-batch B (project-templates): 15 files, ~150 strings - Sub-batch C (settings + small): 30 files, ~80-100 strings - **Total: ~480 strings** (realistic: 500-650) - **(2026-04-05)** `ProjectQuickAdd` reused in global quick-create: `server/src/components/layout/QuickCreateDialog.tsx`. Must work in both contexts. - **(2026-04-05)** Zero-string components to verify: StatusColumn, TaskCommentThread, KanbanBoard, ClientPortalConfigEditor, ProjectPage, KanbanZoomControl, TaskCommentForm, DonutChart, TaskQuickAdd, TaskEdit, HoursProgressBar, ProjectActiveToggle, TaskPrioritySettings. Most are layout/style-only or re-export shims. - **(2026-04-05)** `ProjectSettings` is exported from `@alga-psa/projects/components` and imported by `server/src/components/settings/SettingsPage.tsx` (Settings page wiring). - **(2026-04-07, F001 audit)** Existing `features/projects` keys are enough for: base projects list chrome (`title`, `subtitle`, `searchPlaceholder`, `allStatuses`, `resetFilters`), generic project fields (`fields.*`), summary cards (`taskCompletion`, `budgetHours`, `hoursUsage`, etc.), base task table headers (`tasks.*`), phase shell (`phases.*`), attachments shell (`documents.*`), template status-column management (`templates.statuses.*`), and project/phase status settings (`settings.statuses.*`). - **(2026-04-07, F001 audit)** Confirmed missing MSP key groups for sub-batch A: `projectList.*` (filters, empty-state CTAs, row actions, deletion toasts), `quickAdd.*`, `projectDetail.*` (header actions, tabs, metrics, phase actions, search), `taskForm.*` (field labels/placeholders, validation, checklist/deletion/move/duplicate confirmations, prefill copy), `taskDependencies.*`, `taskTicketLinks.*`, `materials.*`, `export.*`, `import.*`, `dialogs.*`, and `filters.deadline.*`. - **(2026-04-07, F001 audit)** Confirmed missing MSP key groups for sub-batch B: `templates.list.*`, `templates.create.*`, `templates.apply.*`, `templates.detail.*`, `templates.editor.*`, `templates.taskForm.*`, and `templates.wizard.*` with nested per-step groups (`basics`, `phases`, `tasks`, `review`, `clientPortal`). - **(2026-04-07, F001 audit)** Confirmed missing MSP key groups for sub-batch C: `settings.statuses.tenant.*` / project-settings page copy, plus small dialog/filter leaf keys reused by `CreateTaskFromTicketDialog`, `LinkTicketToTaskDialog`, `MoveTaskDialog`, `DuplicateTaskDialog`, `ProjectInfo`, `ProjectTaskStatusSelector`, `ProjectPhases`, `TaskStatusSelect`, `TicketSelect`, and `TaskTypeSelector`. - **(2026-04-07, F001 audit)** Reuse decisions from current inventory: keep attachments copy under existing `documents.*` where strings match; add `taskDocuments.*` only for task-specific actions if required. Keep shared generic buttons in `common`. Reuse `tasks.*`, `fields.*`, `phases.*`, and `status.*` before adding narrower keys. Keep all template-related strings in `features/projects` under `templates.*`. - **(2026-04-07, F001 audit)** Representative concrete gaps seen in code: `Projects.tsx` needs translations for `Projects`, search/filter placeholders, `Open menu`, and delete success toast. `ProjectQuickAdd.tsx` / `ProjectDetailsEdit.tsx` need full form labels, placeholders, unsaved/save confirmation copy, and portal visibility label. `TaskDependencies.tsx` lacks keys for section title, dependency editor actions, and task picker placeholders. `TaskTicketLinks.tsx` lacks keys for duplicate/invalid ticket toasts, section title, link/create dialog labels, and ticket search filters. `TaskDocumentsSimple.tsx` lacks keys for auth/validation toasts, create/upload/link buttons, remove actions, document-name placeholder, and unsaved-change dialog. `PhaseTaskImportDialog.tsx` needs a large `import.*` subtree for CSV instructions, mapping labels, preview stats, unmatched agents/statuses, and completion summaries. - **(2026-04-07, F002)** Expanded `server/public/locales/en/features/projects.json` from 128 leaf keys to 665 leaf keys. Added new top-level groups: `projectList`, `quickAdd`, `edit`, `projectDetail`, `taskForm`, `taskDependencies`, `taskTicketLinks`, `taskDocuments`, `materials`, `export`, `import`, `dialogs`, `filters`, `projectInfo`, `projectPhases`, `selectors`, plus large `settings.*` and `templates.*` extensions. - **(2026-04-07, F002)** Chose pragmatic scaffolding over late piecemeal key creation: the English namespace now contains concrete fallbacks for all plan groups, including template list/create/apply/detail/editor/wizard surfaces and project-settings/task- status-library surfaces. Later component wiring can mostly reuse these keys and only add narrowly missing leaves if a file surfaces unexpected copy. - **(2026-04-07, F003)** Propagated the expanded `features/projects` tree into `fr/es/de/nl/it/pl` by deep-merging each existing locale over the new English source. Result: all six real locales now preserve their pre-existing translated values while gaining the full expanded key set for parity with English. Newly introduced leaves that did not previously exist are currently seeded from English; this keeps validation and wiring unblocked and preserves the prior human translations intact. - **(2026-04-07, F004)** Ran `node scripts/generate-pseudo-locales.cjs` after the namespace expansion. Regenerated `xx/features/projects.json` and `yy/features/projects.json`; the run also refreshed `xx/common.json` because the pseudo generator rewrites every pseudo-locale file from current English sources in one pass. - **(2026-04-07, validation)** `node scripts/validate-translations.cjs` passes after the locale propagation + pseudo generation (8 locales checked, 0 errors, 0 warnings). - **(2026-04-07, F005)** Verified template-route namespace loading with the actual resolver via `node_modules/.bin/tsx -e ...getNamespacesForRoute(...)`. Results: `/msp/projects/templates` → `["common","msp/core","features/projects"]` `/msp/projects/templates/123` → `["common","msp/core","features/projects"]` `/msp/projects/templates/create` → `["common","msp/core","features/projects"]` No explicit `ROUTE_NAMESPACES` entries are needed; longest-prefix matching against `/msp/projects` already loads `features/projects` correctly. - **(2026-04-07, F020)** Wired `packages/projects/src/components/ProjectDetail.tsx` to `useTranslation(['features/projects', 'common'])`. Translated the project-detail header/view controls, search/filter chrome, sticky-status/pin controls, selected-phase completion summary, empty-state guidance, confirmation dialogs, and the main toast copy for task/phase move/update/delete/import flows. - **(2026-04-07, F020)** Added the missing `projectDetail.*` leaves required by the `ProjectDetail.tsx` wiring to English, re-synced `fr/es/de/nl/it/pl` via the merge script, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F020 check)** `node_modules/.bin/eslint packages/projects/src/components/ProjectDetail.tsx` passes with pre-existing warnings only (no new lint errors introduced by the i18n wiring). - **(2026-04-07, F021)** Wired `packages/projects/src/components/TaskForm.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the form labels, placeholders, save/delete/time-entry actions, validation copy, checklist chrome, document/ticket cleanup confirmations, dependency-unsaved prompt, and task-level toast / error fallback copy for move/save/delete/duplicate/agent flows. - **(2026-04-07, F021)** Extended `taskForm.*` with the missing leaves surfaced by the TaskForm audit: field labels (`descriptionLabel`, `dueDateLabel`, `taskTypeLabel`, `priorityLabel`), picker/help copy (`noService`, `addTeamMembers`, `loading`), confirmation/dialog strings (`deleteMessage`, `moveMessage`, `cancelMessage`, `dependencyUnsavedMessage`, keep/delete document/ticket actions), and operational fallback/toast strings (`saveFailed`, `deleteFailed`, `moveFailed`, `duplicateFailed`, `linkingPartialFailure`, `tagCreationPartialFailure`, `prepareTimeEntryFailed`, etc.). - **(2026-04-07, F021)** Re-synced `fr/es/de/nl/it/pl` by deep-merging each locale over the updated English `features/projects` tree, regenerated `xx/yy` via `node scripts/generate-pseudo-locales.cjs`, and re-validated translations successfully. - **(2026-04-07, F021 check)** `node_modules/.bin/eslint packages/projects/src/components/TaskForm.tsx` passes with pre-existing warnings only (25 warnings, 0 errors). The new i18n wiring did not add fresh lint failures. - **(2026-04-07, F022)** Wired `packages/projects/src/components/PhaseTaskImportDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the upload step, field-mapping table, preview summary/table chrome, invalid-row / unmatched-agent / unmatched-status guidance, large-import confirmations, resolution workflows, importing spinner, and completion summaries. - **(2026-04-07, F022)** Extended `import.*` with the dialog-specific gaps surfaced by the audit: CSV read/process/import fallback errors, required/optional field lists, table/tooltip labels, large-import confirmation copy, unmatched agent/status warnings, next-step/import button text, row-limit description, task-count summaries, and `import.fields.*` labels so the field-mapping UI no longer depends on hardcoded English constants from `TASK_IMPORT_FIELDS`. - **(2026-04-07, F022)** Re-synced `fr/es/de/nl/it/pl` from the updated English source, regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` successfully. - **(2026-04-07, F022 check)** `node_modules/.bin/eslint packages/projects/src/components/PhaseTaskImportDialog.tsx` passes with pre-existing warnings only (6 warnings, 0 errors). - **(2026-04-07, F023)** Wired `packages/projects/src/components/Projects.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the page title, create actions, filter placeholders, table headers, row-value fallbacks, screen-reader menu label, reset button, and the delete-success / delete-validation fallback copy. - **(2026-04-07, F023)** Extended `projectList.*` with the list-specific gaps surfaced by the table audit: `columns.*`, `statusOptions.*`, row fallback values (`noClient`, `noContact`, `unassigned`, `notAvailable`, `thisProject`), and delete-validation / delete-failure messages used by `DeleteEntityDialog`. - **(2026-04-07, F023)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` successfully after the `projectList.*` additions. - **(2026-04-07, F023 check)** `node_modules/.bin/eslint packages/projects/src/components/Projects.tsx` passes with pre-existing warnings only (17 warnings, 0 errors). - **(2026-04-07, F024)** Wired `packages/projects/src/components/TaskDocumentsSimple.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the attachments section header, create/upload/link controls, empty state, remove/download/save flows, drawer titles and placeholders, file-attachment viewer copy, folder-selector prompt, and the unsaved-changes confirmation dialog shown above the task drawer. - **(2026-04-07, F024)** Extended `taskDocuments.*` with the missing attachment-surface leaves surfaced by the audit: `attachmentsTitle`, short button labels (`newButton`, `uploadButton`, `linkButton`), empty-state/fallback names, load/create/save/remove/ download failure messages, folder-selection copy, PDF label, and unsaved-change confirm strings. - **(2026-04-07, F024)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` successfully after the `taskDocuments.*` additions. - **(2026-04-07, F024 check)** `node_modules/.bin/eslint packages/projects/src/components/TaskDocumentsSimple.tsx` passes with pre-existing warnings only (10 warnings, 0 errors). - **(2026-04-07, F025)** Wired `packages/projects/src/components/TaskTicketLinks.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the associated-tickets section title, link/create actions, link-existing dialog chrome, filter labels and active-filter chips, select-ticket prompt, cancel/link actions, quick-create checkbox, and ticket-link toast / error fallback copy. - **(2026-04-07, F025)** Extended `taskTicketLinks.*` with the remaining filter/dialog leaves surfaced by the audit: category / board / priority labels, active-chip templates, `selectTicketPlaceholder`, error fallbacks for link/remove/new-ticket flows, and small fallbacks like `clientFallback` and `defaultNewStatus`. - **(2026-04-07, F025)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` successfully after the `taskTicketLinks.*` additions. - **(2026-04-07, F025 check)** `node_modules/.bin/eslint packages/projects/src/components/TaskTicketLinks.tsx` passes with pre-existing warnings only (13 warnings, 0 errors). - **(2026-04-07, F026)** Wired `packages/projects/src/components/TaskDependencies.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the dependency section title, dependency-type labels, add/edit placeholders, action-button titles, empty state, and inline error fallbacks for add/remove/update flows in both edit and pending modes. - **(2026-04-07, F026)** Added the only missing namespace leaf surfaced by the audit: `taskDependencies.updateError`, used when replacing an existing dependency target fails. - **(2026-04-07, F026)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` successfully after the `taskDependencies.updateError` addition. - **(2026-04-07, F026 check)** `node_modules/.bin/eslint packages/projects/src/components/TaskDependencies.tsx` passes with pre-existing warnings only (14 warnings, 0 errors). - **(2026-04-07, F027)** Wired `packages/projects/src/components/ProjectMaterialsDrawer.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the drawer header, add-form labels and placeholders, loading/empty states, add/remove toast copy, table headers/status badges, and the unbilled-total summary. - **(2026-04-07, F027)** Extended `materials.*` with the small gaps surfaced by the component audit: product-search copy, add/remove failure messages, `adding` / `addMaterial`, and `unknownProduct`. - **(2026-04-07, F027)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` successfully after the `materials.*` additions. - **(2026-04-07, F027 check)** `node_modules/.bin/eslint packages/projects/src/components/ProjectMaterialsDrawer.tsx` passes cleanly with 0 warnings / 0 errors after wrapping the translation helper in `useCallback`. - **(2026-04-07, F028)** Wired `packages/projects/src/components/ProjectTaskExportDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the dialog title, phase/field selection headers, select-all toggles, selected-count summaries, export / exporting / completion copy, and the export-field checkbox labels via `export.fields.*`. - **(2026-04-07, F028)** Added the small missing `export.done` leaf so the completion CTA stays `"Done"` instead of drifting to a generic close label. - **(2026-04-07, F028)** Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` successfully after the `export.done` addition. - **(2026-04-07, F028 check)** `node_modules/.bin/eslint packages/projects/src/components/ProjectTaskExportDialog.tsx` passes with pre-existing warnings only (2 warnings, 0 errors). - **(2026-04-07, F029)** Wired `packages/projects/src/components/ProjectQuickAdd.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the quick-add dialog title, field labels/placeholders, validation banner/errors, project-status add-new affordance, client-portal section header, and create/cancel button + toast copy used both on `/msp/projects` and in the global quick-create dialog. - **(2026-04-07, F029)** No new locale keys were required. Existing `quickAdd.*`, `settings.statuses.addStatus`, and `common:actions.*` leaves fully covered the dialog, so this item was a pure component-wiring pass with English fallbacks preserved. - **(2026-04-07, F029 check)** `node_modules/.bin/eslint packages/projects/src/components/ProjectQuickAdd.tsx` passes with a pre-existing hooks warning only (1 warning, 0 errors). - **(2026-04-07, F030)** Wired `packages/projects/src/components/ProjectDetailsEdit.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the edit-form labels, placeholders, validation banner, active/inactive status chip, client-portal section header, save/cancel confirmation dialogs, and the success/failure/save-button copy. - **(2026-04-07, F030)** Added one narrow locale leaf, `projectEdit.updateError`, so the update failure path stays under the project-edit namespace instead of falling back to a generic common save-error message. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo- locales, and re-ran translation validation successfully. - **(2026-04-07, F030 check)** `node_modules/.bin/eslint packages/projects/src/components/ProjectDetailsEdit.tsx` passes with a pre-existing hooks warning only (1 warning, 0 errors). - **(2026-04-07, F031)** Wired `packages/projects/src/components/PrefillFromTicketDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the dialog title, search/filter labels, active-filter chips, ticket selector label, link-checkbox copy, reset/cancel action labels, and the confirm CTA. - **(2026-04-07, F031)** Reused the existing `taskTicketLinks.*` filter chrome instead of adding a parallel prefill-specific subtree for shared ticket filter labels. Added only `dialogs.prefillFromTicket.confirm` for the domain-specific confirm button text, then re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F031 check)** `node_modules/.bin/eslint packages/projects/src/components/PrefillFromTicketDialog.tsx` passes with pre-existing warnings only (4 warnings, 0 errors). - **(2026-04-07, F032)** Wired `packages/projects/src/components/TaskListView.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the responsive table headers, hidden-columns alert, phase empty state, phase/task add buttons, badge/date/ completion chrome, expand/collapse affordances, checklist/dependency tooltip copy, and task action titles. - **(2026-04-07, F032)** Added a small list-view extension to the namespace: `projectDetail.hiddenColumnsAlert`, `projectDetail.listViewEmptyMessage`, `projectDetail.seeMore`, `projectDetail.seeLess`, `projectDetail.checklistItems`, `projectDetail.checklistSummary`, `projectDetail.unknownUser`, `projectDetail.blocksLabel`, plus `taskDependencies.dependsOn`, `taskDependencies.unknownTask`, and `projectPhases.addPhase`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F032 check)** `node_modules/.bin/eslint packages/projects/src/components/TaskListView.tsx` passes with pre-existing warnings only (4 warnings, 0 errors). - **(2026-04-07, F033)** Wired `packages/projects/src/components/TaskCard.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the card ARIA label, quick-actions menu/sr-only copy, priority tooltip, due-date chrome, see-more toggles, additional-agent / checklist / dependency tooltips, hide-tags control, and critical-path badge. - **(2026-04-07, F033)** Added a narrow kanban/task-card extension under `projectDetail.*`: `taskCardAria`, `taskActions`, `priorityLevel`, `dueLabel`, `noDueDate`, `hideTags`, and `criticalPath`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F033 check)** `node_modules/.bin/eslint packages/projects/src/components/TaskCard.tsx` passes with pre-existing warnings only (117 warnings, 0 errors). - **(2026-04-07, F034)** Wired `packages/projects/src/components/PhaseQuickAdd.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the dialog title, inline validation, phase-name/description placeholders, date labels/placeholders, save and cancel actions, and the add-phase failure fallback. - **(2026-04-07, F034)** Added the small phase-quick-add leaves under `projectPhases.*`: `phaseNamePlaceholder`, `descriptionPlaceholder`, `adding`, and `addError`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F034 check)** `node_modules/.bin/eslint packages/projects/src/components/PhaseQuickAdd.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-07, F050)** Wired `packages/projects/src/components/project-templates/TemplateEditor.tsx` to `useTranslation(['features/projects', 'common'])`, including the embedded `TemplateStatusColumn` and template-task-card helpers in the same file. Localized the editor shell, apply/actions menu, delete confirmations, client-portal dialog, phases sidebar, kanban header controls, empty states, status-column add buttons, and all task- card menu/tooltip chrome. - **(2026-04-07, F050)** Expanded `templates.editor.*` with the editor-specific gaps surfaced by the audit: failure toasts (`deleteFailed`, `clientPortalSaveFailed`, `addPhaseFailed`, `updatePhaseFailed`, `deletePhaseFailed`, `moveTaskFailed`, `reorderPhaseFailed`, `taskSaveFailed`, `deleteTaskFailed`, `updateAssigneeFailed`), delete-confirmation messages, badge/action labels, sidebar guidance, status-column summary copy, list/kanban empty states, phase-duration summaries, task-card expand/collapse labels, and fallback labels like `statusFallback`, `unknownUser`, and `unknownTask`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F050 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/TemplateEditor.tsx` passes with pre-existing warnings only (8 warnings, 0 errors). - **(2026-04-07, F051)** Wired `packages/projects/src/components/project-templates/TemplateTaskForm.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the create/edit title, all field labels/placeholders, validation and save-error copy, checklist/dependency section chrome, additional-agent guidance, form action buttons, and the unsaved-change confirmation dialog. - **(2026-04-07, F051)** Extended `templates.taskForm.*` only where the existing subtree had gaps: `addAction`, `updateAction`, `saving`, `saveFailed`, `taskNameRequired`, `addChecklistItem`, `dependenciesHelp`, `cancelEditMessage`, `discardChanges`, `continueEditing`, and `additionalAgentsHelp`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F051 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/TemplateTaskForm.tsx` passes with pre-existing warnings only (3 warnings, 0 errors). - **(2026-04-07, F052)** Wired `packages/projects/src/components/project-templates/wizard-steps/TemplateTasksStep.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the step title and description, phase selector, empty states, task editor labels/placeholders, service and assignment copy, checklist controls, inline validation, per-task summary text, add-task CTA, and the concluding tip callout. - **(2026-04-07, F052)** Extended `templates.wizard.tasks.*` only for wizard-step-specific gaps: `noTasksInPhase`, `thisPhase`, `durationSummaryShort`, `noPriority`, `checklistItemsSummary`, `tipDescription`, `done`, and `addTaskToPhase`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-07, F052 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/wizard-steps/TemplateTasksStep.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F053)** Wired `packages/projects/src/components/project-templates/ApplyTemplateDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the dialog title, validation banner, template/project/client/status/start-date fields, customization options, assignment radio labels, add-status affordance, submit/cancel actions, and success/error toast copy for load/apply flows. - **(2026-04-08, F053)** Extended `templates.apply.*` only where the dialog surfaced small gaps or English drift: added `create`, `creating`, and `createFailed`, and aligned the existing English values for `startDateLabel` and `assignmentOptions.*` to the current UI text so the migration stays fallback-safe without changing the rendered English copy. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F053 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/ApplyTemplateDialog.tsx` passes with a pre-existing hooks warning only (1 warning, 0 errors). - **(2026-04-08, F054)** Wired `packages/projects/src/components/project-templates/ProjectTemplatesList.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the page title, toolbar buttons, search/category filters, table column headers, never-used fallback, row-action menu labels, delete-confirm dialog, loading state, and the user-facing load/delete error-handler copy. - **(2026-04-08, F054)** Extended `templates.list.*` with the only missing list-page leaves surfaced by the audit: `loadFailed` and `deleteFailed`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F054 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/ProjectTemplatesList.tsx` passes with pre-existing warnings only (unused `getTemplateCategories` import, existing `loadData` hooks warning; 3 warnings, 0 errors). - **(2026-04-08, F055)** Wired `packages/projects/src/components/project-templates/CreateTemplateDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the dialog title, source-project / template-name / description / category fields, copy-options section, create/cancel actions, success toast, and the load/create error-handler copy. - **(2026-04-08, F055)** Extended `templates.create.*` with the only missing dialog leaves surfaced by the audit: `loadFailed` and `createFailed`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F055 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/CreateTemplateDialog.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F056)** Wired `packages/projects/src/components/project-templates/TemplateTaskListView.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the responsive table headers, hidden-columns alert, empty state, phase/task add CTAs, untitled-phase/task- count chrome, phase timing summaries, dependency/checklist/additional-agent tooltips, unassigned fallback, status fallback, and edit/delete task action titles. - **(2026-04-08, F056)** Reused existing `tasks.*`, `projectPhases.*`, `taskDependencies.*`, `projectDetail.*`, and `templates.editor.*` leaves where possible. Added only `templates.editor.noPhasesFound`, `untitledPhase`, `taskCount_one`, and `taskCount_other` for the template list-view-specific fallbacks. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F056 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/TemplateTaskListView.tsx` passes with pre-existing warnings only (unused `taskTypes`, `priorities`, and `getAssigneeName`; 6 warnings, 0 errors). - **(2026-04-08, F057)** Wired `packages/projects/src/components/project-templates/wizard-steps/TemplateReviewStep.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the step title and intro, template-information labels, status-columns header, task summary cards, task-details-by-phase heading, checklist-item count suffix, and the final ready-to- create callout. - **(2026-04-08, F057)** Extended `templates.wizard.review.*` with only the review-step gaps surfaced by the audit: `descriptionLabel` for the summary block and `readyDescription` for the final creation callout. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F057 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/wizard-steps/TemplateReviewStep.tsx` passes with pre-existing warnings only (unused `Layers` import and unused `index` callback arg; 4 warnings, 0 errors). - **(2026-04-08, F058)** Wired `packages/projects/src/components/project-templates/wizard-steps/TemplatePhasesStep.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the step title and intro, empty state, phase-form labels/placeholders/help copy, validation message, done/cancel actions, duration/start/task summaries, add-phase buttons, reorder hint, recalculate CTAs, and the phase-timing explainer alert. - **(2026-04-08, F058)** Extended `templates.wizard.phases.*` for the wizard-step gaps only: `intro`, `addFirstPhase`, `phaseNameRequired`, `daysAfterProjectStart`, `tasksCount`, reorder/recalculate copy, and the structured about-timing labels/help text. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F058 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/wizard-steps/TemplatePhasesStep.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F059)** Wired `packages/projects/src/components/project-templates/CreateTemplateForm.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the page title, field labels/placeholders, create/cancel actions, validation toast, success toast, and create-failure error-handler copy. - **(2026-04-08, F059)** No new locale keys were required. The existing `templates.create.*` subtree fully covered the standalone create page after the earlier dialog work, so this item was a pure component-wiring pass. - **(2026-04-08, F059 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/CreateTemplateForm.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F060)** Wired `packages/projects/src/components/project-templates/TemplateDetail.tsx` to `useTranslation(['features/projects', 'common'])`, including the inline `TaskCard` helper. Localized the back/use/delete actions, delete confirmation, success/failure delete flows, template metadata labels, project-phases sidebar, phase header/timing summaries, status-column empty state, status fallback, and the phase-selection empty state in the kanban area. - **(2026-04-08, F060)** Added one narrow detail-page leaf, `templates.detail.selectPhase`, for the kanban empty-state prompt. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F060 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/TemplateDetail.tsx` passes with pre-existing warnings only (unused dropdown-menu imports and unused `onTemplateUpdated`; 12 warnings, 0 errors). - **(2026-04-08, F061)** Wired `packages/projects/src/components/project-templates/wizard-steps/TemplateClientPortalStep.tsx` to `useTranslation('features/projects')`. Localized the step title, intro paragraph, and the explanatory info alert above `ClientPortalConfigEditor`. - **(2026-04-08, F061)** Extended `templates.wizard.clientPortal.*` with the only missing step leaves: `description` and `aboutDescription`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F061 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/wizard-steps/TemplateClientPortalStep.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F062)** Wired `packages/projects/src/components/project-templates/TemplateCreationWizard.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the wizard dialog title, `WizardProgress` step labels, finish CTA, and the fallback load/validation/ create error strings surfaced by the shell. - **(2026-04-08, F062)** Extended the wizard namespace with only two shell-level leaves: `templates.wizard.title` and `templates.wizard.errors.createFailed`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F062 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/TemplateCreationWizard.tsx` passes with pre-existing warnings only (unused exported type imports; 10 warnings, 0 errors). - **(2026-04-08, F063)** Wired `packages/projects/src/components/project-templates/wizard-steps/TemplateBasicsStep.tsx` to `useTranslation('features/projects')`. Localized the template-name/description/ category labels, placeholders, help copy, and the "What's Next?" info alert. - **(2026-04-08, F063)** Extended `templates.wizard.basics.*` with the missing step leaves surfaced by the audit: `nameLabel`, `nameHelp`, `descriptionLabel`, `descriptionHelp`, `categoryLabel`, `categoryHelp`, and `nextHintDescription`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F063 check)** `node_modules/.bin/eslint packages/projects/src/components/project-templates/wizard-steps/TemplateBasicsStep.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F064)** Confirmed `packages/projects/src/components/project-templates/AddTemplateDialog.tsx` is a zero- string wrapper around `TemplateCreationWizard`. It introduces no user-visible copy and requires no i18n wiring of its own, so this item is N/A by design. - **(2026-04-08, F080)** Wired `packages/projects/src/components/settings/projects/TenantProjectTaskStatusSettings.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the task-status-library title/description, create/import CTAs, loading and empty states, closed badge, edit/ delete actions, create/edit dialog title, status-name/preview/color/icon chrome, closed-status checkbox help, submit/cancel actions, and the save/delete/import toast / error-handler / confirm-dialog copy. - **(2026-04-08, F080)** Extended `settings.statuses.*` with the tenant-library-specific gaps surfaced by the audit: task-library description, import button/error copy, dialog preview labels, color/icon field labels, closed-status help text, delete-confirmation message, and update/save helper copy. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F080 check)** `node_modules/.bin/eslint packages/projects/src/components/settings/projects/TenantProjectTaskStatusSettings.tsx` passes with pre-existing warnings only (`any` usages in legacy import/conflict code; 8 warnings, 0 errors). - **(2026-04-08, F081)** Wired `packages/projects/src/components/settings/projects/ProjectStatusSettings.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the table headers, open/ closed labels and project-status hints, actions-menu SR copy, card title/description, add/import buttons, delete-dialog fallback entity name, and the update/delete/import toast / error / validation feedback. - **(2026-04-08, F081)** Extended `settings.statuses.*` with the small project-status page gaps surfaced by the audit: `open`, `order`, project-status description/hints, delete-validation failure, and `this_status`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F081 check)** `node_modules/.bin/eslint packages/projects/src/components/settings/projects/ProjectStatusSettings.tsx` passes with pre-existing warnings only (legacy non-null assertion and `any` usages; 4 warnings, 0 errors). Also fixed the new `useCallback(... t ...)` dependency warning introduced by the i18n wiring. - **(2026-04-08, F082)** Wired `packages/projects/src/components/ProjectTaskStatusEditor.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the inline label, loading state, customize controls, available-statuses prompt, add/remove/reorder feedback, closed badge, empty state, move/remove titles, and the ordering help text. - **(2026-04-08, F082)** Extended `settings.statuses.*` with the narrow inline-editor leaves surfaced by the audit: task/phase labels, customize/project/phase helper copy, available-statuses heading, task-status load/add/remove/reorder errors, and the arrange-order hint. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F082 check)** `node_modules/.bin/eslint packages/projects/src/components/ProjectTaskStatusEditor.tsx` passes with pre-existing warnings only (legacy non-null assertions; 2 warnings, 0 errors). - **(2026-04-08, F083)** Wired `packages/projects/src/components/CreateTaskFromTicketDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the launcher button, dialog title, project/phase/status labels and placeholders, link-ticket checkbox, and create/cancel actions. - **(2026-04-08, F083)** Extended `dialogs.createTaskFromTicket.*` only for the missing launcher/field-label leaves: `button`, `projectLabel`, `phaseLabel`, and `statusLabel`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F083 check)** `node_modules/.bin/eslint packages/projects/src/components/CreateTaskFromTicketDialog.tsx` passes with a pre- existing hooks warning only (`ticket.client_id` effect dependency; 1 warning, 0 errors). - **(2026-04-08, F084)** Wired `packages/projects/src/components/LinkTicketToTaskDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the launcher button, dialog title, project/phase/task labels and placeholders, cancel/link actions, and the success/error link feedback. - **(2026-04-08, F084)** Extended `dialogs.linkTicketToTask.*` with the missing launcher and field/button leaves: `button`, `projectLabel`, `phaseLabel`, `taskLabel`, `linking`, and `confirm`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F084 check)** `node_modules/.bin/eslint packages/projects/src/components/LinkTicketToTaskDialog.tsx` passes with a pre- existing hooks warning only (`ticket.client_id` effect dependency; 1 warning, 0 errors). - **(2026-04-08, F085)** Wired `packages/projects/src/components/DeadlineFilter.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the trigger placeholder / display text, filter-type options, date labels, and clear/apply actions. - **(2026-04-08, F085)** No new locale keys were required. The existing `filters.deadline.*` subtree fully covered the component, so this item was a pure wiring pass. - **(2026-04-08, F085 check)** `node_modules/.bin/eslint packages/projects/src/components/DeadlineFilter.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F086)** Wired `packages/projects/src/components/settings/ProjectSettings.tsx` to `useTranslation('features/projects')`. Localized the page title and the four top- level tab labels for project numbering, project statuses, task statuses, and task priorities. - **(2026-04-08, F086)** Extended `settings.page.*` with a small `tabs.*` group for the settings-shell navigation labels. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo- locales, and re-ran translation validation successfully. - **(2026-04-08, F086 check)** `node_modules/.bin/eslint packages/projects/src/components/settings/ProjectSettings.tsx` passes cleanly with 0 warnings / 0 errors. - **(2026-04-08, F087)** Wired `packages/projects/src/components/ProjectTaskStatusSelector.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the label, customize helper copy, add-existing/create-new CTAs, available-statuses heading, closed badge, move/remove titles, empty-state hint, and ordering help text. - **(2026-04-08, F087)** Extended `settings.statuses.*` only for the selector-specific CTAs: `add_existing` and `create_new`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F087 check)** `node_modules/.bin/eslint packages/projects/src/components/ProjectTaskStatusSelector.tsx` passes with a pre- existing hooks warning only (legacy initialization effect dependencies; 1 warning, 0 errors). - **(2026-04-08, F088)** Wired `packages/projects/src/components/MoveTaskDialog.tsx` to `useTranslation(['features/projects', 'common'])`. Localized the dialog title, body message, tree-select placeholder, validation toasts, cancel action, and confirm/ moving button text. - **(2026-04-08, F088)** Extended `dialogs.moveTask.*` only for the submit-button copy: `moving` and `confirm`. Re-synced `fr/es/de/nl/it/pl`, regenerated pseudo-locales, and re-ran translation validation successfully. - **(2026-04-08, F088 check)** `node_modules/.bin/eslint packages/projects/src/components/MoveTaskDialog.tsx` passes with pre-existing warnings only (unused legacy import/state/catch param; 6 warnings, 0 errors). ## Commands / Runbooks ### The lang-pack loop (run after every namespace edit) ```bash node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs ``` - Regenerates `xx/` and `yy/` from English source (NEVER hand-edit pseudo-locales) - Validates key parity, `{{variable}}` tokens, pseudo-locale fill patterns, Italian accents - Exit code 0 = pass. Keep green before committing. ### Other useful commands - Count strings in a component (lower bound): ```bash grep -cE ">[A-Z][a-zA-Z ]{2,}[a-z]<|(label|title|placeholder)[=:][ ]*['\"][A-Z]|toast\.(error|success|warning|info)\(['\"]|throw new Error\(['\"]" ``` - List all unwired MSP project components (excluding templates): ```bash for f in $(find packages/projects/src/components -type f -name "*.tsx" ! -name "*.test.tsx" ! -path "*/project-templates/*"); do grep -qE "useTranslation" "$f" || echo "$f" done ``` - List all unwired project-template components: ```bash for f in $(find packages/projects/src/components/project-templates -type f -name "*.tsx" ! -name "*.test.tsx"); do grep -qE "useTranslation" "$f" || echo "$f" done ``` - Reference already-wired files (copy the pattern): ``` packages/projects/src/components/project-templates/TemplateStatusManager.tsx packages/projects/src/components/project-templates/wizard-steps/TemplateStatusColumnsStep.tsx packages/projects/src/components/settings/projects/ProjectTaskStatusSettings.tsx ``` - Verify a route's namespaces (from packages/core/src/lib/i18n/config.ts): ```bash grep -A 1 "'/msp/projects'" packages/core/src/lib/i18n/config.ts ``` ## Links / References - 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) - Shared namespace file: `server/public/locales/en/features/projects.json` - Route config: `packages/core/src/lib/i18n/config.ts` (ROUTE_NAMESPACES) - Validation script: `scripts/validate-translations.cjs` - Pseudo-locale generator: `scripts/generate-pseudo-locales.cjs` - Pattern reference (already wired, array form): - `packages/projects/src/components/project-templates/TemplateStatusManager.tsx` - `packages/projects/src/components/project-templates/wizard-steps/TemplateStatusColumnsStep.tsx` - `packages/projects/src/components/settings/projects/ProjectTaskStatusSettings.tsx` - Precedent plan (similar wiring-only work): `ee/docs/plans/2026-03-20-msp-i18n-clients-assets-onboarding/` - MSP template pages (server-side wiring): - `server/src/app/msp/projects/templates/page.tsx` - `server/src/app/msp/projects/templates/[templateId]/page.tsx` - `server/src/app/msp/projects/templates/create/page.tsx` - Global quick-create integration: `server/src/components/layout/QuickCreateDialog.tsx` - Settings page integration: `server/src/components/settings/SettingsPage.tsx` ## Open Questions - Does `/msp/projects/templates*` load `features/projects` via best-match to `/msp/projects`? **Action:** verify at start of sub-batch B via T108. If not, add explicit `ROUTE_NAMESPACES` entries for `/msp/projects/templates`, `/msp/projects/templates/create`, `/msp/projects/templates/[templateId]`. - `features/documents` loading on `/msp/projects/[id]` — does `TaskDocumentsSimple` render translated document strings? **Action:** check if `ROUTE_NAMESPACES['/msp/projects']` needs `'features/documents'` added. - Task dependency cycle warnings — reuse existing `features/projects` keys or need new `taskDependencies.cycle.*`? **Action:** check during F026 implementation. - Kanban drag helpers (ARIA labels) — translate or leave English (accessibility-only text may follow different conventions)? **Tentative answer:** translate; aria-labels are user-facing for screen reader users and should match UI locale. ## Implementation Order (recommended) 1. **Setup (F001-F005):** Audit gaps, extend `en/features/projects.json`, translate to 6 non-English locales (IT accent audit), regenerate pseudo-locales via script, verify template route namespaces. This unblocks all component wiring. 2. **Sub-batch A PR (F020-F034):** 15 largest project components. Ship first while context is fresh. Start with ProjectDetail + TaskForm as they dominate. 3. **Sub-batch B PR (F050-F064):** 15 project-template components. Wizard steps + editor. 4. **Sub-batch C PR (F080-F094):** 30 small/settings components. Quick cleanup pass, confirm zero-string components. 5. **Closeout (F100-F102):** Final lang-pack loop, update parent plan, archive scratchpad. ## Risks - **Layout breakage from long translations.** German ~30% longer than English; French task/phase labels expand button widths. Mitigation: pseudo-locale (xx/yy expand strings) test exercises kanban column widths, task card layouts, wizard nav buttons. - **Reused components in multiple contexts.** `ProjectQuickAdd` (global QuickCreate), `ProjectSettings` (settings page), `LinkTicketToTaskDialog`/`CreateTaskFromTicketDialog` (tickets + projects both). Mitigation: integration tests T110-T111. - **Missing templates namespace loading.** If `/msp/projects/templates*` doesn't match `/msp/projects` best-match, templates pages render untranslated even with components wired. Mitigation: **do F005 before sub-batch B**. - **Existing tests asserting English text.** Many components have `.test.tsx` files that may assert exact English strings. Mitigation: use `t('key', 'Exact English')` fallback so rendered text is identical until locale changes; update tests that break. - **Template wizard shared state.** Wizard passes step data via parent context — ensure `useTranslation` works in all 5 step components independently without state drift. ## Final Status (2026-04-08) **All features implemented (F001–F102).** - **Final `en/features/projects.json` key count:** 1,122 leaf keys (up from 128 at start) - **Components wired:** ~63 (15 sub-batch A + 15 sub-batch B + ~33 sub-batch C including 3 discovered non-zero components) - **Confirmed zero-string:** StatusColumn, KanbanBoard, ProjectPage, KanbanZoomControl, DonutChart, TaskQuickAdd, TaskEdit, HoursProgressBar, TaskPrioritySettings (9 files) - **Discovered non-zero (wired):** TaskCommentThread (~8 strings), TaskCommentForm (~3 strings), ProjectActiveToggle (~3 strings) — originally listed as zero-string in PRD - **Deferred:** ClientPortalConfigEditor.tsx — has many user-visible strings for configuring client portal visibility of project data, but is client-portal config UI. Scope for a future pass. - **Validation:** `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` passes (0 errors, 0 warnings) - **Parent plan updated:** `.ai/translation/MSP_i18n_plan.md` — 2b-21b/c marked ✅ DONE