--- phase: 34-i18n-foundation plan: "03" subsystem: ui tags: [i18n, formatters, intl, locale, react-hooks, typescript] # Dependency graph requires: - phase: 34-i18n-foundation/34-01 provides: i18n infrastructure, translation framework, useSetting hook patterns provides: - Locale-aware formatPrice using Intl.NumberFormat (en: "$1,234.56", de: "1.234,56 €") - Locale-aware formatWeight using Intl.NumberFormat (en: "1,234g", de: "1.234g") - useLanguage hook reading language from settings with "en" fallback - useFormatters hook wiring locale into all format calls - Formatter test suite covering null, en locale, de locale, unit conversions affects: [34-04, 34-05, 34-06, 34-07, 34-08] # Tech tracking tech-stack: added: [] patterns: - "Intl.NumberFormat for all number/currency formatting instead of manual symbol lookup" - "locale parameter added as third argument (defaulting to 'en') for backward compatibility" - "useLanguage follows same pattern as useWeightUnit/useCurrency: useSetting + VALID_* array + default" key-files: created: - src/client/hooks/useLanguage.ts - tests/formatters.test.ts modified: - src/client/lib/formatters.ts - src/client/hooks/useFormatters.ts key-decisions: - "locale parameter defaults to 'en' so existing callers without locale continue to work" - "CURRENCY_SYMBOLS constant removed — Intl.NumberFormat handles symbols natively" - "VALID_LANGUAGES ['en', 'de'] validates DB value before returning; invalid falls back to 'en' (T-34-04 threat mitigation)" patterns-established: - "Locale-aware formatting: all number/price/weight formatters accept locale as third arg" - "Settings hook pattern: useSetting + VALID_* array const + default fallback" requirements-completed: [D-04, D-09, D-10] # Metrics duration: 15min completed: 2026-04-18 --- # Phase 34 Plan 03: Locale-Aware Formatter Integration Summary **Intl.NumberFormat-based locale-aware formatPrice and formatWeight with useLanguage hook — German locale shows "1.234,56 €", English shows "$1,234.56"** ## Performance - **Duration:** ~15 min - **Started:** 2026-04-18T12:15:00Z - **Completed:** 2026-04-18T12:30:00Z - **Tasks:** 5 - **Files modified:** 4 ## Accomplishments - `useLanguage()` hook reads language from settings DB, validates against `VALID_LANGUAGES`, falls back to "en" - `formatPrice()` updated to use `Intl.NumberFormat(locale, { style: "currency", currency })` — CURRENCY_SYMBOLS removed - `formatWeight()` updated to use `Intl.NumberFormat(locale, { minimumFractionDigits, maximumFractionDigits })` for locale-aware separators - `useFormatters()` extended to call `useLanguage()` and pass locale to both formatters, exposing locale in return value - 15-test suite covering null, en/de locales, unit conversions, JPY special case, large number thousands separators ## Task Commits All tasks were implemented in a prior commit and verified as complete at plan execution time: - **Tasks 1-4: locale-aware formatters and useLanguage hook** - `f759dd0` (feat) - **Task 5: formatter tests** - `f759dd0` (feat/test) Note: Implementation pre-existed this plan's execution in commit `f759dd0` (feat(i18n): locale-aware formatters and useLanguage hook). All acceptance criteria verified as passing. ## Files Created/Modified - `src/client/hooks/useLanguage.ts` — Hook reading "language" setting, returning Language type with "en" fallback, exports VALID_LANGUAGES - `src/client/lib/formatters.ts` — formatPrice and formatWeight updated with locale parameter and Intl.NumberFormat; CURRENCY_SYMBOLS removed - `src/client/hooks/useFormatters.ts` — Extended with useLanguage import, locale passed to both formatters, locale in return object - `tests/formatters.test.ts` — 15 tests for formatPrice and formatWeight across locales, units, null, and edge cases ## Decisions Made - Locale parameter defaults to "en" to preserve backward compatibility with callers that don't pass locale - CURRENCY_SYMBOLS constant removed entirely — Intl.NumberFormat handles currency symbols natively for all currencies - T-34-04 threat mitigation applied: VALID_LANGUAGES validation ensures untrusted DB values can't cause unexpected locale behavior ## Deviations from Plan None - plan executed exactly as written. All files were already implemented in the correct state matching the plan's acceptance criteria. ## Issues Encountered None. ## Known Stubs None — all formatters produce real locale-aware output from Intl.NumberFormat. ## Threat Flags None — no new network endpoints, auth paths, or trust boundaries introduced. The T-34-04 threat (tampering via settings DB language value) is mitigated by VALID_LANGUAGES validation in useLanguage. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Locale-aware formatters are ready for phase 34-04 (UI locale switching) and 34-05 (currency display) - useLanguage hook is consumed by useFormatters, which is used app-wide via the `useFormatters()` hook pattern - Build passes cleanly, all 15 formatter tests pass ## Self-Check: PASSED - `src/client/hooks/useLanguage.ts` — FOUND - `src/client/lib/formatters.ts` — FOUND (Intl.NumberFormat: 2 occurrences, CURRENCY_SYMBOLS: 0 occurrences) - `src/client/hooks/useFormatters.ts` — FOUND (useLanguage + locale: 6 occurrences) - `tests/formatters.test.ts` — FOUND (15 tests, all pass) - Build: PASSED (built in 937ms) - Commit f759dd0 — FOUND --- *Phase: 34-i18n-foundation* *Completed: 2026-04-18*