feat: add i18n localization system (en/es/de) with admin panel

- Add i18n module with tForUser/tForLang/t functions and {{param}} interpolation
- Add 3 locale files: en.json, es.json, de.json (201 keys each)
- Add language selection on /start and /language command with flag emojis
- Localize all bot user-facing strings (handlers, keyboards, errors)
- Localize messageRouter keyboard matching via locale keys
- Add DB migrations 008 (language column) and 009 (language_set column)
- Add localization admin tab at /locales for editing translations
- Add userService.getUserLanguage/setUserLanguage methods
- Cache user object on msg.__user to avoid triple DB fetch
- Idempotent migrations with checkColumnExists guards
- Error boundary on i18n locale file loading
- Admin locales route uses AVAILABLE_LANGUAGES import
This commit is contained in:
NW
2026-06-25 21:22:32 +01:00
parent 41ff2b8769
commit a8bf50df24
29 changed files with 1606 additions and 365 deletions

View File

@@ -1,13 +1,14 @@
// userService.js
import db from "../config/database.js";
import config from '../config/config.js';
import Wallet from "../models/Wallet.js";
import WalletUtils from "../utils/walletUtils.js";
import logger from "../utils/logger.js";
const ALLOWED_USER_FIELDS = new Set([
'telegram_id', 'username', 'country', 'city',
'district', 'status', 'total_balance', 'bonus_balance'
'district', 'status', 'total_balance', 'bonus_balance', 'language', 'language_set'
]);
@@ -215,6 +216,19 @@ class UserService {
throw error;
}
}
static async getUserLanguage(telegramId) {
const user = await this.getUserByTelegramId(telegramId);
return user?.language || 'en';
}
static async setUserLanguage(telegramId, lang) {
const normalizedTelegramId = this.normalizeTelegramId(telegramId);
await db.runAsync(
'UPDATE users SET language = ?, language_set = 1 WHERE telegram_id = ?',
[lang, normalizedTelegramId]
);
}
}
export default UserService;