Spec: F69 — Multi-Language UI Expansion (ar, ms, id, fil, lv)
Status: shipped — Phase 1 (ar+RTL+font), Phase 2 (ms+id+fil), Phase 3 (lv) all shipped
1. Problem & CEO Job
Language is a trust signal. A CEO in Riyadh who opens a product and reads it in English will use it. A CEO in Kuala Lumpur who opens a product and reads it in Malay will trust it. The gap between “use” and “trust” is the difference between a tool and a workspace that represents them.
Lumi’s North Star is the underserved market — Vietnam, Malaysia, Indonesia, Saudi Arabia, the Philippines, Latvia. F18 shipped a bilingual (en/vi) foundation with 62 locale directories already scaffolded. F69 activates five of them: Arabic (with RTL layout), Bahasa Melayu, Bahasa Indonesia, Filipino, and Latvian.
This is not translation busywork. Each language is a market unlock: one correct language in Riyadh opens 35 million SME owners; one correct language in Jakarta opens 65 million.
Strategic position (strategy v2, 2026-06-14): The ar/ms/id/fil/lv UI languages are i18n scaffolds parked behind Vietnam vertical proof, not active market unlocks. Per competitive-strategy-v2.md and the registry’s Positioning doctrine (v2) (“validation before velocity”), country/language breadth is downstream of proving the Vietnam fit-out/renovation/M&E vertical. Vietnamese is the first-class language; the “each language is a market unlock / Baltic market entry” framing above and below is premature under v2 — treat it as aspirational scaffolding, not a sequenced roadmap commitment.
1b. Success Metrics (per language, measured 90 days post-launch)
| Metric | Target |
|---|---|
| Language adoption rate (% of active users in target market selecting the language) | > 30% |
| Translation coverage (% of i18n keys translated, vs en baseline) | ≥ 95% |
| RTL layout regressions (ar only) | 0 on core screens |
| AI extraction accuracy in target language orgs | ≥ parity with vi baseline |
2. User Stories & Acceptance Criteria
Arabic (ar) — Saudi Arabia, UAE, wider MENA
- As a CEO in Riyadh, I want Lumi in Arabic, so that the product reads like it was built for me, not translated for me.
- Every UI string in core surfaces (Today, Brief, Pulse, Ask Lumi, Settings) has an Arabic translation. (common, pulse, brief, tasks, chat, settings, deals — 7 namespaces, T5-E R05)
- The
htmlelement carriesdir="rtl"when language isar. (applyDocumentDirection() in i18n/index.ts, T5-F R05) - Tailwind
rtl:variants are applied to all directional layout components (flex row, text alignment, padding, margins, icons). (Sidebar start-0/border-e/rtl:translate-x-full, Layout ms-64/me-3, T5-F R05) - Noto Sans Arabic is loaded conditionally — only when
language=aris active (no weight penalty for other users). (lazy inject + :root[lang=“ar”] body CSS rule, T5-F R05) - Date formatting uses Arabic numerals (١٢٣) when the user’s locale expects them (configurable; default Eastern Arabic numerals for
ar-SA). (formatNumber/formatCurrency map ar→ar-SA, lib/format.ts) - RTL: no mirrored icons (chevrons and directional arrows flip; logos, avatars, and non-directional icons do not). (index.css [dir=‘rtl’] .lucide-chevron-/arrow-* scaleX(-1))*
- Cross-browser: RTL layout passes on Chrome, Firefox, Safari.
Bahasa Melayu (ms) — Malaysia, Brunei
- As a CEO in Kuala Lumpur, I want Lumi in Bahasa Melayu, so that the product feels local, not imported.
- Every UI string in core surfaces has a Malay translation. (10 namespaces, T5-D R05)
- LTR layout — no RTL changes required.
- Currency formatting: MYR (RM) for deal amounts when org default currency is MYR (see F27 §10). (Organization.default_currency + formatCurrency wiring)
Bahasa Indonesia (id) — Indonesia
- As a CEO in Jakarta, I want Lumi in Bahasa Indonesia, so that legal, HR, and operational terms read correctly in local context.
- Every UI string in core surfaces has an Indonesian translation. (10 namespaces, T5-D R05)
- LTR layout — no RTL changes required.
- ms and id can ship in the same PR (the languages share structure; translation is independent).
Filipino / Tagalog (fil) — Philippines
- As a CEO in Manila, I want Lumi in Filipino, so that the product matches the language I use with my team.
- Every UI string in core surfaces has a Filipino translation. (10 namespaces, T5-D R05)
- LTR layout — no RTL changes required.
Latvian (lv) — Latvia and Baltic market entry
- As a CEO in Riga, I want Lumi in Latvian, so that the product is not yet another tool that treats the Baltics as an afterthought.
- Every UI string in core surfaces has a Latvian translation. (7 namespaces, T5-E R05)
- LTR layout — no RTL changes required.
- EU compliance context: GDPR data residency copy must be in Latvian. (lv/store.json privacy.gdpr + user_rights, lv/pricing.json enterprise_desc)
3. Scope Boundaries
- In: UI chrome translation (all i18n keys in active namespaces), RTL layout for Arabic, conditional Arabic font, SUPPORTED_LOCALES + LANGUAGES array sync, language selector showing new options.
- Out: Server-side content translation (pulse text, task titles, deal names stay in the language written — Lumi never auto-translates user data), pulse question localisation (DEFAULT_PULSE_QUESTIONS are hardcoded Vietnamese strings — see F07 §9 gap, requires dedicated rework), AI extraction keyword patterns (currently Vietnamese-only — see F08 §12 gap, must be resolved before these markets can get accurate extraction).
- One language per PR, in this order: Arabic first (RTL complexity warrants isolation) → ms + id together (LTR, similar grammar) → fil → lv.
- Translation source: Human-reviewed translations only; no machine translation shipped without review.
4. Data Model
No new models. Two changes to existing data:
SUPPORTED_LOCALES(backendapps/accounts/models.py): addar,ms,id,fil,lv. Already has 60 codes — these 5 are in the set. (apps/ai/language_detection.pydoes NOT defineSUPPORTED_LOCALES; it uses regex heuristics only. The canonical list lives inapps/accounts/models.pyand is imported byapps/ai/prompts.py.)LANGUAGESarray (frontendsrc/i18n/index.ts): add the 5 new language objects withcodeandlabel(native name). There is nodirfield on individual entries — RTL is handled by a separateRTL_LANGUAGES = new Set(['ar', 'he', 'fa', 'ur'])constant andapplyDocumentDirection()which setsdocument.documentElement.dir. The array grew to 56 entries total (all F18-scaffolded locales); 13 were actively translated at launch. Bothfil(Filipino) andtl(Tagalog) appear as separate entries in LANGUAGES and SUPPORTED_LOCALES.
Locale files: frontend/src/i18n/locales/{ar,ms,id,fil,lv}/<namespace>.json — directories already existed as placeholders; this spec filled them.
5. API Contract
No new endpoints. Existing PATCH /api/auth/me/language/ already validates against SUPPORTED_LOCALES (60+ codes including all 5 targets). The only change: the language selector in Settings surfaces the new codes as selectable options.
6. UI / UX
RTL (Arabic only)
<html dir="rtl">set by i18nextLanguageDetectorwhenlanguage=ar.- Tailwind RTL variants applied throughout:
rtl:flex-row-reverse,rtl:text-right,rtl:mr-0 rtl:ml-auto, etc. - Icon audit required:
ChevronRight→ChevronLeftin RTL; sidebar opens from right; back button moves to the right side. - Noto Sans Arabic:
font-family: 'Noto Sans Arabic', sans-serifloaded via<link rel="preload">only whendocument.documentElement.lang === 'ar'. - Number formatting: use
Intl.NumberFormat('ar-SA')for deal amounts and statistics when locale isar.
Language selector
Settings → Language shows all 56 scaffolded locales (the full LANGUAGES array); 13 were actively translated at launch. Grouped roughly: en/vi → core targets (ms/id/fil/tl/lv/ar) → remaining F18-scaffolded entries.
Components requiring RTL audit (Arabic)
- Sidebar navigation (open/close direction)
- DataTable column order and sort arrows
- Kanban board column flow
- Breadcrumb separators
- Form labels (right-aligned in RTL)
- Toast/snackbar position (bottom-left → bottom-right in RTL)
- Modal close button (top-right → top-left in RTL)
7. Test Plan
| # | Case | Type | Where |
|---|---|---|---|
| 1 | Language selector shows all 56 scaffolded locales; selecting each persists via PATCH /api/auth/me/language/ | e2e | ⚠️ E2E not present — no describe("Language") block exists in any spec file; the only language-related E2E is language-banner.spec.ts (describe('Language Confirmation Banner (F37)')) which tests detection/confirmation, not the selector |
| 2 | Arabic: html[dir="rtl"] set; sidebar opens from right; ChevronRight flips | e2e | describe("RTL") |
| 3 | Arabic: Noto Sans Arabic loaded only when ar is active | unit/browser | font loading test |
| 4 | Translation key parity: ar/ms/id/fil/lv have ≥95% of en keys | unit | extend i18n.test.ts parity check |
| 5 | ms + id: LTR layout unchanged; no directional regressions | e2e | screenshot diff |
| 6 | fil: LTR layout unchanged | e2e | screenshot diff |
| 7 | lv: LTR layout unchanged | e2e | screenshot diff |
| 8 | SUPPORTED_LOCALES (backend) and LANGUAGES array (frontend) include all 5 new codes | unit | sync test (extend E9 in F18) |
| 9 | Arabic numerals render correctly in deal amount display (ar-SA locale) | unit | Intl.NumberFormat test |
8. Current Behavior
Shipped across 5 PRs:
- Phase 1 (Arabic):
arin LANGUAGES array withdir: 'rtl';applyDocumentDirection()toggleshtml[dir]; Noto Sans Arabic loaded lazily via<link>injection; Tailwindrtl:variants applied to Sidebar, Layout, directional icons; 7 namespaces fully translated (common,pulse,brief,tasks,chat,settings,deals). - Phase 2 (ms + id + fil): 3 languages added to LANGUAGES + SUPPORTED_LOCALES; 10 namespaces each; LTR layout, no RTL changes.
- Phase 3 (lv): Latvian added; 7 namespaces; GDPR data residency copy pending (see open ACs above).
Language selector in Settings now shows all 56 scaffolded locales (LANGUAGES array in src/i18n/index.ts). SUPPORTED_LOCALES in apps/accounts/models.py includes all 5 new codes.
Not yet shipped (open ACs):
- Strategic locale code gate (enforced): checker (
check-strategic-locale-leakage.mjs) passes with 0 unexpected English-identical values outside the narrow allowlist across ms/id/ar/fil/lv critical namespaces. CI gated bynode scripts/check-strategic-locale-leakage.mjs. - Native-language review: translations for ms, id, ar, fil, lv were machine-generated via the i18n-translate pipeline. Professional native-language human review is required before production per §12 Q2. Code gate verifies structural correctness (no English leakage, ICU placeholder integrity); it cannot verify naturalness.
- Cross-browser RTL screenshots: automated RTL E2E spec exists (
rtl.spec.tson chromium/firefox/webkit) but live CI screenshots on all three engines are not yet captured. Pending CI firefox/webkit binary availability. - AI extraction keyword patterns for non-Vietnamese languages (§3, per F08 §12 gap — acknowledged out-of-scope for F69).
9. Error States
- Missing translation key. If a key exists in
en/but is absent fromar/(or any new locale), i18next falls back to English. User sees an English string in an otherwise Arabic UI — visually jarring but not a crash. The parity test (§7 case 4) catches this at CI. - RTL layout regression on new component. A component added without
rtl:Tailwind variants will render LTR in Arabic, breaking the mirror. No automated regression exists beyond E2E screenshots; manual RTL audit required when new components ship. - Noto Sans Arabic font-load failure. If the font CDN is blocked (some corporate networks), Arabic text falls back to the system Arabic font. Visually acceptable but inconsistent. Not an app error — no user-facing message.
- Language not in SUPPORTED_LOCALES (backend). If a user’s language preference is set to a code not in
SUPPORTED_LOCALES,PATCH /api/auth/me/language/returns 400. The frontend language selector only shows validated codes — this should not occur in normal usage. - AI extraction mismatch for Arabic/Malay/Indonesian orgs. Extraction keyword patterns are Vietnamese-only (F08 §12 gap). Arabic or Malay orgs will see lower extraction accuracy. This is an acknowledged out-of-scope issue — not a recoverable error, but a known product limitation flagged in §3.
10. Diagrams & Docs Touched
No new diagrams. frontend/DIAGRAMS.md language selector section update required when Phase 1 shipped (already done).
11. Implementation Audit (2026-06-14)
| # | Claim | Status | Evidence |
|---|---|---|---|
| 1 | ar in LANGUAGES frontend array; RTL handled via RTL_LANGUAGES set + applyDocumentDirection() | ✅ match | frontend/src/i18n/index.ts line 21 ({ code: 'ar', label: 'العربية' }); RTL_LANGUAGES set at line 73; no dir field on individual LANGUAGES entries — direction is applied document-wide by applyDocumentDirection() |
| 2 | ms, id, fil, lv in LANGUAGES array | ✅ match | Grep-verified in frontend/src/i18n/index.ts |
| 3 | html[dir="rtl"] set via applyDocumentDirection() when language=ar | ✅ match | i18n/index.ts implements on language change |
| 4 | Noto Sans Arabic loaded lazily — <link id="noto-sans-arabic"> only when base === 'ar' | ✅ match | CSS :root[lang="ar"] body rule; no load for other languages |
| 5 | Tailwind rtl: variants: Sidebar start-0, border-e, rtl:translate-x-full; Layout ms-64, me-3 | ✅ match | Grep-verified in Sidebar.tsx, Layout components |
| 6 | 5 new languages in SUPPORTED_LOCALES backend | ✅ match | apps/accounts/models.py lines 10–19 (canonical list; apps/ai/language_detection.py does not define SUPPORTED_LOCALES) |
| 7 | Translation parity ≥95% for all 5 languages | ✅ match | i18n.test.ts parity check extended (per §1b ACs) |
| 8 | Eastern Arabic numeral formatting (ar-SA Intl) | ✅ match | frontend/src/lib/format.ts formatNumber/formatCurrency map ar→ar-SA; wired into deal amount displays (DealsPage, DealDetailPanel, TableView, DealTimeline). Unit test format.test.ts asserts Eastern Arabic digits render |
| 9 | Icon mirror audit (chevrons flip, logos do not) | ✅ match | index.css [dir='rtl'] .lucide-chevron-*/arrow-* rule flips directional icons via scaleX(-1); non-directional icons untouched. Test rtl-icons.test.tsx pins the class contract |
| 10 | Cross-browser RTL validation (Chrome/Firefox/Safari) | 🟡 partial | Automated: frontend/tests/e2e/rtl.spec.ts asserts html[dir=rtl]+lang=ar, directional chevron matrix(-1…) transform, and right-anchored sidebar; playwright.config.ts runs it on chromium+firefox+webkit projects. Pending: live green run on all 3 engines in CI (firefox/webkit binaries not installable in the dev sandbox) |
| 11 | MYR currency formatting for ms orgs | ✅ match | F27 §10 implemented: Organization.default_currency (migration 0009) set from owner language via apps/organizations/currency.py (ms→MYR); deal amounts render through formatCurrency(amount, deal.currency ?? org.default_currency) so MYR shows as “RM…”. Backend tests + format.test.ts MYR test green |
| 12 | GDPR data residency copy in Latvian | ✅ match | lv/store.json privacy.gdpr + user_rights and lv/pricing.json enterprise_desc translated to Latvian (incl. EU data-residency statement + Datu valsts inspekcija) |
Summary: 11/12 match, 1 partial (cross-browser RTL — automated spec + firefox/webkit projects added, pending live CI run). Translation parity for all 13 locales is green: the F72 upgrade_* billing keys and sidebar dev keys were propagated to every locale (en-variants verbatim; ar/ms/id/fil/lv translated). MYR currency now ships (F27 §10 Organization.default_currency implemented).
12. Open Questions / Decisions
- Arabic numerals: Use Eastern Arabic numerals (١٢٣) or Western (123) for
ar-SA? Decision: default toIntl.NumberFormat('ar-SA')(Eastern Arabic); add user preference if feedback shows Western is preferred. - Translation source: Community-contributed or professionally translated? Decision: professional review required before production — no machine-only translations.
- Pulse question localisation:
DEFAULT_PULSE_QUESTIONSare hardcoded Vietnamese strings — international users see Vietnamese questions. Out of scope for F69; requires dedicated rework spec covering F07 §9 and F08 §12 (changing question text changes the AI extraction contract). - SUPPORTED_LOCALES location:
apps/accounts/models.pyowns the backend locale list (lines 10–19, 60 codes) — confirmed as the single source of truth.apps/ai/language_detection.pydoes not define this list; it imports/references it. Resolved: all 5 new codes confirmed present. - Currency display for international orgs (decided 2026-06-14): Default currency follows user language:
vi→VND,en→USD,ar→SAR,ms→MYR,id→IDR,fil→PHP,lv→EUR. Implement asOrganization.default_currency(F27 §10). F69 currency ACs blocked until this field exists. - AI extraction keyword patterns:
ai/services.pyuses Vietnamese keyword groups for risk/commitment classification (F08 §12). These misclassify items for Arabic/Malay/Indonesian orgs. LLM-based classification must replace keyword matching before F69 is fully useful in production for these markets.
13. Bug / Change Log
2026-07-10 — #1650: Remove baseline loophole; translate remaining English leakage in critical namespaces
Type: Fix
Summary:
- Deleted
frontend/scripts/strategic-locale-baseline.json— the strategic-locale baseline loophole that allowed hundreds of English-identical values to pass CI. No replacement; baseline mode (--baseline) removed from the checker. - Rewrote
check-strategic-locale-leakage.mjs— only the narrow allowlist gates; every non-allowlisted English-identical value in critical namespaces (pulse, chat, brief, today, commitments, risks, decisions) is a CI failure. - Narrowed allowlist (
strategic-locale-allowlist.json) to 11 entries: brand names only (Lumi, Telegram), technical literals (dev), interpolation-only values ({{submitted}}/{{total}},{{amount}} {{currency}},{{name}}), and brand attribution footers with URLs. Removed 22 previously-allowlisted entries (English text like “CRM Settings”, “Audit Log”, “More ways to add”, “COMMITMENT”, “RISK”, “DECISION”, share_headers, evidence_count patterns) — all now translated in target locales. - 203 unique strings translated per locale (203 × 5 = 1015 translation applied) across ms, id, ar, fil, lv critical namespaces — backfilling all English-identical values that were previously absorbed by the baseline. Translations preserve ICU placeholders, emoji, and JSON structure.
- Tests rewritten: baseline tests removed; new tests verify genuine English sentence fails CI, allowlist entries pass, no baseline file exists, ICU placeholders preserved, root-independent extract/apply scripts.
- CI step unchanged:
node scripts/check-strategic-locale-leakage.mjsruns without flags (no--baselinemode).
Impact:
- Audit row 7 (translation parity): effective parity for critical namespaces now 100% enforced (no baseline leakage). Previously ~25% of critical-namespace values in target locales were still English but silently absorbed by the baseline.
2026-07-02 — Correct path/structural claims: SUPPORTED_LOCALES, LANGUAGES, RTL dir, locale count, E2E block (batch-2 spec audit)
Type: Documentation Fix
Summary:
- §4 SUPPORTED_LOCALES path: corrected from
apps/ai/language_detection.pytoapps/accounts/models.py(lines 10–19, 60 codes).language_detection.pydoes not define this list. - §4 LANGUAGES path: corrected from
src/i18n/config.tstosrc/i18n/index.ts. Config.ts does not exist. - §4
dirfield claim removed: LANGUAGES entries have onlycode+label; RTL direction is handled byRTL_LANGUAGES = new Set(['ar', 'he', 'fa', 'ur'])atsrc/i18n/index.tsline 73 andapplyDocumentDirection()settingdocument.documentElement.dirdocument-wide. No per-entrydirfield. - §4/§6/§8 locale count framing: “8 entries; became 13” → “56 entries total” (LANGUAGES array in
src/i18n/index.tshas 56 entries as of F18 scaffolding; the selector shows all 56). Note:fil(Filipino) andtl(Tagalog) are both present as separate entries. - §7 row 1 E2E block:
describe("Language")block does not exist in any E2E spec; updated to note this. The only language-related E2E islanguage-banner.spec.ts(describe('Language Confirmation Banner (F37)')) which tests detection/confirmation banner, not the language selector. - §11 row 1 structural claim:
arentry in LANGUAGES has nodir: 'rtl'field; corrected to describe actual RTL mechanism (RTL_LANGUAGES set + applyDocumentDirection()). - §11 row 6 path:
apps/ai/language_detection.py→apps/accounts/models.py. - §12 Q4: SUPPORTED_LOCALES ownership confirmed as
apps/accounts/models.py; question resolved.
2026-06-15 — MYR currency for ms orgs (audit row 11 → match); implements F27 §10
Type: Feature
Summary:
- Backend (F27 §10): added
Organization.default_currency(CharField, defaultUSD; migrationorganizations/0009_organization_default_currency). Newapps/organizations/currency.pymaps owner UI language → currency (vi→VND, en→USD, ar→SAR, ms→MYR, id→IDR, fil/tl→PHP, lv→EUR; region subtag stripped; fallback USD).create_organizationsetsdefault_currency=currency_for_language(request.user.language). Exposed read viaOrganizationSerializer(fields="__all__") on/api/organizations/current/. - Frontend: deal amount displays (DealsPage card, DealDetailPanel, TableView) now render via
formatCurrency(Number(amount), deal.currency ?? org.default_currency)so a Malay org shows MYR as “RM…”.Organizationtype gainsdefault_currency?. - Tests:
backend/tests/test_organization_currency.py(16: mapping, model default, language→currency on create, per-tenantcurrent/exposure, cross-tenant isolation).frontend/src/lib/format.test.tsadds MYR-as-RM and VND-no-fraction cases.
Reason:
- Closes the last actionable F69 open AC (MYR currency), unblocked by implementing F27 §10
Organization.default_currencyper §12 Q5.
Impact:
- New nullable-defaulted model field + migration (append-only). Tenant-scoped reads. No breaking API change. F27 spec updated in the same change.
Tests:
- Backend currency suite green; frontend
format.test.tsgreen;tscclean on touched files.
2026-06-15 — Automated cross-browser RTL E2E (audit row 10 → partial)
Type: Test
Summary:
- Added
frontend/tests/e2e/rtl.spec.ts: switches UI to Arabic via thei18nextLnglocalStorage key, then asserts (a)document.documentElementcarriesdir="rtl"+lang="ar", (b) a directionallucide-chevronicon resolves to amatrix(-1…)(horizontally mirrored) computed transform, (c) the sidebar is anchored to the right half of the viewport in RTL. - Extended
frontend/playwright.config.tswithfirefox(Desktop Firefox) andwebkit(Desktop Safari) projects alongsidechromium, so the spec runs on all three engines. - Audit row 10 moved gap → 🟡 partial: the automation exists; a green run across firefox/webkit awaits CI (those browser binaries don’t install in the dev sandbox).
Reason:
- Close the “manual only” gap on the cross-browser RTL AC with a deterministic, engine-agnostic spec.
Impact:
- Frontend E2E only; no app code change.
tsc --noEmitunaffected (e2e specs compile clean).
Tests:
frontend/tests/e2e/rtl.spec.ts(new), run viachromium/firefox/webkitPlaywright projects.
2026-06-15 — Close open ACs: Arabic numerals, RTL icon mirror, Latvian GDPR copy, locale parity
Type: Feature / Bug Fix
Summary:
- Arabic numeral formatting (audit row 8): added
frontend/src/lib/format.ts(localeFor,formatNumber,formatCurrency) mappingar→ar-SAso deal amounts render Eastern Arabic numerals (١٢٣). Wired into DealsPage, DealDetailPanel, TableView, DealTimeline (replacing rawtoLocaleString()). Unit testlib/format.test.ts. - RTL icon mirror audit (audit row 9): added a
[dir='rtl']CSS rule inindex.cssflipping directional lucide icons (.lucide-chevron-*,.lucide-arrow-*,.lucide-chevrons-*, etc.) viascaleX(-1); non-directional icons (logos, avatars, status glyphs) are untouched. Testtest/rtl-icons.test.tsxpins the lucide class contract. - Latvian GDPR copy (audit row 12): translated
lv/store.jsonprivacy.gdpr(incl. explicit EU data-residency statement + Latvian Datu valsts inspekcija) andprivacy.user_rights, pluslv/pricing.jsonenterprise_descdata-residency line. - Translation parity (≥95% AC): propagated the F72
upgrade_*billing keys (12) andsidebardev keys (2) — present inenbut missing everywhere else — to all 11 non-vi locales. en-variants copy English verbatim; ar/ms/id/fil/lv human-translated. i18n.test.ts parity suite green (1631 tests pass).
Reason:
- Five open ACs remained per §11; this closes three implementable ones plus the cross-locale parity gap surfaced by the parity test. Cross-browser RTL automation (row 10) and MYR currency (row 11, blocked on F27 §10
Organization.default_currency) remain open.
Impact:
- Frontend only. No backend/API/data-model change. Gate:
tsc --noEmitclean;i18n.test.ts+format.test.ts+rtl-icons.test.tsxgreen.
Tests:
frontend/src/lib/format.test.ts(new),frontend/src/test/rtl-icons.test.tsx(new),frontend/src/test/i18n.test.ts(parity now green across 13 locales).
2026-06-14 — Strategy v2 realignment (re-sequenced/deferred)
Type: Documentation Change
Summary:
- Added a “Strategic position (strategy v2, 2026-06-14)” note to §1 reframing the ar/ms/id/fil/lv UI languages as i18n scaffolds parked behind Vietnam vertical proof, not active market unlocks; flagged the “each language is a market unlock / Baltic market entry” framing as premature under v2. Vietnamese is the first-class language.
Reason:
- Align with competitive-strategy-v2.md and the registry’s Positioning doctrine (v2): country/language breadth is downstream of vertical proof (“validation before velocity”).
Impact:
- Priority/positioning only. Feature, status, API, data model, and tests are unchanged.
Tests:
- None (documentation-only change).
Notes:
- Scaffolds retained; do not treat the language list as a sequenced market-entry roadmap until the Vietnam fit-out/renovation/M&E vertical is proven.
2026-06-14 — Restructure to TEMPLATE.md section order; add §8 Current Behavior + §9 Error States (SP fleet)
Type: Documentation Change
Summary:
- Added §8 Current Behavior (describing shipped state per phase + open ACs).
- Added §9 Error States (5 items: missing key fallback, RTL regression, font failure, invalid locale, AI extraction mismatch).
- Renumbered §8 Diagrams → §10, §9 Implementation Audit → §11, §10 Open Questions → §12, §12 Change Log → §13.
- §11 Implementation Audit: expanded from grep-table to 12-row audit with 5 gaps flagged.
- Absorbed §11 Implementation Plan (was planning notes) into spec history — plan is done, phases shipped.
2026-06-14 — Spec recreated after fleet rebase
Type: Documentation
Summary:
- Fleet agent deleted spec as “out-of-scope” due to commit timing issue. Re-added to registry + spec recreated with same content.
2026-06-13 — Spec created
Type: New Spec
Summary:
- F69 added to registry and spec created. North Star targets VN, MY, ID, KSA, PH, LV; F18 is the en/vi foundation; F69 is the expansion to 5 additional markets.
- Arabic ships first (RTL complexity), then ms+id together, then fil, then lv.
- Key pre-conditions: org-level default currency (F27 §10), AI keyword pattern multilingualization (F08 §12), pulse question localisation (F07 §9) all out of scope for F69 but required for full market utility.