i18n & Localization
Build a multilingual bot using Kythia's built-in internationalization system — locale files, variable interpolation, and AI-powered auto-translation.
Kythia uses a file-based i18n system managed by the TranslatorManager. Every string shown to users should go through the translation function t() so that your bot can serve communities in their native language.
How It Works
The TranslatorManager loads locale files in this priority order during boot:
Priority (highest to lowest)
─────────────────────────────
1. Addon lang files addons/*/lang/*.json
2. User lang files {appRoot}/lang/*.json
3. Core lang files kythia-core/src/lang/*.json (built-in)
Higher-priority files override lower-priority ones by key. This lets your addons override core strings without touching the core package.
Locale File Format
Place locale files at addons/{your-addon}/lang/{locale}.json:
// addons/leveling/lang/en.json
{
"leveling": {
"profile": {
"title": "Profile — {{username}}",
"level": "Level {{level}}",
"xp": "{{current}} / {{required}} XP"
},
"set": {
"success": "Set {{username}}'s level to {{level}}."
}
}
}
addon.command.key) to prevent collisions between addons. Using t() in Commands
The translation function is injected into every command via the DI container:
async execute(interaction, container) {
const { t } = container;
// Simple key lookup
const title = await t(interaction, 'leveling.profile.title', {
username: interaction.user.username,
});
await interaction.reply(title);
}
The interaction parameter provides the user's locale (set in Discord's user settings). Kythia automatically falls back to en.json if the user's language isn't available.
Variable Interpolation
Pass a second object argument with your variables. Use {{variableName}} in your JSON strings:
await t(interaction, 'leveling.set.success', {
username: 'Alice',
level: 25,
});
// → "Set Alice's level to 25."
Adding a New Language
npx kythia lang:translate --target id # Indonesian
npx kythia lang:translate --target ja # Japanese
npx kythia lang:translate --target zh # Chinese
This uses Google Gemini to translate your en.json and saves the result as {locale}.json in the same directory.
npx kythia lang:sync
After adding new keys to en.json, run lang:sync to add [placeholder] stubs for all other locale files so nothing is missing.
Checking for Missing Keys
npx kythia lang:check
Uses Babel AST parsing to scan all .js/.ts files for t('key') calls and compares them against your locale JSON. Reports:
- ❌ Missing keys — your code calls a key that doesn't exist in
en.json - ⚠️ Unused keys — a key exists in
en.jsonbut is never called in code
lang:check to your Husky pre-commit hook so missing translations can never slip into a commit.Supported Locales
Kythia maps Discord's user locale codes to your locale files. Common codes:
| Discord Locale | File name | Language |
|---|---|---|
en-US / en-GB |
en.json |
English |
id |
id.json |
Indonesian |
ja |
ja.json |
Japanese |
zh-CN |
zh.json |
Chinese (Simplified) |
ko |
ko.json |
Korean |
de |
de.json |
German |
fr |
fr.json |
French |
pt-BR |
pt-BR.json |
Portuguese (Brazil) |
en.json.