Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
7.2 KiB
PRD — MSP i18n: Contract Lines Sub-batch
- Slug:
2026-04-09-msp-i18n-contract-lines - Date:
2026-04-09 - Status: Pending
Summary
Extract all hardcoded English strings from the 21 contract-line configuration components in packages/billing/src/components/billing-dashboard/, create the msp/contract-lines namespace, wire useTranslation() throughout, and generate translations for 7 languages + pseudo-locales.
Total scope: ~350-400 new keys across 21 files.
Problem
The contract-line management UI -- preset creation dialog, type-specific configurations (Fixed/Hourly/Usage), service lists, tier editors, and routing components -- contains approximately 350-400 hardcoded English strings. These components are accessed by MSP operators via the billing section and are among the most form-heavy screens in the application. Users with non-English locale preferences see untranslated labels, validation messages, tooltips, section headings, helper text, and button labels throughout the contract-line management workflow.
Goals
- Create the
msp/contract-lines.jsonnamespace with all contract-line-related translation keys - Extract every user-visible hardcoded string from all 21 component files
- Wire all components to consume translations via
useTranslation('msp/contract-lines') - Use
useFormatters()for any currency formatting where applicable - Generate accurate translations for all 7 production languages (en, fr, es, de, nl, it, pl)
- Generate pseudo-locale files (xx, yy) covering all new keys
- Validate all locale files pass
validate-translations.cjs - Register
/msp/billingroute additions inROUTE_NAMESPACESto loadmsp/contract-lines - Zero regressions -- everything works identically with
msp-i18n-enabledflag OFF (forced English)
Non-goals
- Translating billing constants (
BILLING_FREQUENCY_OPTIONS,PLAN_TYPE_OPTIONS,BILLING_FREQUENCY_DISPLAY,PLAN_TYPE_DISPLAY,CONTRACT_LINE_TYPE_DISPLAY) -- those are shared constants used across billing; they belong in amsp/billingnamespace - Translating
ServiceCatalogPickerorBucketOverlayFields-- those are shared components used by contracts too, covered separately - Translating
ServiceConfigurationPanelinservice-configurations/-- separate sub-batch - Translating
FixedContractLineServicesListorFixedContractLinePresetServicesList(in parentbilling-dashboard/directory, not incontract-lines/) -- separate scope - Translating
DeleteEntityDialogorConfirmationDialog-- shared UI components - Adding new languages beyond the existing 7
- Changing the component architecture or data flow
File Inventory
| # | File | LOC | Strings (est.) | Features |
|---|---|---|---|---|
| 1 | ContractLineDialog.tsx |
1292 | ~80 | F001-F003 |
| 2 | contract-lines/HourlyContractLineConfiguration.tsx |
849 | ~55 | F004-F005 |
| 3 | contract-lines/UsageContractLineConfiguration.tsx |
738 | ~45 | F006-F007 |
| 4 | contract-lines/UsageContractLinePresetConfiguration.tsx |
731 | ~45 | F008-F009 |
| 5 | contract-lines/HourlyContractLinePresetConfiguration.tsx |
693 | ~45 | F010-F011 |
| 6 | contract-lines/UsageContractLinePresetServicesList.tsx |
537 | ~30 | F012-F013 |
| 7 | contract-lines/HourlyContractLinePresetServicesList.tsx |
514 | ~30 | F014-F015 |
| 8 | contract-lines/GenericContractLineServicesList.tsx |
513 | ~30 | F016-F017 |
| 9 | ContractLines.tsx |
485 | ~25 | F018-F019 |
| 10 | contract-lines/FixedContractLineConfiguration.tsx |
434 | ~30 | F020-F021 |
| 11 | contract-lines/FixedContractLinePresetConfiguration.tsx |
380 | ~25 | F022-F023 |
| 12 | contract-lines/ServiceHourlyConfigForm.tsx |
352 | ~25 | F024-F025 |
| 13 | contract-lines/ContractLineServiceForm.tsx |
308 | ~10 | F026 |
| 14 | contract-lines/ContractLinesOverview.tsx |
281 | ~20 | F027-F028 |
| 15 | contract-lines/ServiceTierEditor.tsx |
206 | ~15 | F029 |
| 16 | contract-lines/ServiceUsageConfigForm.tsx |
197 | ~15 | F030 |
| 17 | contract-lines/ServiceBucketConfigForm.tsx |
164 | ~12 | F031 |
| 18 | contract-lines/EditContractLineServiceQuantityDialog.tsx |
154 | ~12 | F032 |
| 19 | contract-lines/ContractLineTypeSelector.tsx |
129 | ~10 | F033 |
| 20 | contract-lines/ContractLineTypeRouter.tsx |
79 | ~4 | F034 |
| 21 | contract-lines/ContractLinePresetTypeRouter.tsx |
79 | ~4 | F035 |
Namespace Structure
Namespace: msp/contract-lines
{
"dialog": { // ContractLineDialog.tsx
"title": { "add": "...", "edit": "..." },
"basics": { ... },
"billingModel": { ... },
"fixed": { ... },
"hourly": { ... },
"usage": { ... },
"validation": { ... },
"actions": { ... }
},
"overview": { ... }, // ContractLinesOverview.tsx
"list": { ... }, // ContractLines.tsx
"configuration": { // Shared config patterns
"basics": { ... },
"fixed": { ... },
"hourly": { ... },
"usage": { ... }
},
"preset": { // Preset-specific variants
"basics": { ... },
"fixed": { ... },
"hourly": { ... },
"usage": { ... }
},
"services": { // Service list components
"generic": { ... },
"hourlyPreset": { ... },
"usagePreset": { ... }
},
"forms": { // Form sub-components
"hourlyConfig": { ... },
"usageConfig": { ... },
"bucketConfig": { ... },
"tierEditor": { ... },
"serviceForm": { ... },
"editQuantity": { ... }
},
"typeSelector": { ... }, // ContractLineTypeSelector.tsx
"router": { ... }, // Router loading/error states
"common": { // Shared across multiple files
"validation": { ... },
"actions": { ... },
"labels": { ... },
"errors": { ... }
}
}
Acceptance Criteria
- All 21 files import
useTranslationfrom@alga-psa/ui/lib/i18n/clientand callt()for every user-visible string - Every
t()call includes adefaultValuewith the original English text as fallback msp/contract-lines.jsonexists inserver/public/locales/en/and contains all extracted keys- 7 language files exist in their respective locale directories with accurate translations
- Pseudo-locale files (xx, yy) exist and cover all keys
validate-translations.cjspasses with zero errors for the new namespace- No visual regressions with
msp-i18n-enabledOFF -- all components render identically with English defaults - Interpolation works correctly for dynamic values (e.g., service names, tier numbers, currency amounts)
- Validation messages with interpolated values (e.g., "Service {{index}}: Please select a service") render correctly in all languages
- Long translations (German/Dutch ~30-50% longer) do not cause layout overflow in form labels, buttons, card descriptions, or accordion headers
- Tooltip content is translated (overtime/after-hours info tooltips, rate tooltips)
- Table column headers are translated (Service Name, Billing Frequency, etc.)
- Empty state messages are translated ("No services currently associated...", "Select a contract line to manage its services")
- ROUTE_NAMESPACES updated to load
msp/contract-linesfor billing routes