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:
@@ -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
1
apps/bot/locales/en.ftl
Normal file
@@ -0,0 +1 @@
|
||||
start = Hello, how can I help you
|
||||
1
apps/bot/locales/ru.ftl
Normal file
1
apps/bot/locales/ru.ftl
Normal file
@@ -0,0 +1 @@
|
||||
start = Привет, как я могу тебе помочь
|
||||
@@ -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",
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
112
package-lock.json
generated
112
package-lock.json
generated
@@ -149,6 +149,7 @@
|
||||
"apps/bot": {
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@grammyjs/i18n": "^1.1.2",
|
||||
"@hono/node-server": "^1.15.0",
|
||||
"@repo/config": "*",
|
||||
"@repo/db": "*",
|
||||
@@ -224,6 +225,46 @@
|
||||
"zod": "^3.20.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@deno/shim-deno": {
|
||||
"version": "0.18.2",
|
||||
"resolved": "https://registry.npmjs.org/@deno/shim-deno/-/shim-deno-0.18.2.tgz",
|
||||
"integrity": "sha512-oQ0CVmOio63wlhwQF75zA4ioolPvOwAoK0yuzcS5bDC1JUvH3y1GS8xPh8EOpcoDQRU4FTG8OQfxhpR+c6DrzA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deno/shim-deno-test": "^0.5.0",
|
||||
"which": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@deno/shim-deno-test": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@deno/shim-deno-test/-/shim-deno-test-0.5.0.tgz",
|
||||
"integrity": "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@deno/shim-deno/node_modules/isexe": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
|
||||
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@deno/shim-deno/node_modules/which": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
|
||||
"integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/which.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz",
|
||||
@@ -833,6 +874,43 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fluent/bundle": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@fluent/bundle/-/bundle-0.17.1.tgz",
|
||||
"integrity": "sha512-CRFNT9QcSFAeFDneTF59eyv3JXFGhIIN4boUO2y22YmsuuKLyDk+N1I/NQUYz9Ab63e6V7T6vItoZIG/2oOOuw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fluent/langneg": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@fluent/langneg/-/langneg-0.6.2.tgz",
|
||||
"integrity": "sha512-YF4gZ4sLYRQfctpUR2uhb5UyPUYY5n/bi3OaED/Q4awKjPjlaF8tInO3uja7pnLQcmLTURkZL7L9zxv2Z5NDwg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@grammyjs/i18n": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@grammyjs/i18n/-/i18n-1.1.2.tgz",
|
||||
"integrity": "sha512-PcK06mxuDDZjxdZ5HywBhr+erEITsR816KP4DNIDDds1jpA45pfz/nS9FdZmzF8H6lMyPix3mV5WL1rT4q+BuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deno/shim-deno": "~0.18.0",
|
||||
"@fluent/bundle": "^0.17.1",
|
||||
"@fluent/langneg": "^0.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"grammy": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@grammyjs/types": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.21.0.tgz",
|
||||
@@ -2523,12 +2601,6 @@
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/code-block-writer": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
|
||||
"integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -4375,12 +4447,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -6545,25 +6611,6 @@
|
||||
"zod": "^3.21.4"
|
||||
}
|
||||
},
|
||||
"node_modules/zod-prisma-types": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/zod-prisma-types/-/zod-prisma-types-3.2.4.tgz",
|
||||
"integrity": "sha512-S4spVBMJAmecLv+aLyRhXK26qW9nWwcsOf1H1fRcEmiI8DPbftZ99u0fhqHlymuTpmMcUpuKxcNNOIqNY0ScSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@prisma/generator-helper": "^6.3.0",
|
||||
"code-block-writer": "^12.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"bin": {
|
||||
"zod-prisma-types": "dist/bin.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@prisma/client": "^4.x.x || ^5.x.x || ^6.x.x",
|
||||
"prisma": "^4.x.x || ^5.x.x || ^6.x.x"
|
||||
}
|
||||
},
|
||||
"packages/config": {
|
||||
"name": "@repo/config",
|
||||
"version": "0.0.0",
|
||||
@@ -6583,8 +6630,7 @@
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.11.1",
|
||||
"@repo/exceptions": "*",
|
||||
"prisma-json-types-generator": "^3.5.1",
|
||||
"zod-prisma-types": "^3.2.4"
|
||||
"prisma-json-types-generator": "^3.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@repo/eslint-config": "*",
|
||||
|
||||
Reference in New Issue
Block a user