Skip to main content

Internationalization (i18n)

Bunny UI copy for English and French lives in a single catalog shared by the agent server, Discord bridge, and Web UI.

Catalog

Example:

{
"discord.thread.linked": {
"en": "_Thread linked to shell `{shell}`_",
"fr": "_Thread lié au shell `{shell}`_"
}
}

Rust (bunny-i18n)

Crate: crates/bunny-i18n

use bunny_i18n::{Locale, t};

let msg = t(Locale::Fr, "configure.owner_created", &[]);
  • Locale::En / Locale::Fr
  • parse_locale("fr") / is_valid_locale_code
  • Unit test: every key must have both en and fr

Consumers: apps/server, apps/discord-bridge.

TypeScript (Web UI)

import { t } from '../i18n';

t('fr', 'web.home.language_label');

Web UI merges both JSON files in apps/web/src/i18n.ts. Use useT() in components for translated copy.

User preference storage

  • SQLite: users.preferred_locale (en default, fr allowed)
  • Set during first bunny configure (prompt at start of flow)
  • Web: PATCH /api/v1/auth/me with { "locale": "fr" }
  • Discord: /bunny language locale:fr (requires Discord account linked to a Bunny user)
  • Visible in /bunny status JSON as locale (+ locale_source)

PTY LANG / LC_ALL (UTF-8) is separate from UI locale.

Adding a string

  1. Add a key under the right prefix (configure.*, discord.*, web.*, api.error.*) in messages.json with both locales.
  2. Run cargo test -p bunny-i18n (parity test).
  3. Use t(locale, "your.key", &[("var", "value")]) in Rust or t(locale, 'your.key', { var: 'value' }) in TS.
  4. Optional: ./scripts/check-i18n.sh (validates JSON parity).

Discord OAuth (Web UI)

RoutePurpose
GET /api/v1/auth/discord/startRedirect to Discord OAuth (session cookie required)
GET /api/v1/auth/discord/callbackOAuth callback → redirect /?discord_link=success|conflict|error
DELETE /api/v1/auth/discord/linkUnlink Discord account from current Bunny user

GET /auth/me includes a discord object: oauth_configured, linked, discord_user_id, username.

Internal Discord API (bridge)

RoutePurpose
GET /api/v1/internal/discord/statusLink status + locale
GET /api/v1/internal/discord/user-localeLocale without requiring channel link
POST /api/v1/internal/discord/localeSet locale for linked Discord user (/bunny language)