7.6 KiB
Design: i18n Implementation Architecture
Context
Claude Code UI is a React-based web application with ~50 components, currently serving English-speaking users only. The application uses:
- React 18 with functional components and hooks
- Context-based state management (AuthContext, ThemeContext, etc.)
- localStorage for user preferences
- Vite for bundling
Constraints:
- Must not impact performance (bundle size, runtime overhead)
- Must support hot-reload during development
- Must allow switching language without page reload
- Must be extensible for additional languages in the future
Stakeholders:
- End users: Need intuitive language switching
- Developers: Need easy translation management workflow
- Maintainers: Need to keep translations synchronized
Goals / Non-Goals
Goals:
- Implement production-ready i18n using
react-i18next - Support English (en) and Simplified Chinese (zh-CN) in Phase 1
- Provide language selector in Settings > Account
- Store user language preference in localStorage
- Extract and translate top 100 most common UI strings in Phase 1
- Create reusable translation namespace structure
Non-Goals:
- Translate AI-generated content (chat messages, code responses)
- Translate backend error logs or terminal output
- Implement server-side translation
- Create translation management web UI
- Add RTL (Right-to-Left) language support
Decisions
Decision 1: Use react-i18next Library
Rationale:
- Industry-standard for React internationalization (2.5M weekly downloads)
- Built-in hooks (
useTranslation) compatible with React 18 - Supports interpolation, plurals, date/number formatting
- Excellent TypeScript support (even though we use JS)
- Lazy-loading and code-splitting for translations
- Active community and long-term viability
Alternatives Considered:
- Format.js (React Intl): More complex setup, larger bundle size
- Lingui: Compile-time approach, requires Babel plugin, over-engineering for our needs
- Custom solution: Would require building interpolation, plurals, date formatting from scratch
Decision 2: Namespace-Based Translation Structure
Structure:
src/i18n/
├── locales/
│ ├── en/
│ │ ├── common.json # Shared UI strings
│ │ ├── settings.json # Settings page
│ │ ├── auth.json # Login/Auth
│ │ └── sidebar.json # Navigation/Sidebar
│ └── zh-CN/
│ ├── common.json
│ ├── settings.json
│ ├── auth.json
│ └── sidebar.json
├── config.js # i18next configuration
└── ReactSuspense.js # Lazy-loading wrapper
Rationale:
- Namespace-based approach aligns with feature boundaries
- Smaller files = easier to manage translations
- Supports code-splitting (load only needed namespaces)
- Scalable for future languages
Decision 3: Language Preference Storage in localStorage
Rationale:
- Consistent with existing preferences (theme, code editor settings)
- No backend changes required
- Instant sync across tabs
- Works in offline mode
Storage Key: userLanguage
Default Value: 'en' (English)
Valid Values: 'en', 'zh-CN'
Decision 4: Translation Key Naming Convention
Pattern: {namespace}.{component}.{element}.{state}
Examples:
common.buttons.save→ "Save"common.buttons.cancel→ "Cancel"settings.account.language.label→ "Language"auth.login.title→ "Sign In"sidebar.projects.new→ "New Project"
Rationale:
- Hierarchical structure mirrors component tree
- Easy to locate translation key in code
- Prevents naming collisions across namespaces
- Self-documenting
Decision 5: Fallback Strategy
Fallback Chain:
- User's selected language (e.g.,
zh-CN) - English (
en) as default fallback - Return translation key if not found in any language
Configuration:
fallbackLng: 'en',
debug: false, // Set true in development to log missing keys
saveMissing: false, // Don't auto-create missing keys (manual review required)
Rationale:
- English is source language, guaranteed to have all keys
- Showing translation key in production is better than blank text
- Manual review prevents typo propagation
Risks / Trade-offs
Risk 1: Bundle Size Increase
Impact: Initial bundle may increase by ~30KB (gzip) for react-i18next + translations
Mitigation:
- Use code-splitting to load translations per namespace
- Enable tree-shaking for unused languages
- Target Phase 1: Top 100 strings only
Monitoring:
npm run build
# Check dist/ size before/after
Risk 2: Missing Translations at Runtime
Impact: Users may see English text mixed with Chinese
Mitigation:
- Use
useTranslationwith{ fallbackLng: 'en' } - Implement
missingKeyHandlerto log missing keys in development - Create translation coverage report in CI
Risk 3: Translation Synchronization Debt
Impact: New features may forget to add translations
Mitigation:
- Add ESLint rule to detect hardcoded strings in JSX
- Include i18n checklist in PR template
- Document translation workflow in CLAUDE.md
Risk 4: Context-Aware Translations
Example: "Save" could mean:
- Save file (保存文件)
- Save money (节省)
- Rescue (救援)
Mitigation:
- Use descriptive keys:
common.buttons.saveFilevscommon.buttons.saveMoney - Add context comments for translators:
{{context}} "Save file to disk"
Migration Plan
Phase 1: Infrastructure Setup (Day 1)
- Install
react-i18nextandi18next - Create
src/i18n/directory structure - Configure i18next with lazy-loading
- Add I18nextProvider to App.jsx
- Create translation resource files (en, zh-CN)
Phase 2: Core UI Translation (Day 2-3)
Priority Order:
- Common buttons and labels (Save, Cancel, Delete, Create)
- Navigation elements (Settings, menus)
- Settings page
- Auth/Login pages
- Sidebar labels
Process per component:
- Replace hardcoded strings with
useTranslation()hook - Add translation key to JSON files
- Verify English still works
- Add Chinese translation
- Test language switching
Phase 3: Language Selector (Day 3)
- Add language dropdown to
AccountContent.jsx - Implement
changeLanguage()handler - Save preference to localStorage
- Test reload persistence
Phase 4: Validation (Day 4)
- Manual testing of all translated components
- Check for missing keys (console warnings)
- Test language switching in both directions
- Verify localStorage persistence
- Test mobile responsive layout with longer Chinese text
Rollback Plan
If critical issues arise:
- Revert to commit before i18n changes
- Keep translation files for future retry
- Document blockers for next attempt
Rollback Command:
git revert <i18n-commit-hash>
npm install
Open Questions
-
Should we add TypeScript support for translations?
- Current: No, project uses JavaScript
- Future consideration: Migrate to TypeScript for better type safety
-
Should we integrate with translation management platform (e.g., Crowdin, Lokalise)?
- Current decision: No, manual JSON editing is sufficient for 2 languages
- Future: Consider when adding 3+ languages or external translators
-
Should we format dates and numbers according to locale?
- Current decision: No, keep ISO format for simplicity
- Future: Use
i18next-intlpluralif users request localized dates
-
How to handle pluralization in Chinese (which doesn't have plurals)?
- Solution: Use same translation for singular/plural forms
- Example:
"1 file"→"1 个文件","2 files"→"2 个文件"