Hermes 284313f908
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
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

135 lines
7.2 KiB
Markdown

# 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
1. Create the `msp/contract-lines.json` namespace with all contract-line-related translation keys
2. Extract every user-visible hardcoded string from all 21 component files
3. Wire all components to consume translations via `useTranslation('msp/contract-lines')`
4. Use `useFormatters()` for any currency formatting where applicable
5. Generate accurate translations for all 7 production languages (en, fr, es, de, nl, it, pl)
6. Generate pseudo-locale files (xx, yy) covering all new keys
7. Validate all locale files pass `validate-translations.cjs`
8. Register `/msp/billing` route additions in `ROUTE_NAMESPACES` to load `msp/contract-lines`
9. Zero regressions -- everything works identically with `msp-i18n-enabled` flag 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 a `msp/billing` namespace
- Translating `ServiceCatalogPicker` or `BucketOverlayFields` -- those are shared components used by contracts too, covered separately
- Translating `ServiceConfigurationPanel` in `service-configurations/` -- separate sub-batch
- Translating `FixedContractLineServicesList` or `FixedContractLinePresetServicesList` (in parent `billing-dashboard/` directory, not in `contract-lines/`) -- separate scope
- Translating `DeleteEntityDialog` or `ConfirmationDialog` -- 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
1. **All 21 files** import `useTranslation` from `@alga-psa/ui/lib/i18n/client` and call `t()` for every user-visible string
2. **Every `t()` call** includes a `defaultValue` with the original English text as fallback
3. **`msp/contract-lines.json`** exists in `server/public/locales/en/` and contains all extracted keys
4. **7 language files** exist in their respective locale directories with accurate translations
5. **Pseudo-locale files** (xx, yy) exist and cover all keys
6. **`validate-translations.cjs`** passes with zero errors for the new namespace
7. **No visual regressions** with `msp-i18n-enabled` OFF -- all components render identically with English defaults
8. **Interpolation** works correctly for dynamic values (e.g., service names, tier numbers, currency amounts)
9. **Validation messages** with interpolated values (e.g., "Service {{index}}: Please select a service") render correctly in all languages
10. **Long translations** (German/Dutch ~30-50% longer) do not cause layout overflow in form labels, buttons, card descriptions, or accordion headers
11. **Tooltip content** is translated (overtime/after-hours info tooltips, rate tooltips)
12. **Table column headers** are translated (Service Name, Billing Frequency, etc.)
13. **Empty state messages** are translated ("No services currently associated...", "Select a contract line to manage its services")
14. **ROUTE_NAMESPACES** updated to load `msp/contract-lines` for billing routes