Files
GearBox/.planning/phases/34-i18n-foundation/34-CONTEXT.md

6.5 KiB

Phase 34: i18n Foundation - Context

Gathered: 2026-04-13 Status: Ready for planning

## Phase Boundary

Add a translation framework to GearBox with string extraction, locale-aware formatting, and ship English + German. UI chrome and system content are translated. Catalog data and user-generated content remain untranslated. Language selection is independent from market/currency (Phase 33) but both auto-detected from browser.

## Implementation Decisions

Translation Scope & Boundaries

  • D-01: Translate UI chrome: buttons, labels, headings, navigation items, empty states, error messages, toast notifications, modal titles/descriptions, placeholder text
  • D-02: Translate system content: default category names (e.g., "Uncategorized"), onboarding flow text, MCP tool descriptions, email templates if any
  • D-03: Do NOT translate: catalog item names/descriptions, user-generated content (item names, notes, setup names, thread titles), category names created by users
  • D-04: Locale-aware formatting integrates with the existing useFormatters() hook — number formatting, date formatting, and pluralization handled by the i18n framework, weight/price formatting continues through existing formatters

Library & Architecture

  • D-05: Claude's discretion on library choice — pick between react-i18next and Lingui based on best fit with React 19, Vite, Bun, Hono stack. Key criteria: hook-based API, lazy loading per locale, compile-time or runtime extraction, TypeScript support
  • D-06: Translation files stored as JSON in the repo: src/client/locales/en.json, src/client/locales/de.json. Checked into git. Switching to an external translation service (Crowdin/Lokalise) later is a CI/sync change, not a code change
  • D-07: Translations loaded client-side — the React app loads the appropriate locale JSON. Server-side strings (API error messages, MCP descriptions) use a simple server-side translation utility
  • D-08: Namespace support for organizing strings by feature area (e.g., common, collection, threads, setups, onboarding, settings) to keep files manageable as string count grows

Language Selection UX

  • D-09: Language and market/currency are independent settings. A German expat in the UK can have GBP prices but German UI
  • D-10: Language auto-detected from browser locale on first visit (navigator.language). User can override in settings
  • D-11: Language picker in settings page — alongside but separate from the market/currency picker from Phase 33
  • D-12: If browser locale has no matching translation (e.g., ja), fall back to English

First Additional Language

  • D-13: German (de) ships alongside English (en) as the first additional language. Primary target market is EU/DE
  • D-14: German translations AI-generated by Claude during implementation. No formal review step — user catches and fixes issues organically during app usage
  • D-15: Translation quality approach for future languages: same AI-generated strategy. Professional/community translation deferred until there's a real user base requesting specific languages

Claude's Discretion

  • Library choice between react-i18next and Lingui (evaluate DX, bundle size, extraction tooling, React 19 compatibility)
  • String key naming convention (flat vs. nested, dot notation style)
  • How to handle dynamic content interpolation patterns
  • Whether to extract strings from existing components in one pass or incrementally

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

No external specs — requirements fully captured in decisions above.

Existing Implementation (to integrate with)

  • src/client/hooks/useFormatters.ts — Central formatting hook for weight + price. i18n number/date formatting should integrate here
  • src/client/lib/formatters.tsformatWeight() and formatPrice() functions. Locale-aware formatting may need to wrap or replace these
  • src/client/hooks/useCurrency.ts — Currency/market hook. Language selection is separate but both auto-detect from browser
  • src/client/routes/settings.tsx — Settings page where language picker will be added
  • src/client/routes/__root.tsx — Root layout where i18n provider wraps the app
  • src/client/main.tsx — App entry point for i18n initialization
  • src/server/mcp/ — MCP tool descriptions need server-side translation
  • src/client/components/onboarding/ — Onboarding flow has significant translatable text

Phase 33 Integration

  • src/client/hooks/useCurrency.ts — Market auto-detection logic from Phase 33. Language auto-detection should follow the same pattern but remain independent

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • useFormatters() hook: Already composites weight + price formatting. Extend to include locale-aware number/date formatting
  • useSetting() hook: Settings storage pattern — language preference fits here
  • Settings page: Existing pill-toggle pattern (used for weight units, currency) — reuse for language picker

Established Patterns

  • Hooks for user preferences (useWeightUnit, useCurrency) — useLanguage follows the same pattern
  • Settings stored in DB via settings table, read via useSetting() hook
  • Component structure: presentational components in components/, route components in routes/

Integration Points

  • src/client/main.tsx: Initialize i18n provider
  • src/client/routes/__root.tsx: Wrap app in i18n context provider
  • src/client/routes/settings.tsx: Add language picker
  • Every component with hardcoded English strings: needs t() calls (bulk extraction task)
  • src/server/index.ts: Server-side translation utility initialization for API errors and MCP descriptions

Scale of String Extraction

  • Estimated 100-200 translatable strings across the app (buttons, labels, headings, empty states, error messages, onboarding flow)
  • Onboarding flow is the most string-heavy component

</code_context>

## Specific Ideas
  • Language picker uses the same pill-toggle pattern as weight units and currency in settings
  • Auto-detection: navigator.language → match to available locales → fallback to en
  • String extraction can be done incrementally — doesn't need to be all-at-once
  • German translations generated alongside English during implementation, not as a separate post-extraction step
## Deferred Ideas

None — discussion stayed within phase scope.


Phase: 34-i18n-foundation Context gathered: 2026-04-13