Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
53 KiB
53 KiB
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.jsonundertemplates.*subtree rather than creating a newfeatures/project-templates.jsonnamespace. Rationale: already-wired components (TemplateStatusColumnsStep,TemplateStatusManager,ProjectTaskStatusSettings) usefeatures/projectswithtemplates.*keys and it's working. Splitting would fragment the namespace needlessly. Supersedes the parent plan's tentative "newfeatures/project-templatesnamespace" 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 fromcommonviacommon: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 inmsp/onboarding.json. - (2026-04-05) Task document strings — prefer reusing
features/documentskeys where they match (upload/download/attach/remove). Only addfeatures/projects.taskDocuments.*for project-task-specific copy. Verifyfeatures/documentsis 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.jsontop-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_NAMESPACESentries that loadfeatures/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/createShould match-best against/msp/projectsand 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, supportscommon:prefix for shared keys.
- (2026-04-05) Largest files dominate the string count:
ProjectDetail.tsx3,038 LOC / ~50 stringsTemplateEditor.tsx2,315 LOC / ~28 stringsTaskForm.tsx2,024 LOC / ~39 stringsTaskListView.tsx1,320 LOC / ~9 stringsPhaseTaskImportDialog.tsx1,290 LOC / ~21 stringsTemplateTaskForm.tsx1,015 LOC / ~22 stringsProjects.tsx980 LOC / ~21 stringsTemplateTaskListView.tsx953 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)
ProjectQuickAddreused 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)
ProjectSettingsis exported from@alga-psa/projects/componentsand imported byserver/src/components/settings/SettingsPage.tsx(Settings page wiring). - (2026-04-07, F001 audit) Existing
features/projectskeys 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.*, andfilters.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.*, andtemplates.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 byCreateTaskFromTicketDialog,LinkTicketToTaskDialog,MoveTaskDialog,DuplicateTaskDialog,ProjectInfo,ProjectTaskStatusSelector,ProjectPhases,TaskStatusSelect,TicketSelect, andTaskTypeSelector. - (2026-04-07, F001 audit) Reuse decisions from current inventory:
keep attachments copy under existing
documents.*where strings match; addtaskDocuments.*only for task-specific actions if required. Keep shared generic buttons incommon. Reusetasks.*,fields.*,phases.*, andstatus.*before adding narrower keys. Keep all template-related strings infeatures/projectsundertemplates.*. - (2026-04-07, F001 audit) Representative concrete gaps seen in code:
Projects.tsxneeds translations forProjects, search/filter placeholders,Open menu, and delete success toast.ProjectQuickAdd.tsx/ProjectDetailsEdit.tsxneed full form labels, placeholders, unsaved/save confirmation copy, and portal visibility label.TaskDependencies.tsxlacks keys for section title, dependency editor actions, and task picker placeholders.TaskTicketLinks.tsxlacks keys for duplicate/invalid ticket toasts, section title, link/create dialog labels, and ticket search filters.TaskDocumentsSimple.tsxlacks keys for auth/validation toasts, create/upload/link buttons, remove actions, document-name placeholder, and unsaved-change dialog.PhaseTaskImportDialog.tsxneeds a largeimport.*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.jsonfrom 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 largesettings.*andtemplates.*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/projectstree intofr/es/de/nl/it/plby 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.cjsafter the namespace expansion. Regeneratedxx/features/projects.jsonandyy/features/projects.json; the run also refreshedxx/common.jsonbecause the pseudo generator rewrites every pseudo-locale file from current English sources in one pass. - (2026-04-07, validation)
node scripts/validate-translations.cjspasses 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 explicitROUTE_NAMESPACESentries are needed; longest-prefix matching against/msp/projectsalready loadsfeatures/projectscorrectly. - (2026-04-07, F020) Wired
packages/projects/src/components/ProjectDetail.tsxtouseTranslation(['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 theProjectDetail.tsxwiring to English, re-syncedfr/es/de/nl/it/plvia 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.tsxpasses with pre-existing warnings only (no new lint errors introduced by the i18n wiring). - (2026-04-07, F021) Wired
packages/projects/src/components/TaskForm.tsxtouseTranslation(['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/plby deep-merging each locale over the updated Englishfeatures/projectstree, regeneratedxx/yyvianode scripts/generate-pseudo-locales.cjs, and re-validated translations successfully. - (2026-04-07, F021 check)
node_modules/.bin/eslint packages/projects/src/components/TaskForm.tsxpasses 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.tsxtouseTranslation(['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, andimport.fields.*labels so the field-mapping UI no longer depends on hardcoded English constants fromTASK_IMPORT_FIELDS. - (2026-04-07, F022) Re-synced
fr/es/de/nl/it/plfrom the updated English source, regenerated pseudo-locales, and re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully. - (2026-04-07, F022 check)
node_modules/.bin/eslint packages/projects/src/components/PhaseTaskImportDialog.tsxpasses with pre-existing warnings only (6 warnings, 0 errors). - (2026-04-07, F023) Wired
packages/projects/src/components/Projects.tsxtouseTranslation(['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 byDeleteEntityDialog. - (2026-04-07, F023) Re-synced
fr/es/de/nl/it/pl, regenerated pseudo-locales, and re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully after theprojectList.*additions. - (2026-04-07, F023 check)
node_modules/.bin/eslint packages/projects/src/components/Projects.tsxpasses with pre-existing warnings only (17 warnings, 0 errors). - (2026-04-07, F024) Wired
packages/projects/src/components/TaskDocumentsSimple.tsxtouseTranslation(['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-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully after thetaskDocuments.*additions. - (2026-04-07, F024 check)
node_modules/.bin/eslint packages/projects/src/components/TaskDocumentsSimple.tsxpasses with pre-existing warnings only (10 warnings, 0 errors). - (2026-04-07, F025) Wired
packages/projects/src/components/TaskTicketLinks.tsxtouseTranslation(['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 likeclientFallbackanddefaultNewStatus. - (2026-04-07, F025) Re-synced
fr/es/de/nl/it/pl, regenerated pseudo-locales, and re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully after thetaskTicketLinks.*additions. - (2026-04-07, F025 check)
node_modules/.bin/eslint packages/projects/src/components/TaskTicketLinks.tsxpasses with pre-existing warnings only (13 warnings, 0 errors). - (2026-04-07, F026) Wired
packages/projects/src/components/TaskDependencies.tsxtouseTranslation(['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-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully after thetaskDependencies.updateErroraddition. - (2026-04-07, F026 check)
node_modules/.bin/eslint packages/projects/src/components/TaskDependencies.tsxpasses with pre-existing warnings only (14 warnings, 0 errors). - (2026-04-07, F027) Wired
packages/projects/src/components/ProjectMaterialsDrawer.tsxtouseTranslation(['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, andunknownProduct. - (2026-04-07, F027) Re-synced
fr/es/de/nl/it/pl, regenerated pseudo-locales, and re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully after thematerials.*additions. - (2026-04-07, F027 check)
node_modules/.bin/eslint packages/projects/src/components/ProjectMaterialsDrawer.tsxpasses cleanly with 0 warnings / 0 errors after wrapping the translation helper inuseCallback. - (2026-04-07, F028) Wired
packages/projects/src/components/ProjectTaskExportDialog.tsxtouseTranslation(['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 viaexport.fields.*. - (2026-04-07, F028) Added the small missing
export.doneleaf 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-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully after theexport.doneaddition. - (2026-04-07, F028 check)
node_modules/.bin/eslint packages/projects/src/components/ProjectTaskExportDialog.tsxpasses with pre-existing warnings only (2 warnings, 0 errors). - (2026-04-07, F029) Wired
packages/projects/src/components/ProjectQuickAdd.tsxtouseTranslation(['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/projectsand in the global quick-create dialog. - (2026-04-07, F029) No new locale keys were required. Existing
quickAdd.*,settings.statuses.addStatus, andcommon: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.tsxpasses with a pre-existing hooks warning only (1 warning, 0 errors). - (2026-04-07, F030) Wired
packages/projects/src/components/ProjectDetailsEdit.tsxtouseTranslation(['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-syncedfr/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.tsxpasses with a pre-existing hooks warning only (1 warning, 0 errors). - (2026-04-07, F031) Wired
packages/projects/src/components/PrefillFromTicketDialog.tsxtouseTranslation(['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 onlydialogs.prefillFromTicket.confirmfor the domain-specific confirm button text, then re-syncedfr/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.tsxpasses with pre-existing warnings only (4 warnings, 0 errors). - (2026-04-07, F032) Wired
packages/projects/src/components/TaskListView.tsxtouseTranslation(['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, plustaskDependencies.dependsOn,taskDependencies.unknownTask, andprojectPhases.addPhase. Re-syncedfr/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.tsxpasses with pre-existing warnings only (4 warnings, 0 errors). - (2026-04-07, F033) Wired
packages/projects/src/components/TaskCard.tsxtouseTranslation(['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, andcriticalPath. Re-syncedfr/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.tsxpasses with pre-existing warnings only (117 warnings, 0 errors). - (2026-04-07, F034) Wired
packages/projects/src/components/PhaseQuickAdd.tsxtouseTranslation(['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, andaddError. Re-syncedfr/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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-07, F050) Wired
packages/projects/src/components/project-templates/TemplateEditor.tsxtouseTranslation(['features/projects', 'common']), including the embeddedTemplateStatusColumnand 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 likestatusFallback,unknownUser, andunknownTask. Re-syncedfr/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.tsxpasses with pre-existing warnings only (8 warnings, 0 errors). - (2026-04-07, F051) Wired
packages/projects/src/components/project-templates/TemplateTaskForm.tsxtouseTranslation(['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, andadditionalAgentsHelp. Re-syncedfr/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.tsxpasses with pre-existing warnings only (3 warnings, 0 errors). - (2026-04-07, F052) Wired
packages/projects/src/components/project-templates/wizard-steps/TemplateTasksStep.tsxtouseTranslation(['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, andaddTaskToPhase. Re-syncedfr/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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F053) Wired
packages/projects/src/components/project-templates/ApplyTemplateDialog.tsxtouseTranslation(['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: addedcreate,creating, andcreateFailed, and aligned the existing English values forstartDateLabelandassignmentOptions.*to the current UI text so the migration stays fallback-safe without changing the rendered English copy. Re-syncedfr/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.tsxpasses with a pre-existing hooks warning only (1 warning, 0 errors). - (2026-04-08, F054) Wired
packages/projects/src/components/project-templates/ProjectTemplatesList.tsxtouseTranslation(['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:loadFailedanddeleteFailed. Re-syncedfr/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.tsxpasses with pre-existing warnings only (unusedgetTemplateCategoriesimport, existingloadDatahooks warning; 3 warnings, 0 errors). - (2026-04-08, F055) Wired
packages/projects/src/components/project-templates/CreateTemplateDialog.tsxtouseTranslation(['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:loadFailedandcreateFailed. Re-syncedfr/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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F056) Wired
packages/projects/src/components/project-templates/TemplateTaskListView.tsxtouseTranslation(['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.*, andtemplates.editor.*leaves where possible. Added onlytemplates.editor.noPhasesFound,untitledPhase,taskCount_one, andtaskCount_otherfor the template list-view-specific fallbacks. Re-syncedfr/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.tsxpasses with pre-existing warnings only (unusedtaskTypes,priorities, andgetAssigneeName; 6 warnings, 0 errors). - (2026-04-08, F057) Wired
packages/projects/src/components/project-templates/wizard-steps/TemplateReviewStep.tsxtouseTranslation(['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:descriptionLabelfor the summary block andreadyDescriptionfor the final creation callout. Re-syncedfr/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.tsxpasses with pre-existing warnings only (unusedLayersimport and unusedindexcallback arg; 4 warnings, 0 errors). - (2026-04-08, F058) Wired
packages/projects/src/components/project-templates/wizard-steps/TemplatePhasesStep.tsxtouseTranslation(['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-syncedfr/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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F059) Wired
packages/projects/src/components/project-templates/CreateTemplateForm.tsxtouseTranslation(['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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F060) Wired
packages/projects/src/components/project-templates/TemplateDetail.tsxtouseTranslation(['features/projects', 'common']), including the inlineTaskCardhelper. 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-syncedfr/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.tsxpasses with pre-existing warnings only (unused dropdown-menu imports and unusedonTemplateUpdated; 12 warnings, 0 errors). - (2026-04-08, F061) Wired
packages/projects/src/components/project-templates/wizard-steps/TemplateClientPortalStep.tsxtouseTranslation('features/projects'). Localized the step title, intro paragraph, and the explanatory info alert aboveClientPortalConfigEditor. - (2026-04-08, F061) Extended
templates.wizard.clientPortal.*with the only missing step leaves:descriptionandaboutDescription. Re-syncedfr/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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F062) Wired
packages/projects/src/components/project-templates/TemplateCreationWizard.tsxtouseTranslation(['features/projects', 'common']). Localized the wizard dialog title,WizardProgressstep 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.titleandtemplates.wizard.errors.createFailed. Re-syncedfr/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.tsxpasses 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.tsxtouseTranslation('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, andnextHintDescription. Re-syncedfr/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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F064) Confirmed
packages/projects/src/components/project-templates/AddTemplateDialog.tsxis a zero- string wrapper aroundTemplateCreationWizard. 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.tsxtouseTranslation(['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-syncedfr/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.tsxpasses with pre-existing warnings only (anyusages in legacy import/conflict code; 8 warnings, 0 errors). - (2026-04-08, F081) Wired
packages/projects/src/components/settings/projects/ProjectStatusSettings.tsxtouseTranslation(['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, andthis_status. Re-syncedfr/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.tsxpasses with pre-existing warnings only (legacy non-null assertion andanyusages; 4 warnings, 0 errors). Also fixed the newuseCallback(... t ...)dependency warning introduced by the i18n wiring. - (2026-04-08, F082) Wired
packages/projects/src/components/ProjectTaskStatusEditor.tsxtouseTranslation(['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-syncedfr/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.tsxpasses with pre-existing warnings only (legacy non-null assertions; 2 warnings, 0 errors). - (2026-04-08, F083) Wired
packages/projects/src/components/CreateTaskFromTicketDialog.tsxtouseTranslation(['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, andstatusLabel. Re-syncedfr/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.tsxpasses with a pre- existing hooks warning only (ticket.client_ideffect dependency; 1 warning, 0 errors). - (2026-04-08, F084) Wired
packages/projects/src/components/LinkTicketToTaskDialog.tsxtouseTranslation(['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, andconfirm. Re-syncedfr/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.tsxpasses with a pre- existing hooks warning only (ticket.client_ideffect dependency; 1 warning, 0 errors). - (2026-04-08, F085) Wired
packages/projects/src/components/DeadlineFilter.tsxtouseTranslation(['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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F086) Wired
packages/projects/src/components/settings/ProjectSettings.tsxtouseTranslation('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 smalltabs.*group for the settings-shell navigation labels. Re-syncedfr/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.tsxpasses cleanly with 0 warnings / 0 errors. - (2026-04-08, F087) Wired
packages/projects/src/components/ProjectTaskStatusSelector.tsxtouseTranslation(['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_existingandcreate_new. Re-syncedfr/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.tsxpasses 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.tsxtouseTranslation(['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:movingandconfirm. Re-syncedfr/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.tsxpasses 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)
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs
- Regenerates
xx/andyy/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):
grep -cE ">[A-Z][a-zA-Z ]{2,}[a-z]<|(label|title|placeholder)[=:][ ]*['\"][A-Z]|toast\.(error|success|warning|info)\(['\"]|throw new Error\(['\"]" <file> - List all unwired MSP project components (excluding templates):
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:
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):
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.tsxpackages/projects/src/components/project-templates/wizard-steps/TemplateStatusColumnsStep.tsxpackages/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.tsxserver/src/app/msp/projects/templates/[templateId]/page.tsxserver/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*loadfeatures/projectsvia best-match to/msp/projects? Action: verify at start of sub-batch B via T108. If not, add explicitROUTE_NAMESPACESentries for/msp/projects/templates,/msp/projects/templates/create,/msp/projects/templates/[templateId]. features/documentsloading on/msp/projects/[id]— doesTaskDocumentsSimplerender translated document strings? Action: check ifROUTE_NAMESPACES['/msp/projects']needs'features/documents'added.- Task dependency cycle warnings — reuse existing
features/projectskeys or need newtaskDependencies.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)
- 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. - Sub-batch A PR (F020-F034): 15 largest project components. Ship first while context is fresh. Start with ProjectDetail + TaskForm as they dominate.
- Sub-batch B PR (F050-F064): 15 project-template components. Wizard steps + editor.
- Sub-batch C PR (F080-F094): 30 small/settings components. Quick cleanup pass, confirm zero-string components.
- 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/projectsbest-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.tsxfiles that may assert exact English strings. Mitigation: uset('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
useTranslationworks in all 5 step components independently without state drift.
Final Status (2026-04-08)
All features implemented (F001–F102).
- Final
en/features/projects.jsonkey 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.cjspasses (0 errors, 0 warnings) - Parent plan updated:
.ai/translation/MSP_i18n_plan.md— 2b-21b/c marked ✅ DONE