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.
This commit is contained in:
parent
eee65070a8
commit
aae86a4001
16 changed files with 947 additions and 64 deletions
104
ui-electrobun/CONTRIBUTING_I18N.md
Normal file
104
ui-electrobun/CONTRIBUTING_I18N.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# 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
|
||||
```json
|
||||
"common.save": "Save"
|
||||
```
|
||||
|
||||
### Plurals (English: one/other)
|
||||
```json
|
||||
"search.resultsCount": "{count, plural, one {{count} result} other {{count} results}}"
|
||||
```
|
||||
|
||||
### Plurals (Polish: one/few/many/other)
|
||||
```json
|
||||
"search.resultsCount": "{count, plural, one {{count} wynik} few {{count} wyniki} many {{count} wynikow} other {{count} wynikow}}"
|
||||
```
|
||||
|
||||
### Plurals (Arabic: zero/one/two/few/many/other)
|
||||
```json
|
||||
"tasks.taskCount": "{count, plural, =0 {no tasks} one {one task} two {two tasks} few {{count} tasks} many {{count} tasks} other {{count} tasks}}"
|
||||
```
|
||||
|
||||
### Interpolation
|
||||
```json
|
||||
"errors.unhandled": "Unhandled error: {message}"
|
||||
```
|
||||
|
||||
### Select
|
||||
```json
|
||||
"agent.status": "{status, select, running {Running} idle {Idle} other {Unknown}}"
|
||||
```
|
||||
|
||||
## Using Translations in Components
|
||||
|
||||
```svelte
|
||||
<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`:
|
||||
|
||||
```bash
|
||||
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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue