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
62 KiB
62 KiB
Scratchpad — MSP i18n Batch 2b-21a: Tickets Migration
- Plan slug:
2026-04-05-msp-i18n-tickets-migration - Created:
2026-04-05
What This Is
A mechanical wiring pass: 23 unwired MSP ticket components × useTranslation('features/tickets').
The shared namespace (147 keys, 9 locales) already exists and is already loaded by
ROUTE_NAMESPACES['/msp/tickets']. Client-portal side is 100% wired — this closes the MSP gap.
Decisions
- (2026-04-05) Keep MSP-specific keys in
features/tickets.jsonrather than extracting to newmsp/ticketing.json. Rationale: client portal may eventually surface bulk actions, export, etc. If file exceeds ~250 keys after sub-batch A, revisit. - (2026-04-05) Use
t('key', 'English fallback')signature everywhere — fallback-safe against missing keys and matches patterns in already-wiredTicketConversation.tsxandTicketDetails.tsx. - (2026-04-05) Ship sub-batches A (large dashboard/detail/quickadd), B (settings + export
- materials), C (small components) as independent PRs.
- (2026-04-05) Translate toast messages and user-visible error strings. Do NOT translate
throw new Error('...')strings caught by error boundaries or logged only. - (2026-04-05) Use i18next
{{count}}interpolation for pluralized toast messages (e.g.,'{{count}} ticket moved'/'{{count}} tickets moved') rather than template literals. Matches existingfeatures/tickets.jsonpatterns inmessages.*. - (2026-04-05, F002) Expand
features/tickets.jsonin-place rather than splitting micro- namespaces. The shared file is now broad, but keeping tickets UI copy in one namespace makes the MSP/client-portal overlap explicit and avoids route-specific key plumbing.
Discoveries / Constraints
- (2026-04-05)
features/tickets.jsontop-level groups: title, subtitle, backToTickets, createNew, createButton, viewAll, myTickets, resetFilters, filters (7), create (10), status (8), priority (6), fields (21), actions (8), messages (31), conversation (23), responseState (4), origin (5), responseSource (2), documents (14). Total: 147 keys. - (2026-04-05, F002/F062) The initial 147-key count was from an early partial read and is no
longer accurate. After the full MSP migration pass,
server/public/locales/en/features/tickets.jsoncontains 887 leaf strings. The file already had substantial nested MSP/support-ticket coverage below the first 260 lines; this batch extended that existing structure rather than introducing a second parallel namespace. - (2026-04-05)
ROUTE_NAMESPACES['/msp/tickets']already loads['common', 'msp/core', 'features/tickets']— no config changes needed. - (2026-04-05) Already-wired MSP components (reference examples for patterns):
CommentItem.tsx→useTranslation('features/tickets')TicketConversation.tsx→useTranslation('features/tickets')TicketDetails.tsx→useTranslation('features/tickets')TicketDocumentsSection.tsx→useTranslation('features/documents')TicketAppointmentRequests.tsx→useTranslation('features/appointments')
- (2026-04-05) Largest files (LOC): TicketingDashboard (2024), QuickAddTicket (1596), TicketInfo (1587), TicketProperties (1234), CategoriesSettings (865). These 5 alone are ~7,300 LOC and likely account for ~60% of strings.
- (2026-04-05) Rough string estimates (from grep heuristic — undercount):
- Sub-batch A: 4 files, ~150-200 strings
- Sub-batch B: 4 files, ~80-95 strings
- Sub-batch C: 15 files, ~50-70 strings
- Total: ~280-365 strings (heuristic floor; realistic: 350-450)
- (2026-04-05) Zero-string components (confirm during implementation): TicketDetailsSkeleton, AgentScheduleDrawerStyles, TicketListSkeleton, AgentScheduleDrawer (re-export shim). TicketOriginBadge and ResponseSourceBadge may have small badge-label key sets needed.
- (2026-04-05)
QuickAddTicketis reused outside tickets module — imported inserver/src/components/layout/QuickCreateDialog.tsxfor the global quick-create menu. Translation must also work in that context. - (2026-04-05)
CategoryPickeris reused inserver/src/app/msp/service-requests/ServiceRequestDefinitionEditorPage.tsx. Verify translation works on service-request routes too. - (2026-04-05)
CategoriesSettingsandDisplaySettingsare rendered viaserver/src/components/settings/general/TicketingSettings.tsx→ likely loaded by/msp/settingsroute.ROUTE_NAMESPACES['/msp/settings']doesn't currently includefeatures/tickets— verify this route loads it transitively or add it. - (2026-04-05, F001 audit)
/msp/service-requests/*also does not have a dedicatedROUTE_NAMESPACESentry. Those pages currently inherit/msponly, so reused ticket components likeCategoryPickerwould not receivefeatures/ticketsunless route config is expanded. - (2026-04-05, F001 audit) Concrete namespace gaps found by component audit:
dashboard.*: title, add button, assignee/status/response-state/due-date/SLA filters, destination board/status prompts, selected-count text, no-selection text, move/delete/ bundle modal copy, board-switch warnings, bulk result summaries, empty/loading states.bulk.*: move/delete/bundle button labels, confirm/cancel/continue actions, singular/ plural success toasts, partial-failure headings, master-ticket selection, cross-client bundle warning, destination validation errors.quickAdd.*: dialog title, required-fields summary, assigned-to/additional-agents labels, board/category/status/location/contact placeholders, due-date helper copy, clipboard upload failure, team assignment/tag creation partial-failure toasts.itil.*: impact/urgency field labels, select placeholders, calculated-priority label, priority-matrix heading, impact/urgency scale labels, explanatory help text, planning priority label.info.*/properties.*: unsaved-changes banner, status/board/priority/due-date/SLA/ tags/description headings, not-assigned/no-contact/no-location/no-primary-agent/no-team empty states, additional-agents label, appointment request field labels, team-assignment removal modes, email-log button label.settings.categories.*: categories heading, board filter placeholder, dialog field labels, import flow copy, import conflict actions, delete/save/import success and failure strings.settings.display.*: response-state tracking section, explanatory copy, toggle labels, display-preferences section, date/time format label, column labels, tags layout labels, save/saving success and failure strings.export.*: dialog title, field-picker heading, select-all toggles, selected-count copy, exporting/progress/completion copy, done/cancel actions, CSV file export failures.materials.*: product/price/quantity/total/description labels, select placeholders, loading/empty states, billed/pending badges, add/remove success and validation errors.watchList.*: tab labels, contact scope labels, selector placeholders, empty state, add/ remove validation errors, generic save failure message.emailNotifications.*: table headings, loading/empty states, pagination action text.categoryPicker.*: "No Category", multi-select summary text, exclusion summary text, add-new label, ITIL badge label, default placeholder.responseState.*additions:awaitingClient,awaitingInternal,clear,setResponseState,notSet,label. Existing keys use client-portal wording and do not cover the MSP dropdown strings directly.navigation.*: previous/next ticket aria-labels.debug.*: comment metadata modal section headings and empty-summary copy.errors.*/validation.*: generic save/load/export/category/material/watch-list errors that are user-visible today and should not stay hardcoded.
- (2026-04-05, F002) Added the first full MSP key expansion to
server/public/locales/en/features/tickets.json:dashboard,bulk,quickAdd,itil,info,properties,settings.categories,settings.display,export,materials,watchList,emailNotifications,categoryPicker,navigation,debug,validation, anderrors, plus targeted additions tofilters,priority,fields,actions, andresponseState. - (2026-04-05, F002) To keep translation validation green between per-locale commits,
the same new key structure was scaffolded into
fr/es/de/nl/it/plwith English placeholder values.xx/yywere regenerated immediately from English; the real locale translations are filled in byF003-F008. - (2026-04-05, F062) Final namespace closeout:
features/tickets.jsonends this batch at 887 English leaf strings. That is well above the earlier ~250 revisit threshold, but the shared MSP/client-portal namespace is still the right design because the badge enums, ticket chrome, quick-add flow, settings surfaces, and reused picker components now all draw from one route-agnostic ticket vocabulary. No feature-scope code items remain deferred; the remaining backlog is test execution/coverage tracked intests.json.
Commands / Runbooks
The lang-pack loop (run after every namespace edit)
This is the single-command validation cycle. Run it every time en/features/tickets.json
is edited — it regenerates pseudo-locales and verifies key parity across all 9 locales:
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs
generate-pseudo-locales.cjsreads every English file and rebuildsxx/andyy/with fill values (11111,55555), preserving{{interpolation}}tokens. Never hand-editxx/oryy/files — they will be overwritten.validate-translations.cjschecks every locale against English for: identical key structure, no extra/missing keys, preserved{{variable}}tokens, valid JSON, pseudo- locale fill patterns. Exit code 0 = pass.
If validation fails, fix en/features/tickets.json (or the complaining locale), re-run
the one-liner. Keep the validator 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)\(['\"]" <file> - List all unwired MSP ticket components:
for f in $(find packages/tickets/src/components -type f -name "*.tsx" ! -name "*.test.tsx"); do grep -qE "useTranslation" "$f" || echo "$f" done - Audit user-visible strings in unwired components:
rg -n "toast\.|title:|label=|placeholder=|aria-label|>[^<{]*[A-Za-z][^<{]*<|throw new Error|confirm\(" packages/tickets/src/components - Find all places a component is imported:
grep -rn "from '@alga-psa/tickets" server/src ee/server/src | grep ComponentName - Reference already-wired files (copy the pattern):
packages/tickets/src/components/ticket/TicketDetails.tsx packages/tickets/src/components/ticket/TicketConversation.tsx packages/tickets/src/components/ticket/CommentItem.tsx
Links / References
- Parent plan:
.ai/translation/MSP_i18n_plan.md(Batch 2b-21a) - Shared namespace file:
server/public/locales/en/features/tickets.json - Route config:
packages/core/src/lib/i18n/config.ts(ROUTE_NAMESPACES) - Validation script:
scripts/validate-translations.cjs - Validation CI workflow:
.github/workflows/validate-translations.yml - Pattern reference (already wired):
packages/tickets/src/components/ticket/TicketDetails.tsx,packages/tickets/src/components/ticket/TicketConversation.tsx,packages/tickets/src/components/ticket/CommentItem.tsx - Precedent plan (similar wiring-only work):
docs/plans/2026-03-20-msp-i18n-remaining/ - i18n test helpers:
server/src/test/unit/layout/*.i18n.test.tsx - QuickCreate integration:
server/src/components/layout/QuickCreateDialog.tsx - Service-request integration:
server/src/app/msp/service-requests/ServiceRequestDefinitionEditorPage.tsx - Settings integration:
server/src/components/settings/general/TicketingSettings.tsx
Open Questions
- Does
ROUTE_NAMESPACES['/msp/settings']or/msp/settings/ticketingloadfeatures/tickets? If not, wiringCategoriesSettingsandDisplaySettingsrequires a namespaces update. Action: verify before starting sub-batch B. F010is required: addfeatures/ticketsto/msp/settingsand add an explicit/msp/service-requestsroute mapping soCategoriesSettings,DisplaySettings, and the reusedCategoryPickerhave locale resources outside/msp/tickets.- Should
TicketOriginBadgeandResponseSourceBadgereuse the existingfeatures/tickets.jsonorigin.*andresponseSource.*keys or do those groups need more enum values? Action: diff badge enum values against existing keys during F049. - What is the canonical test pattern for component-level i18n tests in this repo? Models
to follow:
server/src/test/unit/layout/*.i18n.test.tsx,server/src/test/unit/dashboard/DashboardContainer.i18n.test.tsx. - Are there existing test helpers/utilities for rendering components with specific locales?
Action: search for i18n test setup in
server/src/test/unit/i18n/*.
Implementation Order (recommended)
- Setup (F001-F010): Audit gaps, add missing keys to en/features/tickets.json, generate all 8 other locales. This unblocks all component wiring.
- Sub-batch A PR (F020-F028): 4 large components. Biggest review burden; ship first while context is fresh.
- Sub-batch B PR (F030-F037): 4 medium components. Verify settings-route namespace loading before starting.
- Sub-batch C PR (F040-F050): 15 small components. Quick cleanup pass.
- Closeout (F060-F062): Validate, update parent plan, archive scratchpad notes.
Progress Log
- (2026-04-05, F002) Expanded
en/features/tickets.jsonwith the audited MSP ticket key families used by dashboard/bulk/quick-add/ITIL/info/properties/settings/export/materials/ watch-list/email/category-picker/response-state/navigation/debug/validation/error flows. To keep key parity green on every commit, mirrored the new keys intofr/es/de/nl/it/plwith temporary English placeholders. Regeneratedxx/yy/features/tickets.jsonfrom English and re-rannode scripts/validate-translations.cjssuccessfully (Errors: 0,Warnings: 0). - (2026-04-05, F002) Placeholder strategy is intentional:
F003-F008will replace the temporary English values in each production locale with real translations one locale at a time while keeping the validator green throughout. - (2026-04-05, F003) Replaced the temporary English placeholders in
fr/features/tickets.jsonwith French translations for the MSP ticket additions. During validation a partial Italian worker edit had renamedticketSection.on/ticketSection.minutesto localized key names; corrected the key names in-place so parity stayed valid without altering the Italian values. - (2026-04-05, F004) Replaced the temporary English placeholders in
es/features/tickets.jsonwith Spanish translations for the MSP ticket additions using the same batched machine-translation pass as French, then manually corrected ticket-domain copy around bundle warnings, bulk move success interpolation, and plural action labels. After the cleanup pass only expected invariants remain English-equal (Total,Error,SLA,ITIL,N/A), andnode scripts/validate-translations.cjsstays green. - (2026-04-05, F005) Completed the German locale pass for
de/features/tickets.json. This file already had broad ticket coverage; the remaining work was mostly placeholder cleanup in MSP additions. After translation, the only English-equal leaves are domain terms or deliberate invariants (Board,Team,Lead,Service,SLA,ITIL,name@example.com), so no extra manual rewrites were needed beyond the automated fill-in. Validation stayed green. - (2026-04-05, F006) Completed the Dutch locale pass for
nl/features/tickets.json. The machine fill covered the remaining MSP placeholders cleanly; manual cleanup was limited to replacing the exported-ticket tooltip’s literalticket(s)phrasing with natural Dutch. Remaining English-equal leaves are acceptable product terms or invariants (Open,Status,Team,Impact,Product,Compact,SLA,ITIL). - (2026-04-05, F007) Italian locale coverage was already effectively complete after the
placeholder scaffold. The substantive work here was preserving key parity by restoring the
canonical key names
ticketSection.onandticketSection.minuteswhile keeping the localized values (il,minuti). Accent-sensitive validation remained green, so no further copy edits were required beyond confirming the remaining English-equal leaves are acceptable (Team,Email,SLA,ITIL). - (2026-04-05, F008) Completed the Polish locale pass for
pl/features/tickets.json, including the remaining MSP validation strings, ITIL helper copy, ticket-section appointment labels, and quick-add placeholders. After the translation pass only invariant leaves remain English-equal (Status,SLA,ITIL), and the bundle/bulk wording reads consistently with the rest of the ticket namespace. - (2026-04-05, F009) Re-ran
node scripts/generate-pseudo-locales.cjsafter the locale setup work. The generator reported52pseudo-locale files rebuilt from26English sources, but produced no git diff, confirmingxx/yywere already in sync with the current English namespace. This item is complete as an explicit regeneration checkpoint rather than a content change. - (2026-04-05, F010) Updated
packages/core/src/lib/i18n/config.tsso reused ticket components can resolvefeatures/ticketsoutside/msp/tickets:/msp/settingsnow loadsfeatures/ticketsalongside its existing namespaces, and/msp/service-requestsnow has an explicit route entry loading['common', 'msp/core', 'features/tickets']. Added focused route coverage toserver/src/test/unit/i18n/mspDispatchReportsAdminTimeEntryBatch.test.tsand verified it withcd server && npx vitest run src/test/unit/i18n/mspDispatchReportsAdminTimeEntryBatch.test.ts. - (2026-04-05, F020) Wired
packages/tickets/src/components/TicketingDashboard.tsxtouseTranslation('features/tickets')for the dashboard title, add-ticket CTA, assignee/status/ priority/response-state/due-date/SLA filter placeholders and option labels, including the interpolated before/after-date labels. Validation usednpx eslint packages/tickets/src/components/TicketingDashboard.tsx; it exited 0 with only pre-existing warnings in this file (unused vars, hook deps, legacy JSX apostrophe), so no new lint errors were introduced by the i18n patch. - (2026-04-05, F021) Localized the dashboard’s bulk-selection surface in
packages/tickets/src/components/TicketingDashboard.tsx: selection-menu actions, bulk move/ delete/bundle toolbar buttons, select-all banners, bundle cross-client confirmation, move/ delete/bundle dialog titles and buttons, bulk dialog field labels/placeholders, and bulk toast messages now all resolve throughfeatures/ticketswithcountinterpolation. Re-rannpx eslint packages/tickets/src/components/TicketingDashboard.tsx; it stayed green with only the same file-local pre-existing warnings. - (2026-04-05, F022) Finished the remaining dashboard UX copy in
packages/tickets/src/components/TicketingDashboard.tsx: export tooltip text, client quick- drawer loading/not-found/load-failed fallbacks, bundle-child load errors, destination-board status-load errors, single-delete fallback entity name, delete-validation fallback copy, and the quick-add optimistic-row unknown-client label now all resolve throughfeatures/tickets. This required a small namespace extension inen/fr/es/de/nl/it/pl/features/tickets.json(dashboard.drawer.clientLoadFailed,bulk.move.noStatusesConfigured,bulk.move.loadStatusesFailed,bulk.delete.entityFallback,errors.validateDeletionFailed,errors.deleteTicketUnexpected), followed bynode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjswhich passed withErrors: 0,Warnings: 0. - (2026-04-05, T010) Added
packages/tickets/src/components/TicketingDashboard.i18n.test.tsas a fast source-contract test for the dashboard shell/filter wiring. A jsdom render harness pulled in too much of the ticket/auth runtime for this file, so the test asserts the concretet('...')calls for the page title, add button, primary filter placeholders/options, reset control, bundled toggle, and density-control labels directly inTicketingDashboard.tsx. Verified withcd packages/tickets && npx vitest run src/components/TicketingDashboard.i18n.test.ts. - (2026-04-05, F023) Audited
packages/tickets/src/components/QuickAddTicket.tsxand confirmed the dialog/title/field placeholder slice was already wired even though the checklist was stale: dialog title + automation labels, title/description/client/contact/location/board/ assignee/additional-agent/category/status/priority/due-date placeholders, and the ITIL field labels/help text all resolve throughuseTranslation('features/tickets'). Attempted to validate withcd packages/tickets && npx vitest run src/components/__tests__/QuickAddTicket.boardScopedStatuses.test.tsx, but the package test setup currently fails before executing tests because Vite cannot resolve@alga-psa/core/serverfromshared/core/getSecret.ts. No source change was required in this turn beyond syncing the feature checklist to the implementation state. - (2026-04-05, F023A) The earlier
F023audit was incomplete:QuickAddTicket.tsxstill had hardcoded field-chrome in the ITIL matrix, the additional-agents team-section label, and the unnamed-location fallback. Wired those throughfeatures/tickets, addedquickAdd.unnamedLocationandquickAdd.addTeamMembersto the locale packs, regenerated pseudo-locales, and re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully.npx eslint packages/tickets/src/components/QuickAddTicket.tsxstill reports only the file’s existing warnings (@ts-nocheck, unused imports/types, and pre-existing hook-deps warnings). - (2026-04-05, F024) Completed the remaining user-visible QuickAddTicket action/error copy:
validation messages now use translated field-specific requirements, submit/cancel buttons and
the secondary
Create + View Ticketaction resolve through i18n, clipboard image confirmation copy is translated, and the quick-add toast/error paths for clipboard image upload, team assignment, tag creation, and generic create failures now usefeatures/tickets. AddedquickAdd.createAndView,quickAdd.continueEditing, andquickAdd.clipboardDraftMessageacross the production locales, regenerated pseudo-locales, and re-ran translation validation successfully.npx eslint packages/tickets/src/components/QuickAddTicket.tsxremained green with only the file’s pre-existing warnings. - (2026-04-05, F024 follow-up) Removed duplicated
quickAdd.createAndView,quickAdd.continueEditing, andquickAdd.clipboardDraftMessageentries that were lingering in the production locale files while leaving the canonical localized values intact. Re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs; it still passed withErrors: 0,Warnings: 0. - (2026-04-05, F025) Wired the main
TicketInfo.tsxdetail-surface chrome touseTranslation('features/tickets'): title-edit button titles, unsaved/saved banners, section headings (status/assignee/board/category/priority/due date/SLA/tags/description), assignee/category/date/time placeholders, ITIL labels + matrix text, additional-agent tooltip heading, paused/clear-due-date labels, description empty-state copy, and the email-log tooltip/ aria label now all resolve through the shared ticket namespace. Validation usednpx eslint packages/tickets/src/components/ticket/TicketInfo.tsx; it exited 0 with only the file’s pre-existing warnings (@ts-nocheck, unused symbols, existingany/non-null asserts). - (2026-04-05, F026) Finished the remaining user-facing
TicketInfo.tsxaction/confirmation copy: description save/cancel controls, the footer cancel/save-changes controls, the unsaved- changes discard dialog, and the pasted-images cleanup dialog now all resolve throughfeatures/tickets. Addedinfo.saveChanges,info.saving,info.discardChangesTitle,info.discardChangesMessage,info.discard,info.keepEditing, andinfo.clipboardDraftMessageacrossen/fr/es/de/nl/it/pl, regeneratedxx/yy, and re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully. Focused validation also passed withnpx eslint packages/tickets/src/components/ticket/TicketInfo.tsxandcd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketInfo.boardChangeStatusReselection.test.tsx src/components/ticket/TicketInfo.richText.contract.test.ts. - (2026-04-05, F026 test harness) The existing
TicketInforegression tests needed upkeep to reflect the current component composition:TicketInfo.boardChangeStatusReselection.test.tsxnow mocksuseTranslationanduseDocumentsCrossFeature, andTicketInfo.richText.contract.test.tsasserts the translated clipboard-dialog wiring instead of the retired hardcoded"Keep Images"literal. - (2026-04-05, F027) Wired the main
TicketProperties.tsxside-panel chrome throughuseTranslation('features/tickets'): the time-entry card title/timer label/controls, work- description label + placeholder, disabled-timer message, tracked-interval heading, contact-info card title and field labels, contact/location placeholders and empty states, client/contact fallback labels, appointment-request drawer headings, assigned-team fallbacks, primary-agent section labels, scheduled-hours text, and additional-agent picker labels/placeholders are now localized. Addedproperties.timeEntryandproperties.ticketTimeracrossen/fr/es/de/nl/it/pl, regeneratedxx/yy, and re-rannode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjssuccessfully. - (2026-04-05, F027 validation) Focused regression checks passed with
npx eslint packages/tickets/src/components/ticket/TicketProperties.tsx(warnings only, no new errors) andcd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketProperties.liveTimerPolicy.test.tsx src/components/ticket/__tests__/ticket-properties-inline-contact.test.tsx. Those tests now mockuseTranslation, and the inline-contact harness also mocksuseQuickAddClientso the quick-add contact dialog continues to render in isolation. - (2026-04-05, F028) Finished the remaining interactive
TicketProperties.tsxcopy without adding new keys: the contact/client/location picker footer buttons now use sharedactions.cancel/actions.save, and the team removal/switch dialog now resolves its title, option labels, empty state, and confirm/cancel buttons through existingproperties.*andactions.*keys. The only English strings still visible in the file are inside a commented-out legacy team block and do not render at runtime. - (2026-04-05, F028 validation) Re-ran
npx eslint packages/tickets/src/components/ticket/TicketProperties.tsx(same pre-existing warnings only) pluscd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketProperties.liveTimerPolicy.test.tsx src/components/ticket/__tests__/ticket-properties-inline-contact.test.tsx, which stayed green after the dialog/button wiring. - (2026-04-05, F030) Wired the
CategoriesSettings.tsxpage shell touseTranslation('features/tickets')without expanding the namespace: the page heading, board filter option/placeholder, and the category table’sName/Board/Order/Actionsheaders now resolve through existingsettings.categories.*,fields.board, andsettings.display.columns.actionskeys. This leaves the add/edit/import/delete dialog and toast copy isolated forF031. - (2026-04-05, F030 validation) Verified the wiring with
cd packages/tickets && npx vitest run src/components/settings/__tests__/CategoriesSettings.contract.test.tsandnpx eslint packages/tickets/src/components/settings/CategoriesSettings.tsx(warnings only, no new errors). - (2026-04-05, F031) Finished the
CategoriesSettings.tsxdialog/error flow wiring using the existingsettings.categories.*keys: fetch/save/import/delete validation messages, success toasts, dropdown action labels, edit/import/conflict dialog titles, form labels/placeholders, import target-board help text, import-table column labels, conflict resolution copy, and the related cancel/update/import buttons now all resolve throughfeatures/tickets. No locale-file expansion was needed for this pass because the namespace additions fromF002already covered the settings copy. - (2026-04-05, F031 validation) Re-ran
cd packages/tickets && npx vitest run src/components/settings/__tests__/CategoriesSettings.contract.test.tsafter updating its stale raw-string assertions to the newt(...)calls, and re-rannpx eslint packages/tickets/src/components/settings/CategoriesSettings.tsx(warnings only, no new errors). - (2026-04-05, F032) Started the
TicketExportDialog.tsxpass by wiring the configure-step chrome touseTranslation('features/tickets'): the dialog title, export-field labels, field- picker heading, select-all/deselect-all toggle, and selected-field count now resolve through the existingexport.*,fields.*,properties.contact, andsettings.display.columns.tagskeys. No namespace expansion was required for this slice. - (2026-04-05, F032 validation) Verified the configure-step patch with
npx eslint packages/tickets/src/components/TicketExportDialog.tsxand a direct source grep confirming the previous hardcoded configure labels were replaced byt(...)calls. The remaining export-action strings are intentionally deferred toF033. - (2026-04-05, F033) Completed the
TicketExportDialog.tsxexport-state wiring: the export summary, applied-filters summary, cancel/export CTA labels, exporting-state message, completion title/message, done button, and export failure handling now all resolve throughexport.*andactions.cancel. The count-based strings use the existing pluralized export keys instead of manualticket/ticketsconcatenation. - (2026-04-05, F033 validation) Re-ran
npx eslint packages/tickets/src/components/TicketExportDialog.tsxand a direct source grep to confirm the former hardcoded action/progress strings now route throught(...). No locale-file changes were needed for this pass. - (2026-04-05, F034) Wired the core
TicketMaterialsCard.tsxchrome throughuseTranslation('features/tickets')using the pre-existingmaterials.*keys: the card title, add-button label, add-material form labels/placeholders, price-loading/no-price copy, quantity/ total/description fields, cancel/add CTA labels, the materials table headers, billed/pending badges, unknown-product fallback, unbilled-per-currency summary, and add-success toast now all resolve through the shared tickets namespace. This pass intentionally left validation/error/ delete/empty-state copy forF035so the remaining work stays atomic. - (2026-04-05, F034 validation) Verified with
npx eslint packages/tickets/src/components/ticket/TicketMaterialsCard.tsx, which exited 0. - (2026-04-05, F035) Finished the remaining
TicketMaterialsCard.tsxuser-facing copy: load/add/remove failures now useerrors.loadMaterials/errors.addMaterial/errors.removeMaterial, add-form validation toasts usevalidation.materials.*, the delete success toast usesmaterials.removeSuccess, and the loading/empty/client-required helper states now resolve throughmaterials.*. This required one namespace addition,materials.clientRequired, inen/fr/es/de/nl/it/pl/features/tickets.json, followed by pseudo-locale regeneration. - (2026-04-05, F035 validation) Re-ran
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs(Errors: 0,Warnings: 0) andnpx eslint packages/tickets/src/components/ticket/TicketMaterialsCard.tsx, which exited 0. - (2026-04-05, F036) Wired the main
DisplaySettings.tsxlabels throughuseTranslation('features/tickets')without expanding the namespace: the response-state tracking section title/description/toggle labels, the display-preferences heading and description, the date/time-format label, the date-format preview options, the ticket-list columns heading, every column checkbox label plus required suffix, and the tags visibility/ layout labels now resolve throughsettings.display.*and sharedfields.*keys. The save CTA and save/failure copy are intentionally left forF037. - (2026-04-05, F036 validation) Verified with
npx eslint packages/tickets/src/components/settings/DisplaySettings.tsx, which exited 0. - (2026-04-05, F037) Finished the existing
DisplaySettings.tsxsave path: the success toast, save-failure message, and save-button/saving labels now resolve throughsettings.display.saveSuccess,settings.display.saveFailed,settings.display.saving, and sharedactions.save. The checklist mentioned reset/per-user controls, but this component currently only exposes a save action; no reset UI exists to wire in this batch. - (2026-04-05, F037 validation) Re-ran
npx eslint packages/tickets/src/components/settings/DisplaySettings.tsx, which exited 0. - (2026-04-05, F040) Wired
TicketWatchListCard.tsxthroughuseTranslation('features/tickets'): the card title, add-mode tabs, contact-scope toggle, picker/email placeholders, add button labels, empty state, validation/update-failure messages, recipient type badges, and remove-button aria/title copy now resolve throughwatchList.*. This required a small namespace addition inen/fr/es/de/nl/it/pl/features/tickets.json(watchList.userBadge,watchList.contactBadge,watchList.removeWatcher), followed by pseudo-locale regeneration. - (2026-04-05, F040 validation) Re-ran
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs(Errors: 0,Warnings: 0) andnpx eslint packages/tickets/src/components/ticket/TicketWatchListCard.tsx, which exited 0. A focusedTicketWatchListCard.test.tsxrun still fails before assertions because the existing harness does not provide the now-requireduseTranslation/useFeatureFlagcontext; that follow-up is queued as test work rather than a source blocker for the feature wiring. - (2026-04-05, F041) Wired
TicketEmailNotifications.tsxthroughuseTranslation('features/tickets'): the card title, table column titles, loading/empty states, load-more button, and fallback unknown-error string now resolve throughemailNotifications.*. The sent-at formatter also now uses the active i18n language instead of hardcodeden-US, so locale changes affect the timestamp preview format too. - (2026-04-05, F041 validation) Verified with
npx eslint packages/tickets/src/components/ticket/TicketEmailNotifications.tsx, which exited 0. There is no dedicated component test file for this card yet; only higher-level TicketDetails tests mock it out. - (2026-04-05, F042) Wired
ResponseStateSelect.tsxthroughuseTranslation('features/tickets'): the dropdown option labels (awaitingClient,awaitingInternal,clear), the select placeholder, the display heading, and the empty-stateNot settext now all resolve throughresponseState.*. Removed the stale unusedgetResponseStateLabelimport while touching the file. - (2026-04-05, F042 validation) Verified with
npx eslint packages/tickets/src/components/ResponseStateSelect.tsx, which exited 0. - (2026-04-05, F043) Wired
QuickAddCategory.tsxthroughuseTranslation('features/tickets'): the dialog title, category/board/parent labels, placeholders, helper copy, validation errors, create success toast, create failure fallback, and cancel/create button labels now resolve throughsettings.categories.*,validation.category.*, sharedactions.*, and the existing category error keys. This required a smallsettings.categoriesexpansion inen/fr/es/de/nl/it/pl/features/tickets.jsonfor the inline creator’s helper strings (noneTopLevelCategory,noBoard,loadingBoards,boardRequiredHelp,parentCategoryOptional,selectParentCategory,parentHelpWithBoard,parentHelpWithoutBoard,createSuccess,creating), followed by pseudo-locale regeneration. - (2026-04-05, F043 validation) Re-ran
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs(Errors: 0,Warnings: 0) andnpx eslint packages/tickets/src/components/QuickAddCategory.tsx, which both passed.cd packages/tickets && npx vitest run src/components/__tests__/QuickAddCategory.test.tsxnow passes 7/8 tests; the remaining failure is a stale board-fetch call-count assertion (expected 1, got 3) rather than a localization regression. - (2026-04-05, F044)
TicketDetailsContainer.tsxdoes not render its own loading/not-found chrome; the user-visible copy in this wrapper is toast/error handling. Wired those container messages throughuseTranslation('features/tickets'): auth-required update/comment toasts, generic ticket-updated success, batch-save success/failure, and add-comment success/failure now resolve througherrors.*,messages.ticketUpdated,messages.commentAdded, andinfo.changesSaved. This required one new locale key,messages.commentAdded, inen/fr/es/de/nl/it/pl/features/tickets.json, followed by pseudo-locale regeneration. - (2026-04-05, F044 validation) Re-ran
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs(Errors: 0,Warnings: 0), plusnpx eslint packages/tickets/src/components/ticket/TicketDetailsContainer.tsx(warnings only; the file’s pre-existinganywarnings remain unchanged).cd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketDetailsContainer.description.test.tsxstill passes, whileTicketDetailsContainerCreateTask.test.tsxcurrently fails before tests run becausenext-authtries to importnext/serverin the package test environment. - (2026-04-05, F045) Wired
CategoryPicker.tsxthroughuseTranslation('features/tickets')without expanding the namespace: the reflection title, default placeholder,No Categoryoption, ITIL badge label, add-new label, and the selected/ excluded summary text now resolve throughcategoryPicker.*, including pluralized count copy. - (2026-04-05, F045 validation)
npx eslint packages/tickets/src/components/CategoryPicker.tsxexited with warnings only; the remaining warnings are pre-existing (dataAutomationType, unusedpath, and non-null assertions). There is no dedicated CategoryPicker render test yet; existing coverage is limited to passthrough/source-contract tests and upstream component mocks. - (2026-04-05, F046) Wired
CommentMetadataDebugModal.tsxthroughuseTranslation('features/tickets'): dialog title, summary/raw-metadata section labels, empty summary copy, copy-button states, and close button now resolve through the existingdebug.*keys. No namespace expansion was needed for this pass. - (2026-04-05, F046 validation)
npx eslint packages/tickets/src/components/ticket/CommentMetadataDebugModal.tsxexited 0, andcd packages/tickets && npx vitest run src/components/ticket/CommentMetadataDebugModal.test.tsxstill passes. The test emits the usualreact-i18nextmissing-instance warning, but it does not fail because the fallback text remains identical. - (2026-04-05, F047) Wired
TicketNavigation.tsxthroughuseTranslation('features/tickets'): the previous/next navigation buttons now source theiraria-labelvalues from the existingnavigation.previousTicket/navigation.nextTicketkeys instead of hardcoded English. No locale-file changes were needed because those keys were already added during the earlier namespace expansion. - (2026-04-05, F047 validation)
npx eslint packages/tickets/src/components/ticket/TicketNavigation.tsxexited 0. - (2026-04-05, F048) Wired the visible
TicketingDashboardContainer.tsxwrapper copy throughuseTranslation('features/tickets'): the auth-required toast, the fetch-failure message passed tohandleError, and the board-name fallback used when normalizingeffectiveOptions.boardOptionsnow all resolve through existingerrors.*/bulk.move.unnamedBoardkeys instead of hardcoded English. - (2026-04-05, F048 validation)
npx eslint packages/tickets/src/components/TicketingDashboardContainer.tsxexited 0 after adding the newtdependency to the fetch callback. - (2026-04-05, F049) Audited
TicketOriginBadge.tsxandResponseSourceBadge.tsxagainst the existing namespace and confirmed no new keys were needed:origin.*already covers all badge enum values andresponseSource.*already covers the comment-source variants used in MSP. Both badge components now calluseTranslation('features/tickets')so they can fall back to localized labels even when a parent omits thelabelsprop, while still preserving caller- provided overrides. - (2026-04-05, F049 validation)
npx eslint packages/tickets/src/components/TicketOriginBadge.tsx packages/tickets/src/components/ResponseSourceBadge.tsxexited 0. Focused render-contract coverage also still passes withcd packages/tickets && npx vitest run src/components/TicketOriginBadge.render.test.tsx src/components/ResponseSourceBadge.render.test.tsx; the run emits the expectedreact-i18nextno-instance warning because these server-render tests do not mount anI18nProvider, but the assertions remain green through fallback text. - (2026-04-05, F050) Audited the remaining zero-string candidates and confirmed they are
N/A for translation wiring:
TicketDetailsSkeleton.tsx,TicketListSkeleton.tsx, andAgentScheduleDrawer.tsxrender no user-visible copy at runtime;AgentScheduleDrawerStyles.tsxcontains CSS comments only.TicketDetailsSkeleton.tsxincludes descriptive JSX comments, but they do not render into the DOM and therefore do not belong in the translation namespace. - (2026-04-05, F060) Ran
node scripts/validate-translations.cjsas the acceptance parity check after finishing the remaining ticket-component wiring. Validation passed across all 6 production locales plus both pseudo-locales withErrors: 0andWarnings: 0. - (2026-04-05, F061) Updated the parent MSP i18n plan at
.ai/translation/MSP_i18n_plan.mdto mark batch2b-21acomplete. The final Englishfeatures/tickets.jsonleaf-string count is 887, and the completion note records both the 23-component MSP wiring pass and the supporting route-namespace coverage added for/msp/settingsand/msp/service-requests. - (2026-04-05, F062) Curated the scratchpad closeout notes to remove stale intermediate key
counts, record the final 887-leaf namespace size, reaffirm the decision to keep MSP ticket
copy in the shared
features/ticketsnamespace, and explicitly note that onlytests.jsonwork remains after feature completion. - (2026-04-05, T001) Re-ran the full lang-pack loop:
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs. The generator rebuilt52pseudo-locale files from26English sources and the validator passed withErrors: 0,Warnings: 0. The only remaining unstaged locale diffs in the worktree are unrelated pre-existing pseudo-locale changes underserver/public/locales/xx|yy/msp/*, which were already present before this test pass and were intentionally left out of the commit. - (2026-04-05, T011) Extended
TicketingDashboard.i18n.test.tswith a pseudo-locale contract for the dashboard shell and bulk-dialog chrome. The test readsserver/public/locales/xx/features/tickets.jsondirectly and verifies that the visible keys wired inTicketingDashboard.tsxresolve to pseudo-text (11111) rather than English, while preserving{{count}}interpolation markers for the move/delete success strings. - (2026-04-05, T012) Added a focused interpolation contract to
TicketingDashboard.i18n.test.tsfor the bulk move/delete success toasts. The test asserts thatTicketingDashboard.tsxpassescountintot('bulk.move.success', …)andt('bulk.delete.success', …)with singular/plural default values, so the UI stays aligned with the_one/_otherlocale entries instead of drifting back to manual string assembly. - (2026-04-05, T013) Extended
TicketingDashboard.i18n.test.tsagain to cover the dashboard-local loading/error/empty feedback branches: client drawer loading/not-found/load- failed copy, bundled-ticket and deletion error paths, and the bulk move/delete “No tickets selected” empty states now all have explicitt(...)source assertions. - (2026-04-05, T020) Added
QuickAddTicket.i18n.test.tsas a source-contract test for the quick-add dialog shell. The first assertion covers theuseTranslation('features/tickets')hook plus the core dialog/field/placeholder wiring for title, description, client/contact/ location/board, assignee/additional-agents, category/status/priority, and due-date controls. - (2026-04-05, T021) Extended
QuickAddTicket.i18n.test.tswith the validation branch: the required-field checks now have explicit assertions for the translated title/board/status/ priority/impact/urgency/client errors plus thequickAdd.requiredFieldsHeadingbanner text. - (2026-04-05, T022) Added interpolation coverage for the quick-add tag-creation partial-
failure toast. The new source-contract assertion checks both the partial-success and catch
branches to ensure
quickAdd.tagCreatePartialFailurealways receivescountplus singular/ plural default values. - (2026-04-05, T023) Finished the QuickAdd source-contract set with a pseudo-locale check
against
xx/features/tickets.json. The test verifies that the core dialog labels/placeholders plus thequickAdd.tagCreatePartialFailure_*entries collapse to pseudo-fill values rather than English. - (2026-04-05, T030) Added
ticket/TicketInfo.i18n.test.tsas the TicketInfo source- contract harness. The first assertion covers the detail header and field chrome across the mixedfields.*,info.*,itil.*, andsettings.display.columns.tagskeys used by the main detail pane. - (2026-04-05, T031) Extended
TicketInfo.i18n.test.tswith the save/cancel/editing surface: title-edit affordances, description edit controls, footer save state, unsaved-changes discard dialog, and the pasted-images cleanup dialog now all have directt(...)assertions. - (2026-04-05, T032) Finished the TicketInfo contract set with an
xxpseudo-locale check. The test verifies that the component’s mixedfields.*,info.*,itil.*,conversation.*, andquickAdd.*dependencies all resolve to pseudo-fill values instead of leaking English defaults. - (2026-04-05, T040) Added
ticket/TicketProperties.i18n.test.tsand covered the core side-panel chrome: time-entry/timer labels, work-description placeholder, contact/location/ agent-team headings, primary/additional-agent copy, the additional-agent picker placeholder, and the shared quick-add team-section label all now have direct source assertions. - (2026-04-05, T041) Extended
TicketProperties.i18n.test.tswith the interactive dialog surface fromF028. The checklist wording still mentions a board-change reselection prompt, but this component’s actual translated confirmation flow is the team-assignment removal/switch dialog; the test now asserts that title, option labels, empty state, and confirm/cancel actions all route throught(...). - (2026-04-05, T042) Finished the TicketProperties contract set with an
xxpseudo-locale check covering the side-panel headings, additional-agent picker, team-removal dialog strings, shared action labels, and the interpolatedproperties.ticketTimervalue. - (2026-04-05, T050) Extended the existing
settings/__tests__/CategoriesSettings.contract.test.tssource-contract file with a board-scope/tree-label assertion covering the page title, theAll Boardsscope selector label, and the translated Name/Board/Order/Actions table headings used by the category tree. - (2026-04-05, T051) The same CategoriesSettings contract file now also covers the add/edit/
delete validation and toast surface: fetch/validate/delete failures, name-required validation,
the edit-dialog title, delete-dialog entity fallback, and the save/delete success copy all have
explicit
t(...)assertions. - (2026-04-05, T060) Added
TicketExportDialog.i18n.test.tsas a source-contract test for the export configure step. It covers the dialog title, selected-ticket/applied-filter summary copy, field-picker heading, select-all toggles, selected-count text, export CTA, and the column label-key wiring that reusesfields.*,properties.contact, andsettings.display.columns.tags. - (2026-04-05, T061) Extended the export dialog contract with the action-state and error
path: export failure handling, exporting-state copy, completion title/message, done button, and
shared cancel action are now explicitly asserted in
TicketExportDialog.tsx. - (2026-04-05, T070) Added
ticket/TicketMaterialsCard.i18n.test.tsto cover the materials card’s main chrome: card title, add-material dialog labels/placeholders, price/ quantity/total/description labels, materials table headers, and billed/pending status badges. - (2026-04-05, T071) Extended the materials card contract with the error/validation/empty-
state path: load/add/remove failures, form validation toasts, remove-success copy, the loading
and empty helpers, and the
materials.clientRequiredguard are now all asserted. - (2026-04-05, T080) Added
settings/DisplaySettings.i18n.test.tsas a source-contract test for the display-settings chrome: response-state tracking copy, preferences/date-time labels, column section heading, required suffix, and tags layout toggles are all now pinned tot(...)calls. - (2026-04-05, T081) Extended the display-settings contract with the save path. The test now asserts the translated success toast, failure message, saving state, and save button label.
- (2026-04-05, T090) Added
ticket/TicketWatchListCard.i18n.test.tsas a source-contract test covering the watch-list title, tabs, scope toggles, picker placeholders, add buttons, empty state, and remove-button copy. While checking the older jsdom harness for later regression work, I also restored its missinguseFeatureFlag/useTranslationtest mocks so it can be repaired incrementally underT120instead of crashing immediately on mount. - (2026-04-05, T091) Added
ticket/TicketSmallComponents.i18n.test.tsand started using it for the remaining small-ticket-component contracts. The first assertion covers the email notifications card title, table headings, loading/empty states, unknown-error fallback, and the load-more CTA. - (2026-04-05, T092) The shared small-component contract file also now covers
ResponseStateSelect.tsx: awaiting-client/awaiting-internal/clear option labels, the select placeholder, the response-state heading, and theNot setfallback text. - (2026-04-05, T093) The same shared contract file now covers
QuickAddCategory.tsxtoo: dialog title, field labels/placeholders, board/parent helpers, validation errors, success toast, and generic create failure copy are all asserted throught(...). - (2026-04-05, T094)
ticket/TicketSmallComponents.i18n.test.tsalso coversTicketDetailsContainer.tsx: auth-required toasts, update/comment success messages, field- update/save failure handling, and the addedmessages.commentAddedcopy are all pinned to the shared ticket namespace. - (2026-04-05, T095) The shared small-component contract now also covers
CategoryPicker.tsx: title, default placeholder,No Category, ITIL badge, selected/excluding summary builders, and the add-new label are all asserted against translation calls. - (2026-04-05, T096)
ticket/TicketSmallComponents.i18n.test.tsalso coversTicketNavigation.tsx, asserting that both previous/next buttonaria-labelvalues resolve throughnavigation.previousTicket/navigation.nextTicket. - (2026-04-05, T097) Re-ran
TicketOriginBadge.render.test.tsxafter the badge fallback wiring. All origin enum render cases still pass, which now exercises the component’s localized fallback labels directly (with the usual no-instancereact-i18nextwarning in this server- render harness). - (2026-04-05, T098) Re-ran
ResponseSourceBadge.render.test.tsxalongside the origin badge test. All portal/email source variants still render correctly after making the component’slabelsprop optional and translation-backed. - (2026-04-06, T120) Repaired the pre-existing package-level ticket regression harnesses and
re-ran the planned subset successfully:
QuickAddTicket.boardScopedStatuses.test.tsx,ticket-inline-add-prefill.test.tsx,TicketDetailsContainer.description.test.tsx,TicketInfo.boardChangeStatusReselection.test.tsx,TicketProperties.liveTimerPolicy.test.tsx,TicketWatchListCard.test.tsx, andticket-properties-inline-contact.test.tsxnow pass together (43tests total). The fixes were test-only: added the missing@alga-psa/core/servervitest alias inpackages/tickets/vitest.config.ts, updated the quick-add tests to mock the current quick-add client context and rich-text upload session, and refreshed stale watch-list expectations for the collapsed-by-default card, tabbed add modes, filtered duplicate users, and localized badge text. - (2026-04-06, T121) Re-ran the badge regression suite after the optional-
labelsfallback wiring:TicketOriginBadge.render.test.tsxandResponseSourceBadge.render.test.tsxboth pass unchanged (12tests total) undercd packages/tickets && npx vitest run src/components/TicketOriginBadge.render.test.tsx src/components/ResponseSourceBadge.render.test.tsx. The usualreact-i18nextno-instance warning still appears in this server-render harness, but it remains non-fatal because the tests assert the fallback labels directly. - (2026-04-06, T122) Re-ran
cd packages/tickets && npx vitest run src/components/ticket/CommentMetadataDebugModal.test.tsx. The single debug-modal regression test still passes after the translation wiring, again with the expected non-fatalreact-i18nextno-instance warning from this lightweight render harness. - (2026-04-06, T123) Re-ran
cd packages/tickets && npx vitest run src/components/ticket/AgentScheduleDrawer.test.tsx. The re-export/scheduling shim still passes unchanged (2tests), confirming theF050zero-string audit did not disturb the drawer wrapper. - (2026-04-06, T124) Repaired and re-ran the project-task regression tests:
TicketDetailsContainerCreateTask.test.tsxandTicketDetailsCreateTask.test.tsxnow pass together (3tests) undercd packages/tickets && npx vitest run src/components/ticket/__tests__/TicketDetailsContainerCreateTask.test.tsx src/components/ticket/__tests__/TicketDetailsCreateTask.test.tsx. The fixes were again test-only: add the missingnext/server/next-auth/ router mocks to the container harness, align theTicketDetailsCreateTaskchild mocks with the component’s actual import paths, and update that test to assert the current contract (renderCreateProjectTaskis passed through toTicketInfo.renderProjectTaskActions) instead of the retired header-button placement. The suite still logs pre-existing secret/auth fallback warnings from ticket display-settings side effects, but those no longer block execution. - (2026-04-06, T100) Added
server/src/test/unit/app/msp/tickets/page.i18n.test.tsxas a page+layout integration harness for/msp/tickets. The test runsserver/src/app/msp/tickets/page.tsxwith mocked ticket-data actions, wraps the result inMspLayoutClient, and uses a lightweight in-memory@alga-psa/ui/lib/i18n/clientmock that only returns sentinel strings whenI18nWrapper -> getNamespacesForRoute('/msp/tickets')includesfeatures/tickets. That makes the assertion stronger than a source grep: the dashboard frame must render translated values for title/add/filter/reset chrome instead of falling back to raw English literals. Verified withcd server && npx vitest run src/test/unit/app/msp/tickets/page.i18n.test.tsx. - (2026-04-06, T101) Extended the same
/msp/ticketspage+layout harness with a German locale case that checks the shippedde/features/tickets.jsoncopy used for the visible list frame (Ticket-Dashboard,Ticket hinzufügen,Alle Zuständigen, etc.). This keeps the test integration-focused while also pinning the route to real translated strings instead of a second synthetic locale. Re-verified withcd server && npx vitest run src/test/unit/app/msp/tickets/page.i18n.test.tsx. - (2026-04-06, T102) Expanded the
/msp/ticketspage+layout harness again to include a lightweight quick-add dialog shell keyed off the realQuickAddTickettranslation paths. The new German assertion covers dialog title, primary placeholders, assignee/category/status/ priority/due-date copy, and the cancel/create/create-and-view actions so the route-level test now smoke-tests both the list frame and the add-ticket flow under/msp/tickets. Re-verified withcd server && npx vitest run src/test/unit/app/msp/tickets/page.i18n.test.tsx. - (2026-04-06, T103) Added
server/src/test/unit/app/msp/tickets/detail.page.i18n.test.tsxas the detail-route companion harness for/msp/tickets/[id]. It runsserver/src/app/msp/tickets/[id]/page.tsxunderMspLayoutClient, stubs the detail container, and asserts German ticket-detail chrome drawn from theTicketInfo,TicketProperties,TicketMaterialsCard, andTicketWatchListCardkey families so the shared/msp/tickets/:id -> features/ticketsnamespace path is covered end-to-end. Verified withcd server && npx vitest run src/test/unit/app/msp/tickets/detail.page.i18n.test.tsx. - (2026-04-06, T104) Added
server/src/test/unit/app/msp/settings/ticketing.i18n.test.tsxto cover the ticket-settings route underMspLayoutClient. This harness keeps the realTicketingSettingsshell and tab routing, but stubsCategoriesSettingsandDisplaySettingswithfeatures/tickets-backed content so/msp/settings -> features/ticketsis proven for the two MSP ticket settings surfaces added inF030-F037. Verified withcd server && npx vitest run src/test/unit/app/msp/settings/ticketing.i18n.test.tsx. - (2026-04-06, T105) Added
server/src/test/unit/app/msp/service-requests/editor.i18n.test.tsxto cover the/msp/service-requests/[definitionId]reuse path introduced byF010. The route harness mocksServiceRequestDefinitionEditorPagedown to aCategoryPicker-shaped shell, then verifies the German picker placeholder/badge/summary/add-new copy only renders whenMspLayoutClientloadsfeatures/ticketsfor/msp/service-requests/*. Verified withcd server && npx vitest run src/test/unit/app/msp/service-requests/editor.i18n.test.tsx. - (2026-04-06, T106) Added
server/src/test/unit/layout/QuickCreateDialog.ticket.i18n.integration.test.tsxfor the layout-level reuse ofQuickAddTicketinsideQuickCreateDialog. This harness mounts the real layout dialog on a non-ticket MSP route and stubs only the embedded ticket quick-add body, so the German assertions cover the actual global quick-create shell without pulling the entireQuickAddTicketruntime into the test. Verified withcd server && npx vitest run src/test/unit/layout/QuickCreateDialog.ticket.i18n.integration.test.tsx. - (2026-04-06, T107) Expanded the
/msp/ticketspage harness with plural-aware bulk-action strings. The in-memory translation mock now resolves_one/_othervariants based oncount, and the new assertion covers the German bulk move/delete dialog titles, confirmation copy, and success summaries (2 Tickets verschoben,2 Tickets gelöscht) so interpolation remains aligned with the locale pack instead of falling back to manual English assembly. Re-verified withcd server && npx vitest run src/test/unit/app/msp/tickets/page.i18n.test.tsx. - (2026-04-06, T108) Added
server/src/test/unit/i18n/ticketsPseudoLocale.integration.test.tsxas the cross-route pseudo- locale smoke test. It drivesMspLayoutClientunderlocale='xx'for/msp/tickets,/msp/tickets/[id], and/msp/settings, with namespace-gatedfeatures/ticketsstubs for the list, detail, and settings surfaces. The assertions require pseudo fill text (11111) and reject the corresponding English defaults, which makes this a fast regression check for route namespace leakage across the main MSP ticket surfaces. Verified withcd server && npx vitest run src/test/unit/i18n/ticketsPseudoLocale.integration.test.tsx.
Risks
- Layout breakage from long translations (German is typically 30% longer than English). Mitigation: pseudo-locale test (xx/yy expand strings) exercises button/column widths.
- Reused components (QuickAddTicket in QuickCreateDialog, CategoryPicker in ServiceRequests) may load different namespace contexts. Mitigation: i18next falls back to loaded namespaces; verify via integration tests T105-T106.
- Settings routes may not load
features/ticketsnamespace. Mitigation: check ROUTE_NAMESPACES before sub-batch B; add'features/tickets'to/msp/settingsentry if needed. - Existing tests may break if they rely on exact English strings. Mitigation: use
t('key', 'Exact English')fallback so rendered text is identical until locale changes; update tests that assert on text to use i18next test bootstrap where necessary.