agent-orchestrator/ui-electrobun/CONTRIBUTING_I18N.md
Hibryda aae86a4001 feat(electrobun): i18n system — @formatjs/intl + Svelte 5 runes + 3 locales
- 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.
2026-03-22 10:28:13 +01:00

3 KiB

i18n Contributing Guide

This project uses ICU MessageFormat via @formatjs/intl with a custom Svelte 5 store.

Adding a New Language

  1. Create locales/<tag>.json (e.g. locales/de.json) with all keys from locales/en.json.
  2. Register the locale in src/mainview/i18n.svelte.ts:
    • Add an entry to AVAILABLE_LOCALES with tag, label, nativeLabel, and dir.
    • Add a dynamic import entry to the loaders map.
  3. 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 sidebar
  • agent.* -- agent pane
  • terminal.* -- terminal tabs
  • settings.* -- settings drawer
  • statusbar.* -- bottom status bar
  • notifications.* -- notification drawer
  • files.* -- file browser
  • search.* -- search overlay
  • comms.* -- communications tab
  • tasks.* -- task board
  • errors.* -- error messages
  • splash.* -- splash screen
  • common.* -- shared labels (cancel, save, delete)
  • project.* -- project management
  • palette.* -- 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

  1. Switch language in Settings > Appearance > Language.
  2. All t() calls update reactively -- no page reload needed.
  3. Missing keys fall back to the key name itself (e.g. "agent.status.running").
  4. 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.