Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ coverage/
main.py
pyproject.toml
uv.lock

# i18n
i18n.lock
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ Species art lives in `server/art.ts` and `statusline/buddy-status.sh`. Each spec
### New Reactions
Reaction templates are in `server/reactions.ts`. Species-specific reactions go in `SPECIES_REACTIONS`, general ones in `REACTIONS`.

### Translations
Locale files live in `locales/<code>.json` (e.g. `locales/en.json`, `locales/es.json`). To add a new language:

1. Copy `locales/en.json` to `locales/<code>.json`
2. Set `"_language"` to the language name (e.g. `"Español"`)
3. Translate the string values (keep `{variable}` placeholders and `*asterisk actions*` intact)
4. Run `bun test` to validate the locale structure

The i18n module (`server/i18n.ts`) auto-discovers locale files by scanning `locales/*.json`. No code changes needed — just drop in the file.

## DCO (Developer Certificate of Origin)

Every commit to this repo must be **signed off** with the Developer
Expand Down
1 change: 1 addition & 0 deletions cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ In Claude Code:
/buddy position Show or set bubble position (tmux only)
/buddy rarity Show or hide rarity stars (tmux only)
/buddy width Set bubble text width in chars (10-60, tmux only)
/buddy language Show or set buddy language (e.g. 'en', 'es', 'ja')

Usage:
bun run <command> e.g. bun run show, bun run doctor
Expand Down
24 changes: 23 additions & 1 deletion cli/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import { loadConfig, saveConfig } from "../server/state.ts";
import { AVAILABLE_LOCALES } from "../server/i18n.ts";

const args = process.argv.slice(2);
const key = args[0];
Expand All @@ -20,9 +21,11 @@ if (!key) {
─────────────────────
Comment cooldown: ${cfg.commentCooldown}s (0 = no throttling, default 30)
Reaction TTL: ${cfg.reactionTTL}s (0 = permanent, default 0)
Language: ${cfg.language} (${AVAILABLE_LOCALES[cfg.language] ?? cfg.language})

Change: bun run settings cooldown <seconds>
bun run settings ttl <seconds>
bun run settings language <code>
`);
process.exit(0);
}
Expand Down Expand Up @@ -63,6 +66,25 @@ if (key === "ttl") {
process.exit(0);
}

if (key === "language") {
if (value === undefined) {
const cfg = loadConfig();
console.log(`Language: ${cfg.language} (${AVAILABLE_LOCALES[cfg.language] ?? cfg.language})`);
console.log("Available:", Object.entries(AVAILABLE_LOCALES).map(([c, n]) => `${c} (${n})`).join(", "));
process.exit(0);
}

if (!AVAILABLE_LOCALES[value]) {
console.error(`Error: unknown language "${value}"`);
console.error("Available:", Object.keys(AVAILABLE_LOCALES).join(", "));
process.exit(1);
}

const cfg = saveConfig({ language: value });
console.log(`Updated: language → ${cfg.language} (${AVAILABLE_LOCALES[cfg.language]})`);
process.exit(0);
}

console.error(`Unknown setting: ${key}`);
console.error("Available: cooldown, ttl");
console.error("Available: cooldown, ttl, language");
process.exit(1);
18 changes: 18 additions & 0 deletions i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.10",
"locale": {
"source": "en",
"targets": ["zh", "es", "ja", "de", "fr", "pt", "ko", "ru", "ro", "uk", "tr", "hi", "it", "pl", "vi", "ar", "th"]
},
"buckets": {
"json": {
"include": ["locales/[locale].json"]
}
},
"provider": {
"id": "openrouter",
"model": "anthropic/claude-opus-4.6",
"prompt": "You are translating a JSON locale file for claude-buddy — a tamagotchi-style coding companion that lives in a developer's terminal. Preserve: (1) roleplay asterisk actions like *purrs*, *knocks error off table* (translate the action verb, keep the asterisks), (2) the playful, snarky, affectionate tone, (3) all {variable} placeholders EXACTLY as-is, (4) all emoji, (5) JSON structure (keys, nesting, array lengths) EXACTLY as-is. Translate from {SOURCE_LOCALE} to {TARGET_LOCALE}. Sound natural to a native developer — use casual/colloquial register, not formal textbook language."
}
}
Loading