Hardcoded text i18n audit (baseline) โ
Generated during the hardcoded-text remediation pass.
Phase 1 โ Existing audit tools โ
| Tool | Result |
|---|---|
dart run tool/audit_i18n_keys.dart | 2408 keys scanned; 1 false-positive dynamic prefix (valuation.workflowStages.$uiKey) |
audit_missing_i18n_test | 0 missing / 0 self-referential (ar + en) |
audit_areas_i18n_test | 0 missing per feature area |
audit_isAr_hardcoded_test | Pass (6 allowlisted screen files) |
audit_choice_field_coverage_test | Pass |
Phase 2 โ Pattern grep (lib/) โ
| Pattern | Approx. hits |
|---|---|
tOrNull(...) ?? 'English' | 39 across 10 files |
SnackBar(content: Text(e.toString() | 27 across 18 files |
isAr ? in /screens/ | 62 (6 allowlisted files) |
Hardcoded Text('English') in features | ~15 high-priority files |
Allowlisted isAr screens (P2 debt): comparables_screen, site_inspection_screen, 3ร AI screens, add_property_screen.
Fix priority โ
See plan: P0 broker/public โ P1 mailbox/admin โ P2 isAr screens โ P3 dev tools โ P4 error SnackBars โ CI hardening.
Round 2 baseline (2026-06-10) โ
| Tool | Result |
|---|---|
dart run tool/audit_i18n_keys.dart | 2591 keys; ar/en ok |
audit_missing_i18n_test | Pass |
audit_areas_i18n_test | Pass |
audit_isAr_hardcoded_test | Pass |
audit_hardcoded_literals_test | Pass (bare Text('โฆ') clean in features) |
audit_choice_field_coverage_test | Pass |
Round 2 grep targets (pre-fix) โ
| Pattern | Count |
|---|---|
suffixText: 'KWD' | 12 file hits |
suffixText: 'mยฒ' | 5 file hits |
tOrNull(...) ?? slug | ~10 files |
'B ${block}' / 'ู ${block}' | 8+ screens |
Round 2 focus: stored-value chip labels, unit suffixes, block/plot prefixes, tOrNull slug fallbacks, shared-widget defaults.
Round 2 completion (2026-06-10) โ
| Tool | Result |
|---|---|
dart run tool/audit_i18n_keys.dart | 2594 keys; ar/en ok |
test/tool/* (14 tests) | All pass |
audit_hardcoded_literals_test | + shared-widgets scan; + unit suffix scan |
audit_isAr_hardcoded_test | Multiline continuation join |
audit_tOrNull_fallback_test | New โ blocks raw slug fallbacks |
flutter analyze --no-fatal-infos | 0 errors |
Round 2 grep targets (post-fix) โ
| Pattern | Count |
|---|---|
suffixText: 'KWD' / 'mยฒ' / '%' | 0 |
tOrNull(...) ?? rawSlug in features | 0 |
'B ${block}' / 'ู ${block}' in screens | 0 (via formatBlockLabel / formatPlotLabel) |
New / updated helpers โ
lib/core/formatting/location_labels.dartโ block/plot prefixeslib/features/properties/widgets/building_composition_labels.dartโ apt comp room/facility labels- Translation keys:
common.plotPrefix,common.percent,property.values.apt_comp_*,aiMarketing.captionType.*,hermes.dashboard.runsChartSemantics
Round 3 baseline (2026-07-01) โ cross-surface alignment โ
| Tool | Result |
|---|---|
dart run tool/audit_i18n_keys.dart | 3241 keys; ar/en ok |
npm run i18n-check (aldilaijanre) | 14716 keys; ar/en parity ok |
audit_missing_i18n_test | Pass |
audit_hardcoded_literals_test | Pass |
audit_isAr_hardcoded_test | Pass |
audit_data_layer_i18n_test | Pass (ai_content_repository allowlisted) |
taxonomy_registry_lock_test | Pass after al_maktab โ free-text |
cross_repo_governorate_lock_test | Flutter โ registry โ React |
cross_repo_label_parity_test | deal_type EN labels match |
codegen_kuwait_area_labels.dart --check | 221 areas in sync |
Domain consolidation (this pass) โ
| Domain | Change |
|---|---|
| Governorates | propertyGovernorateOptions / kPublicToolGovernorates / kMojGovernorateOptions โ kCanonicalGovernorates |
| Property types | propertyTypeOptions โ schema; valuationPropertyTypes derived from canonical subset |
| Deals / requests | StoredChoiceOption inline labels removed; i18n-only display |
| Voice notes | voiceNoteContextLabel / relative time โ voiceNotes.* + common.* keys |
| Comparables subject | ValuationSubject.title uses formatBlockLabel |
| Areas | export/paci_areas_catalog.json + codegen drift check |
Remaining grep debt (P2) โ
| Pattern | Location |
|---|---|
isAr ? wire prefixes | ai_content_repository.dart (server parity) |
kPublicToolPropertyTypes | Arabic MoJ/hedonic consumers โ intentional separate vocab |
| Arabic price-group stored values | requests/constants.dart (al_majmua legacy) |
| Dual i18n namespaces | Flutter *.values.* vs React enums.* โ spot-checked for deals |
Manual QA checklist (Round 3) โ
- [x] Flutter Web 375px + 1280px โ ar/en governorate chips on registry, client browse, transactions
- [x] Flutter mobile โ voice notes relative time ar/en (bundle keys verified; device spot-check deferred)
- [x] React SPA โ deal type labels on deal form match Flutter (
cross_repo_label_parity_test) - [x] RTL โ governorate filter chips truncate cleanly (web layout smoke at 375px)
Round 4 baseline (2026-07-01) โ backlog closure โ
| Tool | Result |
|---|---|
dart run tool/promote_locale_namespaces.dart --check | Canonical ar/en namespace parity for individuals, individualForm, partyRegistry, duplicateReview |
dart run tool/sync_i18n.dart --check | Canonical Flutter bundle structure and bilingual key parity |
dart run tool/export_taxonomy_registry.dart + sync_export_edge_bundle.dart | Flutter-owned taxonomy and deployable export bundle |
cross_repo_label_parity_test | Local deal_type, governorates, built_or_land, MoJ transactions.values.* coverage (ar+en) |
valuation_workflow_stages_lock_test | 7 UI stages โ 11 schema keys via valuationWorkflowUiKey |
React eslint i18next/no-literal-string | warn on src/pages/** only |
| MoJ facets | transactions.values.category.ุฎุงุต + publicTransactions.dealSubtitle in both bundles |
Still intentional P2 โ
| Pattern | Location |
|---|---|
| ~2k mobile-only keys | Party 360 / individuals until React screens ship |
| Dual namespaces | Flutter *.values.* vs React enums.* beyond spot-checked catalogs |
kPublicToolPropertyTypes | Arabic MoJ/hedonic vocab |
ai_content_repository | Server parity wire prefixes |
Round 5 baseline (2026-07-01) โ P2 Party 360 + create/edit โ
| Deliverable | Status |
|---|---|
React usePartyActivity | Mirrors Flutter party_activity_repository (deals, requests, properties, valuations, invoices, tahdeed) |
PartyDetail tabs | Brokerage / valuation / financial / activity bodies wired with deep links |
partyDetailVisibility | Data-gated tabs via partyActivityFlags (Vitest lock) |
ContactsList | Debounced search, people classification chips, entity sector/category chips, i18n AK/KH badges |
IndividualForm | /clients/new, /clients/:id/edit โ core fields (name, phone, civil ID, classification) |
AddPartyDialog | Entities FAB quick-add via find_or_create_party RPC |
PartyOrganizationProfile | Org profile tab โ people linked via primary_contact_id |
usePartyRegistry | create/update individual, org quick-add, duplicate detection |
| Routes | contacts.new, contacts.edit(id) registered before :id detail |
promote_locale_namespaces | + duplicateReview namespace |
cross_repo_label_parity_test | + enums.propertyCategory valuation subset; built_or_land EN aligned |
| React locales | Restored nested contacts.* + tahdeed.section.letters |
Verification (Round 5) โ
| Tool | Result |
|---|---|
React npm run i18n-check | 14784 static keys; AR/EN 24164 each |
| React vitest | individualParty, partyDetailVisibility |
React tsc --noEmit -p tsconfig.app.json | Clean after phase 2 |
Manual QA checklist (Round 5) โ
- [ ]
/clients/newโ create individual, duplicate phone toast - [ ]
/clients/:id/editโ hydrate + save - [ ] Entities FAB โ
AddPartyDialogorg create - [ ] Org detail profile โ people-at-org list
- [ ] Entity list sector + category filter chips
- [ ] Party 360 tabs show/hide per linked data (ar/en)
Still intentional P2 โ
| Item | Notes |
|---|---|
Full *.values.* โ enums.* migration | MoJ coarse categories migrated on PredictiveAnalytics; remaining domains TBD |
al_majmua / kPublicToolPropertyTypes | DB migration or intentional vocab |
| Admin convergence health detail panel | โ Admin contacts list + duplicate review (Round 8) |
ESLint i18n error on all pages | src/pages/contacts/** ratcheted to error (Round 6) |
Round 6 baseline (2026-07-01) โ deferred closure โ
| Deliverable | Status |
|---|---|
IndividualForm nickname + profession | Sectioned form matching Flutter |
IndividualPaciAddressField | Governorate/area/block/street/building/PACI serial |
CivilIdDocumentField + uploadCivilIdDoc | entity-documents bucket, post-save upload |
PartyRegistrySummaryBar | Admin KPI strip (total / orgs / linked properties) |
useConvergenceHealth | Duplicate-party badge on review button |
mojCategoryTaxonomy | English UI keys + bilingual .in('category', โฆ) |
| ESLint | i18next/no-literal-string error on src/pages/contacts/** |
Round 7 baseline (2026-07-01) โ optional polish โ
| Deliverable | Status |
|---|---|
ClassificationField | Client buyer/seller chips; investor subtype + area-of-interest multi-select |
classificationField.ts | composeClassificationSubtype / composeAreasOfInterest (Flutter parity) |
OrganizationForm + PartyEditPage | /clients/:id/edit routes to individual or org editor |
partyPersonsSchema.ts | Typed partyPersonsTable() โ replaces scattered as any on registry writes |
| Locales | contacts.entities.editTitle (ar/en) |
Round 8 baseline (2026-07-01) โ deferred closure โ
| Deliverable | Status |
|---|---|
React price_groups[] | Form write + list filter on price_groups; PRICE_GROUP_OPTIONS English keys |
enums.priceGroup.* | 9 keys + legacy Arabic aliases in enumMaps.priceGroup |
cross_repo_label_parity_test | + request.values.price_groups โ enums.priceGroup |
Flutter requestPriceGroupOptions | Value-only StoredChoiceOption (no inline labelAr/En) |
| Identity drift panel | DuplicateReview drift tab + useIdentityDriftWatch |
| ESLint ratchet | error on src/pages/admin/AdminDuplicateReviewPage.tsx |
Round 9 baseline (2026-07-01) โ admin ESLint ratchet โ
| Deliverable | Status |
|---|---|
WhatsAppShadowReview.tsx | Entity filter, skip badges, run id via shadowReview.* keys |
| ESLint | i18next/no-literal-string error on all src/pages/admin/** |
| Locales | shadowReview.entities.*, skippedWithReason, runId (ar/en) |
| Intentional freeze (documented) | kPublicToolPropertyTypes, ai_content_repository wire prefixes |
Manual QA checklist (Round 5โ8) โ
- [x] Automated: i18n-check, vitest, tsc, ESLint contacts + admin
- [x] Automated: cross-repo price_group + party registry parity tests
- [ ]
/clients/newโ create individual, duplicate phone toast (device) - [ ]
/clients/:id/editโ hydrate + save (device) - [ ] Request form โ price-band chips persist to
price_groups(device) - [ ] Duplicate review drift tab โ snapshot vs CRM name rows (device)
Closed deferrals โ
| Item | Resolution |
|---|---|
al_majmua price bands | React uses price_groups; al_majmua reserved for area groups |
| Admin drift panel | Full table in DuplicateReview |
| ESLint beyond contacts | Admin pages ratcheted |
*.values.* โ enums.* | Price groups domain locked; domain-by-domain continues in CI |
Still intentional (no change) โ
| Item | Notes |
|---|---|
kPublicToolPropertyTypes | Arabic MoJ/hedonic stored vocab โ query boundary |
ai_content_repository wire prefixes | Server parity tokens, not UI copy |
| Full enum namespace merge | Remaining domains via parity test batches |
Round 8 baseline (2026-07-01) โ adjacent / ops closure โ
| Deliverable | Status |
|---|---|
| Admin convergence panel on contacts list | Flutter _ConvergenceBar + React PartyConvergenceHealthBar |
| Ops runbook | CONTACTS_UNIFIED.md โ production monitoring checklist |
React dataQuality.* convergence keys | Added to public/locales/{ar,en}/translation.json (parity with Flutter) |
cross_repo_label_parity_test | + contacts segments, classification, org sectors, convergence labels |
| Network CRM gate | Already empty in kV1HiddenRoutes; parity doc current โ on-device QA only |
DuplicateReview deep link | React ?tab=clients opens Clients merge tab |
Verification (Round 8) โ
| Tool | Result |
|---|---|
npm run i18n-check (aldilaijanre) | โ Pass โ 24,191 AR/EN keys in sync |
cross_repo_label_parity_test | โ Pass โ contacts segments, classification, org sectors, convergence labels |
party_detail_visibility_test.dart | โ Pass |
npm run typecheck (aldilaijanre) | โ Pass โ 0 errors vs baseline |
| Vitest party/search/admin/visibility | โ Pass โ 10 tests |
flutter analyze individuals list | โ
Clean after unawaited(context.pushโฆ) fix |
P2 closure (2026-07-01) โ
| Item | Status |
|---|---|
Legacy UnifiedPersons / BuyersList pages | Removed; routes already redirect to /clients |
| Flutter org edit | OrganizationFormScreen + PartyEditScreen dispatcher |
| CRM actions on party 360 | Flutter picker on brokerage/valuation/activity; React QuickInteractionButton |
| Widget tests | party_detail_screen_test, duplicate_review_screen_test |
| Supabase type regen | Deferred โ run npx supabase gen types from Linux/macOS (Windows libuv blocker) |
| Seeded E2E contacts | Listโdetail still skip when empty; admin/persons redirect test added |
| Network CRM on-device QA | Manual โ see docs/DEVICE_QA_CHECKLIST.md |
