Files
MobilityOps/docs/fleet-ops-correction/i18n-inventory.md
T
NuklearRabbitandClaude Sonnet 5 6deb95524d fix: safe status-recommendation flow, MO-016 order independence, brand constant, message codes
- Add a single shared, pure vehicle-status evaluator (app/services/vehicle_status.py)
  used identically by the data-quality scanner, a new non-mutating status-recommendation
  preview endpoint, and a transactional apply endpoint with optimistic-concurrency token
  revalidation -- eliminates the old opaque "calculate and apply" action and the unsafe
  "maintenance + active booking -> auto rented" shortcut. Frontend
  DataQualityIssueDetail.tsx now shows a review/decide/confirm panel with localized
  why/evidence/consequence text in nl-BE/en-GB/fr-BE, with an exact "Change status to
  <status>" confirm action per the brief.
- Fix MO-016 issue-order dependency: resolving the booking-overlap issue before vs.
  after the status-conflict issue now converges on the same final vehicle status,
  proven by test_mo_016_status_conflict_recommendation_is_order_independent.
- Make "Fleet Ops" a non-localizable brand constant (frontend/src/product.ts,
  backend PRODUCT_NAME) via {{productName}} interpolation everywhere the brand name
  appeared in locale prose; add a permanent test guarding against a translation file
  ever defining the brand name or an "appName" key again.
- Convert dynamic backend prose to stable message codes + params: return status
  reasons, audit field/actor-type labels, automation last_error, and search
  section/vehicle/booking/issue results all now carry codes the frontend localizes,
  with raw technical text demoted to a "Technical details" disclosure.
- docs/fleet-ops-correction/: gap audit, i18n inventory, and the vehicle-status
  decision table documenting the evaluator's rules and safe-status principles.

148 backend tests + Ruff + mypy green; Alembic migration verified upgrade/downgrade;
frontend tsc/build and the i18n-coverage Playwright suite green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 21:37:34 +02:00

3.7 KiB

i18n inventory — dynamic/backend content requiring message-code treatment

Static UI chrome (navigation, dashboard, forms, filters, dialogs, empty/loading states, Demo Guide, About page, accessibility labels) was already moved to the i18next namespace system in the prior task and is not re-inventoried here in full — see docs/final-product-polish/audit.md and docs/final-product-polish/i18n-inventory.md for that pass. This inventory covers only the sources confirmed still bypassing translation (per current-gap-audit.md), the fix chosen, and the tests that verify it.

# Source Location Static/dynamic Fix Tests
1 Duplicate-customer evidence signals backend/app/services/data_quality.py::_scan_duplicate_customers dynamic evidence.signals: [{code, params}] (duplicate.exact_email, duplicate.exact_phone, duplicate.same_postal_code, duplicate.similar_name + score param); frontend maps code→localized phrase test_data_quality.py::test_duplicate_customer_evidence_has_structured_signals; Playwright DQ evidence-language test
2 Missing-required-field evidence _scan_missing_required_fields dynamic evidence.signals: [{code: "missing_field", params: {field}}]; frontend maps field through the existing CUSTOMER_FIELD_LABELS/VEHICLE_FIELD_LABELS-equivalent i18n keys same
3 Booking-overlap evidence _scan_booking_overlaps dynamic evidence.signals: [{code: "overlap.reserved_bookings", params: {refs: [...]}}] same
4 Vehicle-status-conflict evidence _scan_vehicle_status_conflicts dynamic replaced entirely by the new evaluate_vehicle_status() evaluator's recommendation_code; scanner reuses the evaluator instead of its own reason strings new evaluator unit tests
5 Odometer-regression evidence _scan_odometer_regressions dynamic evidence.signals: [{code: "odometer.regression", params: {later_ref, later_km, earlier_ref, earlier_km}}] same
6 Status-recommendation reason _recommend_vehicle_status / new evaluate_vehicle_status dynamic recommendation_code + facts (structured), no free prose from the backend at all; frontend renders the full explanation from quality:statusRecommendation.codes.<code> evaluator unit tests + preview/apply contract tests
7 Return status reason backend/app/services/returns.py::_derive_vehicle_status_with_reason dynamic status_reason_code + params alongside the existing human string (kept for backward-compat, demoted to technical fallback) test_returns.py updated; Playwright return-flow-language test
8 Automation last_error dispatcher.py / seed data, rendered in Automation.tsx dynamic known-cause codes (n8n.connection_timeout, n8n.http_error, etc.) mapped to a localized summary + "what happened / what's pending / what retry does"; raw string demoted to Technical details Playwright automation-language test
9 aria-label="Demo scenario" frontend/src/pages/BookingDetail.tsx:76 static, just un-wired t("returns:scenario.ariaLabel"), 3 locales i18n-coverage (key parity) + route-matrix test
10 Brand name (common:appName + 8 embedded mentions) see gap audit §1 static, wrongly translatable PRODUCT_NAME constant, {{productName}} interpolation new brand-invariant test

Message-code mapping module

Centralised in frontend/src/i18n/messageCodes.ts (new): a single resolveMessageCode(t, code, params) helper used by the Data Quality evidence renderer, the status-recommendation panel, the return-result panel, and the automation ledger, so there is one place mapping code → i18next key rather than per-page switch statements.