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

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

  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