- i18n.svelte.ts: store with $state locale + createIntl(), t() function, formatDate/Number/RelativeTime, getDir() for RTL, async setLocale() - i18n.types.ts: TranslationKey union (codegen from en.json) - locales/en.json: 200+ strings in ICU MessageFormat - locales/pl.json: full Polish translation - locales/ar.json: partial Arabic (validates 6-form plural + RTL) - scripts/i18n-types.ts: codegen script for type-safe keys - 6 components wired: StatusBar, AgentPane, CommandPalette, SettingsDrawer, SplashScreen, ChatInput - Language selector in AppearanceSettings - App.svelte: document.dir reactive for RTL - CONTRIBUTING_I18N.md: guide for adding languages Note: currently Electrobun-only. Will extract to @agor/i18n shared package for both Tauri and Electrobun.
3 KiB
3 KiB
i18n Contributing Guide
This project uses ICU MessageFormat via @formatjs/intl with a custom Svelte 5 store.
Adding a New Language
- Create
locales/<tag>.json(e.g.locales/de.json) with all keys fromlocales/en.json. - Register the locale in
src/mainview/i18n.svelte.ts:- Add an entry to
AVAILABLE_LOCALESwith tag, label, nativeLabel, and dir. - Add a dynamic import entry to the
loadersmap.
- Add an entry to
- The language will automatically appear in Settings > Appearance > Language.
Key Naming Convention
Use dot-notation: component.element.state
sidebar.settings -- component.element
agent.status.running -- component.element.state
settings.appearance -- component.element
common.cancel -- shared across components
errors.connectionFailed -- error messages
Group prefixes:
sidebar.*-- left sidebaragent.*-- agent paneterminal.*-- terminal tabssettings.*-- settings drawerstatusbar.*-- bottom status barnotifications.*-- notification drawerfiles.*-- file browsersearch.*-- search overlaycomms.*-- communications tabtasks.*-- task boarderrors.*-- error messagessplash.*-- splash screencommon.*-- shared labels (cancel, save, delete)project.*-- project managementpalette.*-- command palette
ICU MessageFormat Syntax
Plain text
"common.save": "Save"
Plurals (English: one/other)
"search.resultsCount": "{count, plural, one {{count} result} other {{count} results}}"
Plurals (Polish: one/few/many/other)
"search.resultsCount": "{count, plural, one {{count} wynik} few {{count} wyniki} many {{count} wynikow} other {{count} wynikow}}"
Plurals (Arabic: zero/one/two/few/many/other)
"tasks.taskCount": "{count, plural, =0 {no tasks} one {one task} two {two tasks} few {{count} tasks} many {{count} tasks} other {{count} tasks}}"
Interpolation
"errors.unhandled": "Unhandled error: {message}"
Select
"agent.status": "{status, select, running {Running} idle {Idle} other {Unknown}}"
Using Translations in Components
<script lang="ts">
import { t } from './i18n.svelte.ts';
</script>
<button>{t('common.save')}</button>
<span>{t('sidebar.notifCount', { count: 5 })}</span>
Testing Translations
- Switch language in Settings > Appearance > Language.
- All
t()calls update reactively -- no page reload needed. - Missing keys fall back to the key name itself (e.g.
"agent.status.running"). - Check the browser console for
[i18n]warnings about missing or malformed messages.
Regenerating Types
After adding or removing keys in locales/en.json:
bun scripts/i18n-types.ts
This regenerates src/mainview/i18n.types.ts with the updated TranslationKey union type. TypeScript will then flag any t() calls using keys that no longer exist.
RTL Support
Arabic (ar) sets document.dir = 'rtl' automatically. CSS should use logical properties (inline-start/inline-end) instead of left/right where possible.