feat(bot): add i18n support with grammy-i18n package

Add internationalization support to the bot using grammy-i18n package.
Includes English and Russian locale files and updates to session handling.
Disabled db:generate in Dockerfile as it's not needed during build.
This commit is contained in:
Yuu Ottosoka
2025-07-12 16:37:09 +03:00
parent 701e9c0ef8
commit df8bfa049f
8 changed files with 104 additions and 42 deletions

View File

@@ -18,6 +18,7 @@
"@repo/config": "*",
"@repo/logger": "*",
"@repo/typescript-config": "*",
"@repo/db": "*",
"dotenv": "^17.1.0",
"hono": "^4.8.4",
"hono-openapi": "^0.4.8",

1
apps/bot/locales/en.ftl Normal file
View File

@@ -0,0 +1 @@
start = Hello, how can I help you

1
apps/bot/locales/ru.ftl Normal file
View File

@@ -0,0 +1 @@
start = Привет, как я могу тебе помочь

View File

@@ -11,11 +11,12 @@
"clean": "rm -rf dist"
},
"dependencies": {
"@grammyjs/i18n": "^1.1.2",
"@hono/node-server": "^1.15.0",
"@repo/config": "*",
"@repo/db": "*",
"@repo/typescript-config": "*",
"@repo/logger": "*",
"@repo/typescript-config": "*",
"dotenv": "^17.1.0",
"grammy": "^1.37.0",
"hono": "^4.8.4",

View File

@@ -7,6 +7,7 @@ import { PrismaStorageAdapter } from './session-storage';
import { BotContext } from './types';
import { upsertUser } from './middlewares/upsert-user';
import { onError } from './middlewares/on-error';
import { I18n } from '@grammyjs/i18n';
const bot = new Bot<BotContext>(config.BOT_TOKEN);
const storage = new PrismaStorageAdapter();
@@ -15,9 +16,20 @@ bot.use(onError);
bot.use(session({ storage, initial: () => ({}) }));
bot.use(upsertUser);
const i18n = new I18n<BotContext>({
defaultLocale: 'ru',
useSession: true, // whether to store user language in session
directory: 'locales', // Load all translation files from locales/.
localeNegotiator: async (ctx) => {
const session = await ctx.session;
return session.__language_code ?? ctx.from?.language_code ?? 'ru';
},
});
bot.use(i18n);
bot.command('start', async (ctx) => {
const session = await ctx.session;
await ctx.reply('Привет, ' + JSON.stringify(session));
await ctx.reply(ctx.t('start'));
});
if (config.MODE === 'webhook') {

View File

@@ -1,5 +1,5 @@
import { prisma } from "@repo/db";
import { StorageAdapter } from "grammy";
import { prisma } from '@repo/db';
import { StorageAdapter } from 'grammy';
export class PrismaStorageAdapter<T extends object>
implements StorageAdapter<T>
@@ -14,7 +14,6 @@ export class PrismaStorageAdapter<T extends object>
.then((session) => (session?.data as T) || undefined);
}
async write(key: string, data: T): Promise<void> {
console.log("write", data);
await prisma.telegramSession.upsert({
where: {
id: key,

View File

@@ -1,5 +1,6 @@
import { Context, LazySessionFlavor } from "grammy";
import { I18nFlavor } from '@grammyjs/i18n';
import { Context, LazySessionFlavor } from 'grammy';
// config your session data
export type SessionData = {};
export type BotContext = Context & LazySessionFlavor<SessionData>;
export type SessionData = { __language_code?: string };
export type BotContext = Context & LazySessionFlavor<SessionData> & I18nFlavor;