14 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, gap_closure, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | gap_closure | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 34-i18n-foundation | 06 | execute | 4 |
|
|
true | true |
|
|
Purpose: UAT test 4 revealed that only the settings page, nav bar, and FAB were translated. The home page, collection components, setups, profile, and many other components were never wired to i18n. This plan closes that gap by adding useTranslation to every remaining file. Output: All 14 components and 3 routes fully internationalized, with new locale keys added to both en and de JSON files.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/34-i18n-foundation/34-CONTEXT.md @.planning/phases/34-i18n-foundation/34-UAT.md useTranslation hook pattern (already established in codebase): ```typescript import { useTranslation } from "react-i18next";function MyComponent() { const { t } = useTranslation("common"); // or specific namespace return {t("actions.save")}; }
// For multiple namespaces: const { t } = useTranslation(["collection", "common"]); // Access: t("collection:totals.totalWeight"), t("common:actions.save")
For interpolation:
```typescript
t("items.count", { count: 5 }) // "5 items"
Existing namespace structure:
common— nav, actions, errors, auth, shared stringscollection— collection page, item cards, forms, weight summary, totals, classificationsthreads— thread list, candidates, comparison, status badgessetups— setup list, setup detail, share, impactonboarding— onboarding flow screenssettings— settings page sections
src/client/routes/index.tsx (home/discovery page):
- Use
const { t } = useTranslation("common")(or["common", "collection"]if it shows collection-related text) - Replace all section headings (e.g., "Popular Setups", "Recently Added", "Trending Categories", etc.) with t() calls
- Replace empty states, loading text, CTAs like "Go to Collection" with t() calls
- Add new keys to en/common.json under a
homeordiscoverysection, e.g.:"home": { "popularSetups": "Popular Setups", "recentlyAdded": "Recently Added", "trendingCategories": "Trending Categories", "goToCollection": "Go to Collection" } - Add corresponding German translations to de/common.json:
"home": { "popularSetups": "Beliebte Setups", "recentlyAdded": "Kürzlich hinzugefügt", "trendingCategories": "Trend-Kategorien", "goToCollection": "Zur Sammlung" } - Do NOT translate user-generated content (setup names, item names, user names)
src/client/routes/setups/index.tsx (setups list page):
- Use
const { t } = useTranslation(["setups", "common"]) - Replace headings like "Setups", "Your Setups", empty state text, CTA buttons
- Add new keys to en/setups.json and de/setups.json as needed
src/client/routes/profile.tsx:
- Use
const { t } = useTranslation("common") - Replace headings like "Profile", "Your Gear", "Public Setups", any labels or descriptions
- Add new keys under a
profilesection in en/common.json and de/common.json
src/client/routes/settings.tsx (currency suggestion banner only):
- The file already has useTranslation. Find the currency suggestion banner (around line 298) that shows "Based on your region, we suggest {symbol} ({code})" and the "Switch" and "Dismiss" buttons.
- Add new keys to en/settings.json:
"currency": { ..., "suggestion": "Based on your region, we suggest {{symbol}} ({{code}})", "switch": "Switch" } - Add German translations:
"currency": { ..., "suggestion": "Basierend auf Ihrer Region empfehlen wir {{symbol}} ({{code}})", "switch": "Wechseln" } - Replace the hardcoded banner text with
t("currency.suggestion", { symbol: ..., code: suggestedCurrency }) - Replace "Switch" button text with
t("currency.switch") - The "Dismiss" button's aria-label should also use t()
CRITICAL: For every new key added to an en/.json file, add the corresponding German translation to the de/.json file. Use proper German umlauts (ä, ö, ü, Ä, Ö, Ü, ß) — NOT ASCII fallbacks. cd /home/jlmak/Projects/jlmak/GearBox && for f in src/client/routes/index.tsx src/client/routes/setups/index.tsx src/client/routes/profile.tsx; do echo -n "$(basename $f): "; grep -c "useTranslation" "$f"; done && grep -c "suggestion" src/client/locales/en/settings.json All 3 route pages and settings currency suggestion use useTranslation with t() calls, locale files updated for both en and de
Task 2: Wire useTranslation into remaining 11 components src/client/components/DashboardCard.tsx, src/client/components/ThreadTabs.tsx, src/client/components/PlanningView.tsx, src/client/components/TotalsBar.tsx, src/client/components/ThreadCard.tsx, src/client/components/PublicSetupCard.tsx, src/client/components/SetupImpactSelector.tsx, src/client/components/ClassificationBadge.tsx, src/client/components/ImpactDeltaBadge.tsx, src/client/components/ImageUpload.tsx, src/client/locales/en/common.json, src/client/locales/en/collection.json, src/client/locales/en/threads.json, src/client/locales/en/setups.json, src/client/locales/de/common.json, src/client/locales/de/collection.json, src/client/locales/de/threads.json, src/client/locales/de/setups.json src/client/components/DashboardCard.tsx, src/client/components/ThreadTabs.tsx, src/client/components/PlanningView.tsx, src/client/components/TotalsBar.tsx, src/client/components/ThreadCard.tsx, src/client/components/PublicSetupCard.tsx, src/client/components/SetupImpactSelector.tsx, src/client/components/ClassificationBadge.tsx, src/client/components/ImpactDeltaBadge.tsx, src/client/components/ImageUpload.tsx, src/client/locales/en/collection.json, src/client/locales/en/threads.json, src/client/locales/en/setups.json, src/client/locales/en/common.json For EACH of the 11 components listed below, read the file fully, then: 1. Add `import { useTranslation } from "react-i18next"` 2. Add `const { t } = useTranslation(...)` with the appropriate namespace 3. Replace every hardcoded English string with the corresponding t() call 4. Add any new keys to both en and de locale JSON filesNamespace assignments:
DashboardCard.tsx→useTranslation("collection")— labels like "Total Weight", "Total Price", "Items", stat labelsThreadTabs.tsx→useTranslation("threads")— tab labels like "All", "Active", "Resolved", "Archived"PlanningView.tsx→useTranslation(["threads", "common"])— "Planning" heading, "Research Threads" section title, empty states, "Start a Thread" CTATotalsBar.tsx→useTranslation("collection")— "Total Weight", "Total Cost", weight/price summary labelsThreadCard.tsx→useTranslation("threads")— thread card labels, candidate count text, status textPublicSetupCard.tsx→useTranslation("setups")— "items", "by", setup card labelsSetupImpactSelector.tsx→useTranslation("setups")— "Compare with setup", "Select a setup", impact labelsClassificationBadge.tsx→useTranslation("collection")— "Ultralight", "Light", "Medium", "Heavy" classification labelsImpactDeltaBadge.tsx→useTranslation("setups")— delta labels like "lighter", "heavier", "+", "-" prefix text if anyImageUpload.tsx→useTranslation("common")— "Upload image", "Click to upload", "Drop image here", file size/type error messages
For each component: Read it fully. Find every string literal that is user-visible UI chrome (not CSS classes, not data attributes, not code identifiers). Replace with the matching t() key. If the key does not exist in the en locale file, add it in the appropriate namespace JSON.
CRITICAL: For every new key added to an en/.json file, add the corresponding German translation to the de/.json file. Use proper German umlauts (ä, ö, ü, Ä, Ö, Ü, ß) — NOT ASCII fallbacks. Examples:
- "Ultralight" → "Ultraleicht"
- "Items" → "Gegenstände"
- "Upload image" → "Bild hochladen"
- "lighter" → "leichter"
- "heavier" → "schwerer"
Do NOT translate: User-generated content (item names, setup names, thread titles, category names created by users).
If a component has NO hardcoded translatable strings (e.g., it only renders numeric data or user content), skip it — do not add unnecessary imports. cd /home/jlmak/Projects/jlmak/GearBox && for f in DashboardCard ThreadTabs PlanningView TotalsBar ThreadCard PublicSetupCard SetupImpactSelector ClassificationBadge ImpactDeltaBadge ImageUpload; do echo -n "$f: "; grep -c "useTranslation" src/client/components/$f.tsx; done && bun run build 2>&1 | tail -3 All 11 components use useTranslation with t() calls, no hardcoded English UI chrome remains, locale files updated for both en and de, build passes
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| translation files→DOM | Translation strings rendered in JSX — React escapes by default |
STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|---|---|---|---|---|
| T-34-07 | Injection | t() output in JSX | accept | i18next interpolation escapeValue is false BUT React's JSX escaping prevents XSS. Translation strings are bundled static content, not user input. Same mitigation as T-34-03 from Plan 02. |
| </threat_model> |
<success_criteria>
- Every file listed in the UAT gap has useTranslation wired in
- No hardcoded English UI chrome strings remain in these files
- All new en keys have corresponding de translations with proper umlauts
- Key parity test still passes
- Build passes </success_criteria>