---
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*
