Internasionalisasi (I18n)
Workflow I18n
Lokalkan reply bot dengan deteksi locale, dictionary, dan helper terdekat dengan handler.
Deteksi locale
Ambil bahasa dari user, chat, atau konfigurasi middleware.
Helper terjemahan
Gunakan `ctx.i18n` dari handler setelah middleware terpasang.
Fallback
Sediakan default locale agar translation yang hilang tidak merusak reply.
Middleware I18n bawaan untuk respons bot multibahasa dengan deteksi lokal otomatis.
Memulai Cepat
typescript
import { Bot, I18n } from 'vibegram';
const bot = new Bot(process.env.BOT_TOKEN!);
const i18n = new I18n('id'); // lokal default
// Muat kamus/dictionary lokal
i18n.loadLocale('en', {
selamat_datang: 'Welcome {name}!',
bantuan: 'Available commands: /start, /help',
selamat_tinggal: 'Goodbye!',
});
i18n.loadLocale('id', {
selamat_datang: 'Selamat datang {name}!',
bantuan: 'Perintah tersedia: /start, /bantuan',
selamat_tinggal: 'Sampai jumpa!',
});
i18n.loadLocale('ar', {
selamat_datang: 'مرحباً {name}!',
bantuan: 'الأوامر المتاحة: /start, /help',
selamat_tinggal: 'وداعاً!',
});
bot.use(i18n.middleware());Menggunakan Terjemahan
typescript
bot.command('start', async ctx => {
const teks = ctx.i18n!.t('selamat_datang', {
name: ctx.from?.first_name || 'Teman',
});
await ctx.reply(teks);
});
bot.command('bantuan', async ctx => {
await ctx.reply(ctx.i18n!.t('bantuan'));
});Variabel Template
Gunakan placeholder {variable}:
typescript
i18n.loadLocale('id', {
pesanan_dikonfirmasi: 'Pesanan #{id} dikonfirmasi. Total: Rp{jumlah}.',
sambutan_vip: 'Halo {nama}! Kamu adalah anggota {level}.',
});
ctx.i18n!.t('pesanan_dikonfirmasi', { id: '1234', jumlah: '99.000' });
// → "Pesanan #1234 dikonfirmasi. Total: Rp99.000."Deteksi Bahasa Otomatis
Middleware secara otomatis membaca ctx.from?.language_code dari pengaturan klien Telegram. Jika lokal belum dimuat, akan fallback ke default:
Pengguna dengan Telegram en-US → menggunakan lokal 'en'
Pengguna dengan Telegram id → menggunakan lokal 'id'
Lokal tidak ada → menggunakan lokal defaultMuat dari File JSON
typescript
import { readFileSync } from 'fs';
const loadLocaleFile = (lang: string) =>
JSON.parse(readFileSync(`./locales/${lang}.json`, 'utf-8'));
i18n.loadLocale('en', loadLocaleFile('en'));
i18n.loadLocale('id', loadLocaleFile('id'));
i18n.loadLocale('ar', loadLocaleFile('ar'));Contoh locales/id.json:
json
{
"selamat_datang": "Selamat datang {name}!",
"pilih_menu": "Silakan pilih menu:",
"error_umum": "Terjadi kesalahan. Coba lagi nanti."
}Override Bahasa Manual
Izinkan pengguna memilih bahasa secara manual:
typescript
// Simpan pilihan bahasa di session
bot.use(session({ initial: () => ({ lang: 'id' }) }));
bot.use(async (ctx, next) => {
// Override lokal berdasarkan session pengguna
if (ctx.session?.lang) {
ctx.i18n?.setLocale(ctx.session.lang);
}
await next();
});
bot.command('bahasa', async ctx => {
await ctx.reply('Pilih bahasa:', {
reply_markup: Markup.inlineKeyboard([
[
Markup.button.callback('🇮🇩 Indonesia', 'lang_id'),
Markup.button.callback('🇬🇧 English', 'lang_en'),
],
]),
});
});
bot.action(/^lang_(\w+)$/, async ctx => {
const lang = ctx.match![1];
ctx.session.lang = lang;
ctx.i18n?.setLocale(lang);
await ctx.answerCbQuery('✅ Bahasa diperbarui');
await ctx.reply(ctx.i18n!.t('selamat_datang', { name: ctx.from?.first_name }));
});