From 1992778ce6a9083c232710c375a08312fdb29d29 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 13 Apr 2026 18:00:10 +0200 Subject: [PATCH] docs(34): capture phase context --- .../phases/34-i18n-foundation/34-CONTEXT.md | 115 ++++++++++++++++++ .../34-i18n-foundation/34-DISCUSSION-LOG.md | 92 ++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 .planning/phases/34-i18n-foundation/34-CONTEXT.md create mode 100644 .planning/phases/34-i18n-foundation/34-DISCUSSION-LOG.md diff --git a/.planning/phases/34-i18n-foundation/34-CONTEXT.md b/.planning/phases/34-i18n-foundation/34-CONTEXT.md new file mode 100644 index 0000000..8e3d4c3 --- /dev/null +++ b/.planning/phases/34-i18n-foundation/34-CONTEXT.md @@ -0,0 +1,115 @@ +# 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 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.ts` — `formatWeight()` 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 + + + + +## 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 + + + + +## 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* diff --git a/.planning/phases/34-i18n-foundation/34-DISCUSSION-LOG.md b/.planning/phases/34-i18n-foundation/34-DISCUSSION-LOG.md new file mode 100644 index 0000000..da0ee31 --- /dev/null +++ b/.planning/phases/34-i18n-foundation/34-DISCUSSION-LOG.md @@ -0,0 +1,92 @@ +# Phase 34: i18n Foundation - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-04-13 +**Phase:** 34-i18n Foundation +**Areas discussed:** Translation scope & boundaries, Library & architecture, Language selection UX, First additional language + +--- + +## Translation Scope & Boundaries + +| Option | Description | Selected | +|--------|-------------|----------| +| UI chrome only | Buttons, labels, headings, empty states, errors, navigation | | +| UI chrome + system content | UI chrome plus default categories, onboarding text, MCP descriptions | ✓ | +| Everything including catalog | UI + system + catalog item names/descriptions per locale | | + +**User's choice:** UI chrome + system content +**Notes:** Catalog data translation deferred — too much content to maintain translations for. + +--- + +## Library & Architecture + +### Library choice + +| Option | Description | Selected | +|--------|-------------|----------| +| react-i18next | Most popular, hook-based, JSON files, namespaces, lazy loading | | +| Lingui | Compile-time extraction, smaller runtime, macro-based | | +| You decide | Claude picks based on stack fit | ✓ | + +**User's choice:** Claude's discretion + +### Translation storage + +| Option | Description | Selected | +|--------|-------------|----------| +| JSON files in repo | en.json, de.json checked into git. Simple, reviewable in PRs | ✓ | +| External translation service | Crowdin/Lokalise with web UI for translators | | +| You decide | Claude picks | | + +**User's choice:** JSON in repo. User asked whether this is easy to switch later — confirmed it is. Translation keys are the same regardless of storage; switching to external service is a CI/sync change, not a code rewrite. + +--- + +## Language Selection UX + +| Option | Description | Selected | +|--------|-------------|----------| +| Separate setting | Language and currency/market are independent | | +| Tied to market | EUR/DE = German, GBP/UK = English | | +| Auto-detect with override | Browser locale auto-detect, independent from market, overridable | ✓ | + +**User's choice:** Auto-detect with override, independent from market/currency. + +--- + +## First Additional Language + +### Language choice + +| Option | Description | Selected | +|--------|-------------|----------| +| German (de) | Primary target market, user speaks it natively | ✓ | +| French (fr) | Tests different grammar family | | +| Both German + French | Stress-tests the system | | + +**User's choice:** German. User noted they can't validate French ("aint speaking french, can only send ai agents to test"). + +### Translation production + +| Option | Description | Selected | +|--------|-------------|----------| +| Manual translation | User writes German strings | | +| AI-generated, user reviews | Claude generates, user does formal review pass | | +| AI-generated, fix organically | Claude generates, user catches issues during normal app usage | ✓ | + +**User's choice:** AI-generated, no dedicated review — fix issues as they're noticed. + +## Claude's Discretion + +- Library choice (react-i18next vs. Lingui) +- String key naming convention +- Dynamic content interpolation patterns +- Extraction strategy (bulk vs. incremental) + +## Deferred Ideas + +None — discussion stayed within phase scope.