feat: complete i18n internationalization system with 9 language support#577
Open
openasocket wants to merge 92 commits intoRunMaestro:mainfrom
Open
feat: complete i18n internationalization system with 9 language support#577openasocket wants to merge 92 commits intoRunMaestro:mainfrom
openasocket wants to merge 92 commits intoRunMaestro:mainfrom
Conversation
…English base translations Install i18next, react-i18next, i18next-browser-languagedetector, i18next-fs-backend, and i18next-http-backend. Create src/shared/i18n/config.ts with initI18n() function supporting 9 languages and 6 namespaces. Add English base translation files with skeleton keys for common, settings, modals, menus, notifications, and accessibility namespaces. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create placeholder translation files (copied from English base) for es, fr, de, zh, hi, ar, bn, pt. Each directory contains all 6 namespace files (common, settings, modals, menus, notifications, accessibility). These will be translated in I18N-11. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds locale:get-system and locale:set IPC handlers that detect system locale via Electron's app.getLocale() and app.getPreferredSystemLanguages(), map BCP 47 tags to supported language codes, and persist user preference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Calls initI18n() before ReactDOM render, wraps app tree in Suspense, and detects system locale via IPC when no stored preference exists. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… attribute support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n for typed translations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…imeFormat Add getActiveLocale() helper that reads from the i18n instance for auto-detecting the user's language. Refactor formatRelativeTime() to use Intl.RelativeTimeFormat for localized relative time output (e.g., "5 minutes ago" in English, "hace 5 minutos" in Spanish). Add optional locale parameter for manual override. Update all 8 affected test files to match new output format and add i18n config mocks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e-aware i18n Add optional locale parameter to both functions, backed by i18n translation keys for time unit abbreviations (ms, s, m, h, d and compact M, H, D variants). Add getTimeUnitLabel() helper that resolves translations via i18n.t() with graceful fallback to English templates when i18n is uninitialized. Add "time" section to common.json with all unit abbreviation keys. Update test mock to support t() and add locale parameter test cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ency formatting Use Intl.NumberFormat with style:'currency' and currency:'USD' for locale-aware cost display (e.g., "$1.23" in English, "1,23 $" in German). Fixed ParticipantCard .slice(1) hack by removing DollarSign icon and using full formatted cost string. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aware Intl.NumberFormat compact notation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ss 21 files Renderer/web components (17 files) now use getActiveLocale() from shared/formatters for i18n-aware locale detection. Main process files (director-notes.ts, error-patterns.ts, symphony.ts) and shared templateVariables.ts use undefined to fall back to system default, avoiding browser-side i18n imports in Node.js context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lations
Create CONVENTIONS.md documenting key format (namespace:section.action),
namespace assignment rules, snake_case naming, max 3-level nesting, i18next
plural suffixes, and {{variable}} interpolation patterns.
Populate all 6 English namespace JSON files with example translations
demonstrating the convention patterns including nested keys, interpolation,
and pluralization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create a thin wrapper around useTranslation that provides concise JSX syntax for common translation cases: simple keys, pluralization, and interpolation. Also creates barrel export for shared components directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create tNotify() wrapper around notifyToast() that translates titleKey and messageKey via i18n.t() direct import (not React hook), enabling translated toast notifications from any context. Includes barrel export at src/renderer/utils/index.ts and 5 passing tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…trings Creates scripts/i18n-audit.ts that scans .tsx files under src/renderer/components/ and src/web/ to identify hardcoded user-facing strings not yet wrapped with t(), <T>, or tNotify(). Supports --json and --summary-only output modes. Adds npm script "i18n:audit" and 35 passing tests covering the core detection and skip-list logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ct contexts Adds typed SHORTCUT_LABELS key constants mapping all 74 shortcut IDs to translation keys, enabling constants files to reference i18n keys without React hooks. Registers 'shortcuts' namespace in i18n config/types and creates en/shortcuts.json with all shortcut labels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Installs tailwindcss-rtl as a dev dependency and configures it in tailwind.config.mjs. This provides directional utility classes (ms-*, me-*, ps-*, pe-*, start-*, end-*, text-start, text-end) that automatically flip in RTL mode, replacing fixed LTR classes like ml-*, mr-*, pl-*, pr-*. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Centralizes direction (LTR/RTL) logic into a single component that sets dir, data-dir, lang attributes and --dir-start/--dir-end CSS custom properties on document.documentElement based on the active language. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… logical CSS properties Convert directional Tailwind classes to RTL-aware logical properties: - ml-*/mr-* → ms-*/me-* (margin-inline-start/end) - border-l/border-r → border-s/border-e (border-inline-start/end) - left-*/right-* → start-*/end-* (inset-inline-start/end) - text-left → text-start - rounded-bl → rounded-es (end-start corner) - borderLeft (inline) → borderInlineStart Updated test selectors to match new class names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add rtlIcons.ts with a curated set of Lucide icons that need horizontal mirroring in RTL mode (arrows, chevrons, external links, etc.), a global CSS rule ([dir="rtl"] .rtl-flip), and a DirIcon wrapper component that automatically applies the flip class when the current language is RTL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ows, and translateX Add a documented [dir="rtl"] section in index.css with contributor guidelines covering scrollbar positioning (browser-automatic), border-inline-start/end preference, box-shadow mirroring patterns, and translateX sign inversion. Concrete overrides: - --rtl-sign custom property (1 for LTR, -1 for RTL) for component translateX - Shimmer animation direction reversal in RTL - Progress bar stripe angle mirror (-45deg in RTL) 9 tests verify CSS structure and rule correctness. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace tab labels, modal aria-label, LLM connection test messages, and error strings with t() calls using the 'settings' namespace. Add tabs, modal, and llm sections to en/settings.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 20+ hardcoded user-facing strings in EncoreTab with t() calls
using the 'settings' namespace. Strings extracted include: section header,
description, Director's Notes title/badge/description, provider selection
labels, agent detection states, customize button, configuration header,
lookback period labels and help text.
All new keys added to en/settings.json under encore.director_notes.*
with interpolation support for dynamic values ({{name}}, {{days}}).
Added react-i18next mock to EncoreTab test using actual English locale
for translation resolution. All 56 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…components to i18n Extract 100+ user-facing strings from 8 settings-adjacent components: - Settings/EnvVarsEditor.tsx (labels, placeholders, validation messages) - Settings/IgnorePatternsSection.tsx (labels, errors, button text) - Settings/SshRemoteIgnoreSection.tsx (title, description) - Settings/SshRemotesSection.tsx (section labels, badges, button titles) - Settings/SshRemoteModal.tsx (form labels, validation, status messages) - shared/AgentConfigPanel.tsx (path/args labels, env var section) - shared/AgentSelector.tsx (status badges, empty state) - shared/SshRemoteSelector.tsx (labels, status indicators) All keys added to en/settings.json under env_editor.*, ignore_patterns.*, ssh_remote_ignore.*, ssh_remotes.*, ssh_modal.*, agent_config.*, agent_selector.*, and ssh_selector.* namespaces. Added react-i18next mock to EnvVarsEditor and AgentConfigPanel test files. All 433 settings-related tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…to i18n
Replaced 15 menu item labels and descriptions with t() calls using the
'menus' namespace (hamburger.*). Added useTranslation('menus') hook and
all corresponding keys to en/menus.json.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…i18n Extracted 90+ command labels, subtexts, placeholders, toast messages, and UI text from the command palette to the menus.commands namespace. Handles interpolation for dynamic values (agent names, branch names, file paths, counts) and pluralization for participants/tasks. Search filtering continues to work on translated text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… i18n Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…onListItem.tsx to i18n Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add getShortcutLabel() utility in shortcutFormatter.ts that resolves shortcut labels through the i18n shortcuts namespace via SHORTCUT_LABELS mapping, with fallback to hardcoded English labels. Updated three consumer components to use translated labels: - ShortcutsHelpModal: label display, search filtering, sorting - ShortcutsTab: label display and filtering - ShortcutEditor: label display and "Press keys..." string Also extracted 8 hardcoded strings from ShortcutsHelpModal to modals.json (title, search placeholder, mastery progress text, etc.). Updated 13 test files to include getShortcutLabel in shortcutFormatter mocks, and added react-i18next mocks to ShortcutsHelpModal and ShortcutEditor tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tioning (I18N-14) Create src/web/utils/rtlCoordinates.ts with directional helpers: - getDirectionalDelta: normalizes swipe deltas for LTR/RTL - getDirectionalTranslateX: direction-aware CSS translateX - getDirectionalOffsetLeft: inline-start offset calculation - getLogicalSide: maps logical start/end to physical left/right - useDirection hook: reads document.documentElement.dir reactively Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ures (I18N-14) Swap swipe direction callbacks so "backward" swipe reveals delete in both LTR (swipe-left) and RTL (swipe-right). Use getDirectionalTranslateX() for transforms, getLogicalSide() for delete button positioning, and convert drawer container left/right to insetInlineStart/End. Make chevron hint and swipe hint text direction-aware. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ioning (I18N-14) Use getDirectionalDelta() for swipe navigation so forward/backward gestures map correctly in both LTR and RTL layouts. Convert fixed left/right positioning to logical insetInlineStart/End properties. Pinch-to-zoom remains direction-agnostic as the gesture is symmetric. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ioning (I18N-14) Replace offsetLeft-based scroll calculations with getBoundingClientRect()-relative deltas for direction-agnostic active pill centering. Group header scroll uses dir-aware edge alignment. Convert paddingLeft/paddingRight to logical properties. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…itioning (I18N-14) - Swap onSwipeLeft/onSwipeRight callbacks in RTL so swipe direction matches reading direction for entry navigation - Use getDirectionalTranslateX() for content transform during swipe - Convert swipe hint overlay positioning from left/right to insetInlineStart/insetInlineEnd with direction-aware chevrons - Use logicalOffsetX for threshold checks so hints appear on correct side - Convert fixed-position containers to logical properties Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mponents (I18N-14) Convert hardcoded left/right CSS properties to logical properties (insetInlineStart/insetInlineEnd) in CommandInputBar, SlashCommandAutocomplete, and ConnectionStatusIndicator. QuickActionsMenu uses useDirection() hook and getLogicalSide() utility for computed menu positioning with RTL-aware offset calculation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ateX patterns (I18N-14) Created shared ToggleSwitch component using CSS logical property inset-inline-start for knob positioning, which auto-mirrors in RTL without manual direction detection. Replaced inline toggle patterns in EncoreTab, SshRemoteModal, and DurationTrendsChart with the shared component. Supports sm/md/lg size presets, custom colors, and interactive/visual-only rendering modes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…l properties for RTL support (I18N-14) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…itioning (I18N-14) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d formatSize (I18N-15) Remove local formatBytes() reimplementation and use the locale-aware formatSize() from shared/formatters.ts. Update test expectations to match the shared formatter's consistent 1-decimal-place output. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tting with getActiveLocale() (I18N-15) Audited and fixed all toLocaleDateString, toLocaleTimeString, and toLocaleString calls across 25 renderer files that used [], undefined, or no locale argument. All now pass getActiveLocale() from shared/formatters.ts so date/time formatting respects the user's selected language. Added i18n-locale-audit.test.ts guardrail test (10 tests) that scans the renderer source for any remaining bad locale patterns and verifies document.documentElement.lang is set by the i18n system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create src/main/i18n.ts with a dedicated i18next instance for translating main-process user-facing strings (dialog titles, notifications, error messages). Only loads common and notifications namespaces. Resources are statically imported for all 9 languages to ensure reliable path resolution in both dev and packaged builds. - initMainI18n() reads stored language from settings store at startup - changeMainLanguage() called from locale:set IPC for runtime switching - mainT() wraps i18n.t() for main-process translation calls - 20 new tests covering init, switching, interpolation, and scripts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Exclude .claude/ from Prettier checks (gitignored but was scanned) - Add --ignore-unknown to lint-staged prettier command to skip files without a known parser (e.g., .prettierignore itself) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hardcoded English dialog title strings in main process IPC handlers with mainT() calls for locale-aware OS-native dialogs: - system.ts: Select Working Directory, Save File, Select Settings Folder - debug.ts: Save Debug Package - playbooks.ts: Export Playbook, Import Playbook Added dialog.* keys to common.json for all 9 supported languages. Added 5 tests verifying dialog key resolution across all locales. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dialog title translation changes require the mainT() function to be mocked in handler tests that assert on dialog options. Added i18n mocks returning English translations to maintain existing assertions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 30+ hardcoded English error strings in messageHandlers.ts (WebSocket web server) and 5 error messages in context-groomer.ts with mainT() i18n calls using notifications:error.web.* and notifications:error.grooming.* keys. Add translated error keys to all 9 locale notification files. Update test mocks to provide English translations for mainT() so existing assertions pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract hardcoded English strings from getUserFriendlyErrorMessage() and ErrorDisplay component in WizardConversationView.tsx to i18n keys using i18n.t() direct import (not useTranslation hook, since the error mapping function is a plain function outside React component scope). Added common:wizard_errors.* keys covering 7 error type mappings (timeout, not_available, session_error, spawn_failed, agent_error, parse_error, generic) plus UI strings (try_again, dismiss, technical_details). Translations added for all 9 supported languages. Added 13 new tests covering error display rendering, error type mapping, button interactions, and technical details. All 75 WizardConversationView tests + 383 i18n suite tests pass. tsc clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add centralized getStatusLabel() utility in theme.tsx that maps SessionState to translated short labels via common:status.label.* keys. Replace hardcoded English status strings in 4 components: SessionTooltipContent, ParticipantCard, SendToAgentModal, and SessionPillBar. Add status.label keys (idle/busy/waiting_input/ connecting/error/exited) to all 9 locale common.json files. Add 6 getStatusLabel unit tests to theme.test.tsx. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap t() in QuickActionsModal to build a translated→English label map during rendering. When the UI is non-English, the search filter matches against both the translated label and the English fallback, so users can find commands by name in either language. Search placeholder was already extracted to i18n keys. Added 7 tests with full Spanish locale mock. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added _i18n_note field to manifest.json explaining that PWA manifest strings are intentionally kept in English due to inconsistent browser support for manifest localization. Points to scripts/generate-manifests.ts as the path forward if locale-specific manifests are needed later. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…(I18N-15) Add Exclusions section to i18n CONVENTIONS.md explaining that src/prompts/*.md files remain in English (AI-facing, not user-facing). Add translated help text to Settings AI Commands tab across all 9 locales. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `scripts/i18n-size-check.ts` enforcing per-file (200 KB) and total (3 MB) size budgets on the 63 translation JSON files. Verifies lazy loading: English bundled statically, other languages code-split into separate locale-* chunks. Supports --json output for machine consumption. - Added `i18n:size-check` npm script - Added size check step to CI lint-and-format job - 9 tests covering file existence, budget enforcement, lazy loading config verification, and build output chunk detection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…8n calls (I18N-15) Replaced 3 hardcoded English error messages in notifications.ts (queue full, not supported, no active process) with mainT() calls using notifications:error.notification.* keys. Added error.notification.* keys to all 9 locale notifications.json files. Added mainT mock to notification handler tests and 4 new i18n tests for notification error translations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s, fix GroupChatRightPanel i18n (I18N-15) - Extract hardcoded strings from 13 components to i18n (GroupChatHeader, HistoryPanel, SymphonyModal, ShortcutsHelpModal, UpdateCheckModal, UsageDashboard charts, EmojiPickerField, Modal, NodeBreadcrumb) - Fix GroupChatRightPanel tabs showing raw string capitalization instead of translated text — now uses t() calls for tab labels, tooltips, and empty state text - Add group_chat_right_panel i18n keys to all 9 locale files - Add complete Chinese (zh) translations for accessibility, menus, and shortcuts namespaces - Add i18n-aware number/date formatters in shared/formatters.ts - Fix QueuedItemsList text overflow beyond container boundary - Clean up unused imports across 15+ modal/component files - Update tests for formatter i18n support and context menu positioning Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nslations for all 9 languages (I18N-16) - Replace 18 hardcoded English strings in EmptyStateView.tsx with tMenus() calls using existing hamburger.* keys - Replace 7 hardcoded strings in QueuedItemsList.tsx (queued separator, show less/all, images attached, remove confirmation modal) - Replace "Completed in" string in Toast.tsx with t() call - Add queued_items.* and toast.* keys to common.json (all 9 locales) - Add hamburger.menu and empty_state.* keys to menus.json (all 9 locales) - First-pass translations for es, fr, de, zh, hi, ar, bn, pt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 299 files, which is 149 over the limit of 150. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (299)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.Add a .trivyignore file to your project to customize which findings Trivy reports. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Complete internationalization (i18n) system enabling Maestro to be used in 9 languages: English, Spanish, French, German, Chinese, Hindi, Arabic, Bengali, and Portuguese. This is a foundational infrastructure change that touches every user-facing component.
Architecture Decisions
Why i18next + react-i18next: Industry standard with TypeScript support, pluralization via Unicode CLDR, namespace splitting for code organization, and built-in lazy loading. The React bindings provide hooks (
useTranslation) that trigger re-renders only when the active language changes.Why lazy-load non-English languages: English is bundled statically (~184KB) as the always-available fallback. All other languages are lazy-loaded via Vite dynamic imports on first use, producing per-language chunks (~20-40KB each). This adds zero bundle size for English-only users.
Why 7 namespaces instead of 1 monolith: Translation keys are split across
common,settings,modals,menus,notifications,accessibility, andshortcuts. This enables granular loading (only load what's needed), clear ownership boundaries for contributors, and manageable file sizes (~50-100 keys each vs ~1000 in a single file).Why system prompts stay English: AI agent system prompts are English-only by design. LLMs perform best with English instructions regardless of the user's display language. Documented in
ARCHITECTURE.md.What's Included
Infrastructure (I18N-01 through I18N-08)
useI18n()hook with namespace autocompletiontNotify()helper for i18n-aware toast notifications<T>convenience component for inline translations<CodeText>component for WCAG 2.1 SC 3.1.2langattribute supportmainT()for main-process translations (Electron dialogs, IPC errors)formatRelativeTime(),formatElapsedTime(),formatCost(),formatSize(),formatTokens()— all usingIntl.NumberFormat/Intl.RelativeTimeFormat'en-US'replaced withgetActiveLocale()across 21 filesString Extraction (I18N-09 through I18N-16)
mainT()(Electron dialogs, IPC error messages)RTL Support (I18N-13 through I18N-14)
DirectionProvidercomponent managingdir="rtl"on document rootleft/right/margin-leftetc. across SessionList, MainPanel, RightPaneltailwindcss-rtlplugin for RTL-aware utility classesToggleSwitchcomponent replacing inlinetranslateXpatternsTranslations
Testing & Validation
npm run i18n:validate— checks missing keys, orphaned keys, interpolation variable mismatches, plural form completeness across all localesDocumentation
src/shared/i18n/ADDING_LANGUAGES.md— 10-step contributor guide for adding new languagessrc/shared/i18n/CONVENTIONS.md— key naming rules and patternsKey Design Patterns
Cascading fallback: Missing key in selected language → English fallback (automatic, zero-config). Users never see raw i18n keys.
Namespace-scoped hooks: Components import only the namespace they need:
const { t } = useI18n('settings'). Thecommonnamespace is the default.Interpolation: Dynamic values use
{{variable}}syntax —t('tasks_completed', { completed: 5, total: 10 })→ "5 of 10 tasks completed".Pluralization: Uses i18next's CLDR-based plural rules. English uses
_one/_other. Arabic uses all 6 forms (_zerothrough_other). Japanese uses only_other(no singular/plural distinction).Future Work
title=""attributes — Tooltip-level strings across renderer components still use hardcoded English. These are lower priority (tooltips, not primary UI) but should be converted in a follow-up passADDING_LANGUAGES.mdguide makes this a straightforward contributionTest plan
npm run lintpasses (all 3 tsconfig targets)npm run i18n:validatepasses🤖 Generated with Claude Code