Built-In Locales (i18n)
If you are expanding globally or catering to multilingual communities, you can leverage Disapp's deeply integrated i18n (Internationalization) mechanism to dynamically deploy responses depending on the application language registered by the Discord user.
How it Works
Disapp Core natively features an I18n class. Utilizing local directories, it evaluates files ending in .json and streams their payload immediately into cached Memory (RAM), yielding lightning-fast language swaps.
Setup
1. Create Locale Files
Create a locales folder in your bot project:
locales/
├── en.json
├── tr.json
├── de.json
└── fr.json
2. Define Translations
locales/en.json
{
"commands": {
"ping": {
"description": "Bot latency",
"response": "🏓 Pong! {latency}ms"
}
},
"buttons": {
"confirm": "Confirm",
"cancel": "Cancel"
},
"errors": {
"noPermission": "❌ You don't have permission!"
}
}
locales/tr.json
{
"commands": {
"ping": {
"description": "Bot gecikmesi",
"response": "🏓 Pong! {latency}ms"
}
},
"buttons": {
"confirm": "Onayla",
"cancel": "İptal"
},
"errors": {
"noPermission": "❌ Yetkiniz yok!"
}
}
3. Initialize i18n
import { I18n } from "@disapp/core";
import path from "path";
const i18n = I18n.getInstance({
defaultLanguage: "en",
fallbackLanguage: "en",
localesPath: path.join(__dirname, "../locales"),
});
await i18n.loadLocales(path.join(__dirname, "../locales"));
4. Use in Commands
import { Command, I18nHelper } from "@disapp/core";
import { SlashCommandBuilder } from "discord.js";
export default class PingCommand extends Command {
async execute(interaction: any) {
const lang = interaction.locale?.split("-")[0] || "en";
const latency = Math.round(interaction.client.ws.ping);
const message = I18nHelper.command("ping.response", lang, { latency });
await interaction.reply(message);
}
}
I18nHelper Methods
t() - Generic Translation
Works with ANY key structure:
I18nHelper.t("commands.ping.response", "en", { latency: 50 });
I18nHelper.t("ticket.created", "en");
I18nHelper.t("custom.nested.key", "en", { param: "value" });
tUser() - User-Specific Translation
Translate using the user's preferred language:
I18nHelper.setUserLanguage("user123", "tr");
const message = I18nHelper.tUser("user123", "commands.ping.response", {
latency: 50,
});
setUserLanguage() - Set User Language
I18nHelper.setUserLanguage("user123", "tr");
I18nHelper.setUserLanguage("user456", "de");
getUserLanguage() - Get User Language
const userLang = I18nHelper.getUserLanguage("user123");
console.log(userLang); // 'tr'
Shortcut Methods
I18nHelper.button("confirm", "en");
I18nHelper.modal("settings", "en");
I18nHelper.message("welcome", "en");
I18nHelper.error("noPermission", "en");
I18nHelper.command("ping.response", "en", { latency: 50 });
I18nHelper.embed("welcome", "en");
Usage Examples
Basic Command with User Language
import { Command, I18nHelper } from "@disapp/core";
import { SlashCommandBuilder } from "discord.js";
export default class PingCommand extends Command {
async execute(interaction: any) {
const userId = interaction.user.id;
const latency = Math.round(interaction.client.ws.ping);
const message = I18nHelper.tUser(userId, "commands.ping.response", {
latency: latency.toString(),
});
await interaction.reply(message);
}
}
Language Selection Command
import { Command, I18nHelper } from "@disapp/core";
import { SlashCommandBuilder } from "discord.js";
export default class LanguageCommand extends Command {
async execute(interaction: any) {
const userId = interaction.user.id;
const language = interaction.options.getString("language");
I18nHelper.setUserLanguage(userId, language);
const message = I18nHelper.tUser(userId, "commands.language.success");
await interaction.reply(message);
}
}
Components V2 with i18n
import { Command, v2, I18nHelper } from "@disapp/core";
import { SlashCommandBuilder, ButtonStyle } from "discord.js";
export default class MenuCommand extends Command {
async execute(interaction: any) {
const lang = interaction.locale?.split("-")[0] || "en";
const t = (key: string, params?: Record<string, any>) =>
I18nHelper.t(key, lang, params);
const message = v2()
.text(`# ${t("menu.title")}`)
.separator()
.text(t("menu.description"))
.buttons(
{
id: "confirm",
label: I18nHelper.button("confirm", lang),
style: ButtonStyle.Success,
onClick: async (i) => {
await i.reply(t("menu.confirmed"));
},
},
{
id: "cancel",
label: I18nHelper.button("cancel", lang),
style: ButtonStyle.Danger,
onClick: async (i) => {
await i.reply(t("menu.cancelled"));
},
},
)
.build();
await interaction.reply(message);
}
}
Translation File Structure
You can organize translations however you want:
{
"commands": {
"ping": { "response": "..." }
},
"buttons": { "confirm": "..." },
"messages": { "welcome": "..." },
"errors": { "noPermission": "..." },
"ticket": {
"created": "🎫 Ticket created!",
"closed": "🎫 Ticket closed!"
}
}
Parameter Substitution
Use {paramName} syntax for dynamic values:
{
"commands": {
"ban": {
"success": "✅ **{user}** has been banned.",
"reason": "📝 Reason: {reason}"
}
}
}
const message = I18nHelper.command("ban.success", "en", {
user: "John",
reason: "Spam",
});
Automatic Fallback
If a user happens to interact with the bot using German (de) and you primarily facilitate tr and en, the execution chain won't shatter; it seamlessly routes backward toward your target defaultLanguage designation.
Best Practices
- Organize by category - Use nested objects
- Use consistent naming - Follow a naming convention
- Provide fallback language - Always have English as fallback
- Use parameters - Avoid hardcoding values
- Keep translations updated - Maintain all language files together
- Test all languages - Verify translations work correctly
Learn More
- API Reference - Complete i18n API
- Examples - i18n usage examples
- Advanced Features - Advanced i18n patterns