Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
11 KiB
PRD — MSP i18n Batch 2b-21a: Tickets Migration
- Slug:
2026-04-05-msp-i18n-tickets-migration - Date:
2026-04-05 - Status: Draft
- Parent plan:
/.ai/translation/MSP_i18n_plan.md(Batch 2b-21a)
Summary
Wire the existing features/tickets namespace into 23 unwired MSP ticket components in
packages/tickets/src/components/. Infrastructure is already in place: the JSON file
(features/tickets.json, 147 keys across 7 languages) exists and ROUTE_NAMESPACES already
loads it for /msp/tickets. The client-portal equivalents are 100% wired; this batch closes
the MSP-side gap.
Problem
MSP users currently see translated navigation, sidebar, dashboard, and settings chrome, but
the ticket module — one of the highest-traffic MSP surfaces — is still hardcoded English.
5 of 28 production ticket components (18%) are wired; 23 remain. Because ROUTE_NAMESPACES
for /msp/tickets already loads features/tickets, the translation assets are downloaded
but unused. Non-English MSP users switch from a translated sidebar into an English wall of
tickets every day.
This is not a new-translation project — it is a mechanical wiring pass plus gap-filling for MSP-specific strings (bulk operations, dispatch hooks, display settings, dashboard filters) that the client portal didn't need.
Goals
- Wire
useTranslation('features/tickets')into all 23 unwired production MSP ticket components - Identify and add MSP-specific keys missing from
features/tickets.json(bulk actions, export options, watch list, materials, display/category settings, dashboard filters, agent schedule drawer) - Regenerate translations for 6 non-English locales + 2 pseudo-locales for any new keys
- Preserve 100% test pass rate and zero user-facing regressions
- Measurable: MSP tickets coverage goes from 18% → 100% of production components wired
Non-goals
- Retranslating existing 147 keys — they already have 7-language coverage and are actively used
- Moving
features/tickets.jsonkeys into a separatemsp/tickets.json— the shared namespace is the correct design per the parent plan - Translating ticket content itself (titles, descriptions, comments) — those are tenant data, not UI chrome
- Wiring test-only components (23
.test.tsxfiles inpackages/tickets/src/components/) - Extending
ROUTE_NAMESPACES—/msp/ticketsis already wired; only verify route coverage - Translating EE-only ticket components (ITIL extras) beyond what's exported through
@alga-psa/tickets/components
Users and Primary Flows
Primary user: MSP technicians, dispatchers, and managers using non-English UI language (any of fr, es, de, nl, it, pl).
Primary flows affected:
/msp/ticketslist — dashboard, filters, bulk actions, quick-add/msp/tickets/[id]detail — ticket info, properties, materials, watch list, email notifications/msp/settings/*for ticket configuration — categories, display settings, response states/msp/service-requests/*— CategoryPicker reused from tickets package- Quick-create dialog (global nav) — QuickAddTicket reused in
layout/QuickCreateDialog
UX / UI Notes
- No visual changes. Text replaced inline via
t('key', 'English fallback'). - Toast messages (
toast.success/toast.error) also translated. throw new Error('...')strings that surface to users (handled by error boundaries) translated; those that only appear in logs stay in English.- Skeletons, badges, and style-only files (
TicketOriginBadge,ResponseSourceBadge,TicketListSkeleton,TicketDetailsSkeleton,AgentScheduleDrawerStyles) have no or 0-1 strings — confirm and skip if truly zero.
Requirements
Functional Requirements
Sub-batch A: Large dashboard + quick-add components (4 files, ~150-200 strings)
| Component | LOC | Est. strings | Key content |
|---|---|---|---|
| TicketingDashboard.tsx | 2,024 | ~65 | Page title, add button, filter chrome (status/priority/assignee/response state/due date), bulk actions (move/delete/bundle), empty states, toast messages |
| QuickAddTicket.tsx | 1,596 | ~50 | Dialog title, field labels, placeholders, validation errors, toast messages, tag-creation errors |
| ticket/TicketInfo.tsx | 1,587 | ~45 | Ticket detail header, field labels, action buttons, confirmations |
| ticket/TicketProperties.tsx | 1,234 | ~45 | Side panel labels, inline edit controls, status/priority/board selects, assignment UI |
Sub-batch B: Settings + mid-size (4 files, ~90 strings)
| Component | LOC | Est. strings | Key content |
|---|---|---|---|
| settings/CategoriesSettings.tsx | 865 | ~30 | Board/channel scoping, category tree, add/edit/delete dialogs |
| TicketExportDialog.tsx | 243 | ~25 | Export format options, column picker, date range, toast messages |
| ticket/TicketMaterialsCard.tsx | 444 | ~20 | Materials list, add material dialog, cost display, confirmations |
| settings/DisplaySettings.tsx | 250 | ~20 | Column visibility, sort options, per-user preferences |
Sub-batch C: Small + utility components (15 files, ~50-70 strings)
| Component | LOC | Est. strings | Notes |
|---|---|---|---|
| ticket/TicketWatchListCard.tsx | 512 | ~10 | Watcher list, add/remove |
| ticket/TicketEmailNotifications.tsx | 172 | ~8 | Notification toggle labels |
| ResponseStateSelect.tsx | 110 | ~6 | Response state options |
| QuickAddCategory.tsx | 274 | ~6 | Inline category creation |
| ticket/TicketDetailsContainer.tsx | 252 | ~5 | Loading/error states |
| CategoryPicker.tsx | 274 | ~5 | Picker placeholder + empty state |
| ticket/CommentMetadataDebugModal.tsx | 97 | ~4 | Debug modal (dev only, low priority) |
| ticket/TicketNavigation.tsx | 143 | ~3 | Prev/next buttons |
| TicketingDashboardContainer.tsx | 607 | ~2 | Mostly logic, few strings |
| ticket/TicketDetailsSkeleton.tsx | 155 | 0 | Skeleton — confirm zero |
| TicketOriginBadge.tsx | 143 | 0-2 | Origin labels — may need small key set |
| ticket/AgentScheduleDrawerStyles.tsx | 93 | 0 | Styles only |
| ResponseSourceBadge.tsx | 56 | 0-2 | Source labels — may need small key set |
| TicketListSkeleton.tsx | 32 | 0 | Skeleton |
| ticket/AgentScheduleDrawer.tsx | 16 | 0 | Re-export shim |
Namespace key gaps to fill (preliminary):
The current features/tickets.json covers client-portal needs. Likely MSP gaps:
bulk.*— bulk move/delete/bundle actions and their toast messagesdashboard.*— page title, bulk action bar, "N tickets selected"export.*— export dialog labels, format optionssettings.categories.*— board-scoped category managementsettings.display.*— column visibility, sort preferenceswatchList.*— watcher add/remove, empty statematerials.*— materials add/edit, cost labelsquickAdd.*— extended quickAdd labels beyond existingcreate.*properties.*— inline-edit side panel labels (many overlap withfields.*— reuse where possible)errors.*— toast error strings (session required, permission denied, partial failure)validation.*— form validation messages beyond existingcreate.errors.*
Final gap list determined during implementation — run pseudo-locale tests to surface missing keys.
Non-functional Requirements
- No regressions: all existing ticket tests pass after migration
- Lang-pack validation: after every namespace edit, run
node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjsand commit only when green. The validator covers key parity, missing/extra keys,{{variable}}preservation, pseudo-locale fill patterns, and Italian accent preservation in a single run. Pseudo-locale files (xx/,yy/) are always regenerated from English — never hand-edited. - Naming convention: follow existing
features/tickets.jsonpatterns (camelCase, nested under semantic groups) - Fallback-safe: all
t()calls uset('key', 'English fallback')signature so missing keys don't break UI - Shared with client portal: before adding a key, check if existing
features/ticketskey covers it
Data / API / Integrations
- No database changes
- No API changes
- No new npm dependencies
- Reuses existing
useTranslationhook from@alga-psa/ui/lib/i18n/client - Reuses existing i18next infrastructure loaded via
I18nWrapper(already in MSP layout)
Security / Permissions
No change. Translation is a pure presentation-layer concern.
Observability
N/A — no new operational concerns.
Rollout / Migration
- Flag: work proceeds without feature-flag gating because
I18nWrapperin MSP layout already forces English fallback whenmsp-i18n-enabledis off (see parent plan Phase 0.7) - Per-PR rollout: ship sub-batches A/B/C as independent PRs to keep review scope manageable
- Deploy path: translations are static JSON served from
server/public/locales/; no cache invalidation beyond standard Next.js static-asset rebuild - Back-out: each PR is independently revertable; components continue rendering English
via
defaultValuefallbacks even if JSON keys are reverted
Open Questions
- Should
bulk.*,dashboard.*,export.*,settings.*keys live infeatures/tickets.jsonor be extracted to a newmsp/ticketing.jsonnamespace? Tentative answer: keep infeatures/tickets.jsonsince client portal may eventually surface similar controls (bulk operations on client-side ticket list). Revisit after sub-batch A if the file grows past ~250 keys. - For toast messages using template literals (e.g.,
`${N} tickets moved`), use i18next interpolationt('bulk.moved', { count: N })or keep template string witht()for the static part? Tentative answer: interpolation — matches existing patterns infeatures/tickets.jsonmessages.*. - For
throw new Error('User must be logged in')— translate or leave English? Tentative answer: translate only if the error surfaces to user via toast/UI; leave English if caught and rethrown by error boundaries or logged only.
Acceptance Criteria (Definition of Done)
- All 23 unwired production MSP ticket components either (a) import
useTranslationand wrap all user-visible strings, or (b) are confirmed to have zero user-visible strings (skeletons, styles, re-exports) features/tickets.jsoncontains all keys referenced by MSP ticket componentsnode scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjsexits 0 (covers key parity across 9 locales, pseudo-locale fill patterns, Italian accent preservation, and{{variable}}interpolation preservation)- All existing ticket-related unit/integration tests pass
- Visual smoke test:
/msp/tickets,/msp/tickets/[id],/msp/settings/ticketing,/msp/service-requests/[id]render correctly inenand at least one non-English locale (de or fr recommended);xxpseudo-locale shows pseudo-text for every visible string (no bare English leakage) - Parent plan
.ai/translation/MSP_i18n_plan.mdupdated: sub-batch 2b-21a marked ✅