# PRD -- MSP i18n: Invoicing Sub-batch - Slug: `2026-04-09-msp-i18n-invoicing` - Date: `2026-04-09` - Status: Draft (ready to start — verified unchanged on 2026-04-17; see SCRATCHPAD "Status Recheck (2026-04-17)" for corrected file paths and upstream deltas) - Parent plan: `/.ai/translation/MSP_i18n_plan.md` ## Summary Extract all hardcoded English strings from 22 invoicing components in `packages/billing/src/components/`, create the `msp/invoicing` namespace, wire `useTranslation('msp/invoicing')`, and generate translations for 7 languages plus pseudo-locales. PaperInvoice.tsx (44 LOC) has zero user-facing strings and is excluded. ## Problem MSP billing pages are one of the highest-value surfaces in the application (invoicing, templates, tax management, recurring billing). All 22 invoicing components are hardcoded English -- zero currently use `useTranslation`. Non-English MSP users encounter a fully translated sidebar, dashboard, and settings chrome, but switch to raw English when they enter any invoicing workflow (drafts, finalized, automatic generation, manual creation, templates, tax reconciliation, etc.). ## Goals 1. Create `server/public/locales/en/msp/invoicing.json` with all keys extracted from 22 invoicing components 2. Wire `useTranslation('msp/invoicing')` into every component with user-visible strings 3. Use `useFormatters` from `@alga-psa/ui/lib/i18n/client` for currency and date formatting where hardcoded `Intl.NumberFormat` / `toLocaleString` is currently used 4. Generate translations for 6 non-English locales (fr, es, de, nl, it, pl) + 2 pseudo-locales (xx, yy) 5. Run `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` green (exit 0) before shipping 6. Measurable: invoicing coverage goes from 0% to 100% of production components wired ## Non-goals - Translating template content (user-designed invoice layouts, template AST field names) - Translating invoice data (invoice numbers, client names, descriptions) -- those are tenant data, not UI chrome - Moving existing non-invoicing billing keys from other namespaces - Wiring EE-only invoicing components not exported through `@alga-psa/billing/components` - Changing layout or visual design of any component - Translating `console.error` / `console.log` / `console.warn` developer-only messages ## File Inventory ### Large (600+ LOC) -- 2-3 features each | Component | LOC | Est. strings | Key content | |-----------|-----|-------------|-------------| | AutomaticInvoices.tsx | 1,983 | ~120 | Ready-to-invoice table, parent group labels, cadence badges, incompatibility reasons, materialization gap panel, recurring history table, reverse/delete dialogs, preview dialog, PO overage dialogs, error messages | | ManualInvoices.tsx | 859 | ~50 | Form labels, automated items table, line item headers, prepayment checkbox, error messages, validation, submit button states | ### Medium (200-600 LOC) -- 1-2 features each | Component | LOC | Est. strings | Key content | |-----------|-----|-------------|-------------| | DraftsTab.tsx | 550 | ~30 | Table column headers, search placeholder, bulk actions, empty state, reverse confirmation dialog | | FinalizedTab.tsx | 506 | ~30 | Table column headers, search placeholder, bulk actions (download/email/unfinalize), empty state | | RecurringServicePeriodsTab.tsx | 458 | ~40 | Page title, schedule selector, form labels, summary stats, table headers, repair panel, regeneration preview | | BillingCycles.tsx | 397 | ~25 | Page title, tooltip, column headers, anchor labels, month names, search, loading text | | InvoicePreviewPanel.tsx | 386 | ~20 | Preview title, template selector, action buttons, loading/error/empty states | | InvoiceTemplateEditor.tsx | 345 | ~20 | Back nav, heading, template name label, visual/code tabs, save/cancel buttons, timestamps, validation errors | | InvoiceTemplates.tsx | 344 | ~20 | Section heading, column headers, action menu items (edit/clone/set default/delete), create button, loading text | | ExternalTaxBatchImportDashboard.tsx | 320 | ~25 | Card title/description, column headers, batch import button, progress bar labels, results summary, help text, empty state | | ExternalTaxImportPanel.tsx | 297 | ~25 | Panel title, pending/imported alerts, import button, history section, reconciliation labels, help text | | SendInvoiceEmailDialog.tsx | 278 | ~25 | Dialog title, summary counts, recipients heading, recipient source labels, custom message label, email preview, send/cancel buttons | | TaxReconciliationView.tsx | 222 | ~20 | Card title, comparison labels (Internal/External), table headers, warning alert, help text | | GenerateTab.tsx | 207 | ~15 | Invoice type selector labels, type descriptions, success dialog message | ### Small (< 200 LOC) -- 1 feature each | Component | LOC | Est. strings | Key content | |-----------|-----|-------------|-------------| | PrepaymentInvoices.tsx | 165 | ~15 | Form heading, field labels, type options, placeholders, validation errors, submit button states | | ContractInvoiceItems.tsx | 133 | ~10 | Table headers (Description/Quantity/Rate/Amount), subtotal labels, product badge | | InvoicingHub.tsx | 94 | ~5 | Section heading, tab labels (Generate/Drafts/Finalized) | | InvoiceTemplateManager.tsx | 92 | ~5 | Headings (Invoice Template Manager, Sample Invoices, Template Preview) | | InvoiceTaxSourceBadge.tsx | 81 | ~10 | Badge labels (Tax: Internal/External/Pending), tooltip strings, adapter names | | InvoiceAnnotations.tsx | 60 | ~5 | Heading, internal/external labels, placeholder, add button | | PurchaseOrderSummaryBanner.tsx | 45 | ~5 | Field labels (PO Number, PO Authorized, PO Consumed, PO Remaining) | ### Excluded (zero user-facing strings) | Component | LOC | Reason | |-----------|-----|--------| | PaperInvoice.tsx | 44 | Pure layout wrapper, no text content | ## Namespace Structure ``` msp/invoicing.json automaticInvoices.* -- Ready-to-invoice, parent groups, history, dialogs manualInvoices.* -- Manual invoice form, line items, prepayment draftsTab.* -- Drafts list, bulk actions, reverse dialog finalizedTab.* -- Finalized list, bulk actions recurringServicePeriods.* -- Schedule management, repair, regeneration billingCycles.* -- Billing cycle table, anchor labels invoicePreview.* -- Preview panel, action buttons templateEditor.* -- Template editor form, tabs templates.* -- Template list, actions externalTax.* -- Batch import, single import, reconciliation sendEmail.* -- Email dialog, recipient info generateTab.* -- Type selector, descriptions prepayment.* -- Prepayment form contractItems.* -- Contract invoice items table hub.* -- Invoicing hub, tab labels templateManager.* -- Template manager, sample invoices taxBadge.* -- Tax source badge labels, tooltips annotations.* -- Annotations section purchaseOrder.* -- PO summary banner common.* -- Shared strings (Search, Cancel, Actions, Loading, etc.) ``` ## Acceptance Criteria - [ ] All 22 components with user-visible strings import `useTranslation('msp/invoicing')` and wrap every user-visible string with `t('key', { defaultValue: '...' })` - [ ] `server/public/locales/en/msp/invoicing.json` contains all keys referenced by components - [ ] Currency formatting uses `useFormatters` or locale-aware `Intl.NumberFormat` instead of hardcoded `'en-US'` locale - [ ] `ROUTE_NAMESPACES` for `/msp/billing` loads the `msp/invoicing` namespace - [ ] `node scripts/generate-pseudo-locales.cjs && node scripts/validate-translations.cjs` exits 0 (key parity across 9 locales, pseudo-locale fill patterns, Italian accent preservation, `{{variable}}` interpolation preservation) - [ ] All existing billing-related unit/integration tests pass - [ ] Visual smoke test: `/msp/billing?tab=invoicing` (all sub-tabs), `/msp/billing?tab=billing-cycles`, `/msp/billing?tab=service-periods`, `/msp/billing?tab=templates` render correctly in `en` and at least one non-English locale; `xx` pseudo-locale shows pseudo-text for every visible string