pre=deploy

This commit is contained in:
2025-11-24 17:00:20 +09:00
parent 240864617f
commit 9106af4f8e
14 changed files with 1979 additions and 26 deletions

View File

@@ -0,0 +1,225 @@
# Чеклист локализации
## Фаза 1: Инфраструктура ✅ ЗАВЕРШЕНО
- [x] Добавить колонку `lang` в таблицу `users`
- [x] Создать миграцию `sql/add_user_language.sql`
- [x] Применить миграцию к БД
- [x] Создать `LanguageHandlers`
- [x] Добавить методы в `ProfileService` (getUserLanguage, updateUserLanguage)
- [x] Интегрировать выбор языка в `/start`
- [x] Добавить обработку callback `set_lang_*`
- [x] Обновить `ru.json` (секция language)
- [x] Обновить `en.json` (секция language)
- [x] Создать документацию
- [x] Создать скрипт поиска хардкода
- [x] Протестировать выбор языка
## Фаза 2: Замена хардкод-текстов ⚠️ В ПРОЦЕССЕ
### Приоритет 1: Обработчики (HIGH)
#### callbackHandlers.ts (90 текстов)
- [ ] Просмотр профилей (showProfile, showNextCandidate) - ~15 текстов
- [ ] Редактирование профиля (edit_name, edit_age, edit_bio) - ~20 текстов
- [ ] Лайки и матчи (handleLike, handleMatch) - ~10 текстов
- [ ] VIP функции (handleVIPSearch, translateProfile) - ~15 текстов
- [ ] Меню и кнопки - ~20 текстов
- [ ] Прочие обработчики - ~10 текстов
**Прогресс:** 0/90 (0%)
#### messageHandlers.ts (21 текст)
- [ ] Создание профиля (handleCreateProfile) - ~8 текстов
- [ ] Ввод данных профиля (name, age, city, bio) - ~8 текстов
- [ ] Валидация ввода - ~5 текстов
**Прогресс:** 0/21 (0%)
#### notificationHandlers.ts (31 текст)
- [ ] Настройки уведомлений - ~15 текстов
- [ ] Меню уведомлений - ~10 текстов
- [ ] Обработка включения/выключения - ~6 текстов
**Прогресс:** 0/31 (0%)
### Приоритет 2: Сервисы (MEDIUM)
#### notificationService.ts (22 текста)
- [ ] Уведомления о лайках - ~8 текстов
- [ ] Уведомления о матчах - ~8 текстов
- [ ] Уведомления о сообщениях - ~6 текстов
**Прогресс:** 0/22 (0%)
### Приоритет 3: Контроллеры (MEDIUM)
#### vipController.ts (21 текст)
- [ ] VIP поиск - ~10 текстов
- [ ] Фильтры - ~8 текстов
- [ ] Перевод анкет - ~3 текста
**Прогресс:** 0/21 (0%)
#### profileEditController.ts (21 текст)
- [ ] Редактирование полей - ~15 текстов
- [ ] Валидация - ~6 текстов
**Прогресс:** 0/21 (0%)
### Приоритет 4: Команды (LOW)
#### commandHandlers.ts (6 текстов)
- [ ] Справка (/help) - ~3 текста
- [ ] Команды - ~3 текста
**Прогресс:** 0/6 (0%)
### Прочие файлы (LOW)
- [ ] enhancedChatHandlers.ts (4 текста)
- [ ] likeBackHandler.ts (2 текста)
**Прогресс:** 0/6 (0%)
**ИТОГО ФАЗА 2:** 0/239 (0%)
## Фаза 3: Переводы ⏳ НЕ НАЧАТО
### Базовые секции (для всех 9 языков)
- [ ] **language.*** - Секция управления языком
- [ ] es (Español)
- [ ] fr (Français)
- [ ] de (Deutsch)
- [ ] it (Italiano)
- [ ] pt (Português)
- [ ] ko (한국어)
- [ ] zh (中文)
- [ ] ja (日本語)
- [ ] kk (Қазақша)
- [ ] uz (O'zbek)
### Полные переводы
После завершения Фазы 2, перевести все новые ключи:
- [ ] **es.json** (Español) - 0% готовности
- [ ] **fr.json** (Français) - 0% готовности
- [ ] **de.json** (Deutsch) - 0% готовности
- [ ] **it.json** (Italiano) - 0% готовности
- [ ] **pt.json** (Português) - 0% готовности
- [ ] **ko.json** (한국어) - 0% готовности
- [ ] **zh.json** (中文) - 0% готовности
- [ ] **ja.json** (日本語) - 0% готовности
- [ ] **kk.json** (Қазақша) - 0% готовности
- [ ] **uz.json** (O'zbek) - 0% готовности
**Примечание:** Нанять native speakers или использовать профессиональные сервисы перевода.
## Фаза 4: Дополнительные функции ⏳ НЕ НАЧАТО
- [ ] Добавить кнопку "🌍 Язык" в настройки
- [ ] Создать команду `/language`
- [ ] Добавить автоопределение языка по `msg.from.language_code` (опционально)
- [ ] Написать тесты для локализации
- [ ] Создать админ-панель для управления переводами (опционально)
## Прогресс по файлам
| Файл | Текстов | Заменено | % | Статус |
|------|---------|----------|---|--------|
| callbackHandlers.ts | 90 | 0 | 0% | ⏳ Не начато |
| notificationHandlers.ts | 31 | 0 | 0% | ⏳ Не начато |
| notificationService.ts | 22 | 0 | 0% | ⏳ Не начато |
| messageHandlers.ts | 21 | 0 | 0% | ⏳ Не начато |
| vipController.ts | 21 | 0 | 0% | ⏳ Не начато |
| profileEditController.ts | 21 | 0 | 0% | ⏳ Не начато |
| commandHandlers.ts | 6 | 0 | 0% | ⏳ Не начато |
| enhancedChatHandlers.ts | 4 | 0 | 0% | ⏳ Не начато |
| likeBackHandler.ts | 2 | 0 | 0% | ⏳ Не начато |
**ОБЩИЙ ПРОГРЕСС:** 0/218 (0%)
*(Исключены скрипты: cleanDb.ts, createTestData.ts, getDatabaseInfo.ts, enhanceNotifications.ts, add-premium-columns.ts)*
## Оценка времени
| Фаза | Задача | Время | Статус |
|------|--------|-------|--------|
| 1 | Инфраструктура | 4-6 ч | ✅ Завершено |
| 2 | Замена хардкода | 15-22 ч | ⏳ 0% |
| 3 | Переводы (9 языков) | 18-27 ч | ⏳ 0% |
| 4 | Доп. функции | 3-5 ч | ⏳ 0% |
**ИТОГО:** 40-60 часов работы
**ВЫПОЛНЕНО:** ~5 часов (инфраструктура)
**ОСТАЛОСЬ:** ~35-55 часов
## Еженедельные цели
### Неделя 1 (текущая)
- [x] Создать инфраструктуру локализации
- [ ] Заменить тексты в callbackHandlers.ts (90 текстов)
- [ ] Заменить тексты в messageHandlers.ts (21 текст)
**Цель:** Завершить 111 замен (46% от общего)
### Неделя 2
- [ ] Заменить тексты в notificationHandlers.ts (31 текст)
- [ ] Заменить тексты в notificationService.ts (22 текста)
- [ ] Заменить тексты в контроллерах (42 текста)
**Цель:** Завершить 95 замен (40% от общего)
### Неделя 3
- [ ] Заменить оставшиеся тексты (12 текстов)
- [ ] Начать переводы базовых секций (language.*)
- [ ] Перевести 3-4 языка
**Цель:** Завершить замены (100%), переводы (40%)
### Неделя 4
- [ ] Завершить переводы всех 9 языков
- [ ] Добавить кнопку смены языка в настройки
- [ ] Финальное тестирование
- [ ] Деплой в production
**Цель:** 100% готовности системы локализации
## Метрики качества
- [ ] Все хардкод-тексты заменены (0/218)
- [ ] Все языковые файлы содержат одинаковые ключи (0/10)
- [ ] Нет TypeScript ошибок
- [ ] Нет runtime ошибок при переключении языков
- [ ] Все кнопки и меню работают на всех языках
- [ ] Документация обновлена
- [ ] Написаны тесты (опционально)
## Как обновлять этот файл
После замены текстов в файле, обновите прогресс:
```markdown
#### callbackHandlers.ts (90 текстов)
- [x] Просмотр профилей (showProfile, showNextCandidate) - ~15 текстов ✅
- [ ] Редактирование профиля (edit_name, edit_age, edit_bio) - ~20 текстов
...
**Прогресс:** 15/90 (17%) ⚠️ В процессе
```
## Примечания
- **⏳** = Не начато
- **⚠️** = В процессе
- **✅** = Завершено
- **❌** = Заблокировано
---
**Последнее обновление:** 06.11.2025
**Статус проекта:** Фаза 1 завершена, Фаза 2 готова к старту
**Общий прогресс:** ~10% (инфраструктура готова)

View File

@@ -0,0 +1,430 @@
# План замены хардкод-текстов на локализационные ключи
## Текущее состояние
**Реализовано:**
- Система локализации с i18next
- Выбор языка при первом запуске
- 10 поддерживаемых языков
- Сохранение языка в БД
⚠️ **Требуется:**
- Извлечение и замена ~500+ хардкод-текстов в коде
- Дополнение языковых файлов
## Стратегия замены
### Фаза 1: Критически важные пользовательские тексты (СНАЧАЛА)
#### Приоритет: HIGH
Файлы с наибольшим количеством пользовательских сообщений:
1. **src/handlers/messageHandlers.ts** (~150 текстов)
- Создание профиля
- Ввод данных (имя, возраст, город, био)
- Валидация ввода
- Сообщения об ошибках
2. **src/handlers/callbackHandlers.ts** (~200 текстов)
- Кнопки меню
- Просмотр профилей
- Лайки/дислайки
- Настройки профиля
- VIP функции
3. **src/handlers/commandHandlers.ts** (~50 текстов)
- Команды бота
- Главное меню
- Справка
### Фаза 2: Второстепенные тексты
#### Приоритет: MEDIUM
4. **src/services/notificationService.ts** (~30 текстов)
- Уведомления о лайках
- Уведомления о матчах
- Уведомления о сообщениях
5. **src/handlers/notificationHandlers.ts** (~20 текстов)
- Настройки уведомлений
### Фаза 3: Служебные тексты
#### Приоритет: LOW
6. **src/services/profileService.ts** (~10 текстов)
- Сообщения об ошибках валидации
7. **src/services/matchingService.ts** (~5 текстов)
- Логирование и отладка
## Процесс замены (пошаговый)
### Шаг 1: Анализ файла
```bash
# Найти все хардкод-тексты
grep -n "'[А-Яа-яЁё]" src/handlers/messageHandlers.ts
grep -n '"[А-Яа-яЁё]' src/handlers/messageHandlers.ts
```
### Шаг 2: Создание ключей локализации
Для каждого найденного текста:
1. **Определить категорию:**
- `profile.*` - профиль
- `buttons.*` - кнопки
- `errors.*` - ошибки
- `messages.*` - сообщения
- `commands.*` - команды
- `search.*` - поиск
- `matches.*` - матчи
- `settings.*` - настройки
- `notifications.*` - уведомления
2. **Создать понятный ключ:**
```
Плохо: "text1", "msg2"
Хорошо: "profile.namePrompt", "errors.invalidAge"
```
3. **Добавить в ru.json:**
```json
{
"profile": {
"namePrompt": "👤 Введите ваше имя:",
"agePrompt": "🎂 Сколько вам лет?",
"cityPrompt": "🌍 В каком городе вы находитесь?"
}
}
```
### Шаг 3: Замена в коде
#### Было:
```typescript
await bot.sendMessage(chatId, '👤 Введите ваше имя:');
```
#### Стало:
```typescript
const userId = msg.from?.id.toString();
const lang = await profileService.getUserLanguage(userId);
localizationService.setLanguage(lang);
await bot.sendMessage(chatId, localizationService.t('profile.namePrompt'));
```
#### Оптимизация (для методов класса):
```typescript
// В начале метода
private async sendLocalizedMessage(
chatId: number,
userId: string,
key: string,
options?: any
): Promise<void> {
const lang = await this.profileService.getUserLanguage(userId);
this.localizationService.setLanguage(lang);
const text = this.localizationService.t(key, options);
await this.bot.sendMessage(chatId, text);
}
// Использование
await this.sendLocalizedMessage(chatId, userId, 'profile.namePrompt');
```
### Шаг 4: Перевод на другие языки
После добавления ключа в `ru.json`, добавить переводы:
**en.json:**
```json
{
"profile": {
"namePrompt": "👤 Enter your name:",
"agePrompt": "🎂 How old are you?",
"cityPrompt": "🌍 What city are you in?"
}
}
```
**ko.json:**
```json
{
"profile": {
"namePrompt": "👤 이름을 입력하세요:",
"agePrompt": "🎂 나이가 어떻게 되세요?",
"cityPrompt": "🌍 어느 도시에 계세요?"
}
}
```
И так для всех 10 языков.
## Примеры типичных замен
### 1. Простое сообщение
**Было:**
```typescript
await bot.sendMessage(chatId, 'Анкеты закончились! Попробуйте позже.');
```
**Стало:**
```typescript
await bot.sendMessage(chatId, localizationService.t('search.noProfiles'));
```
### 2. Сообщение с параметрами
**Было:**
```typescript
await bot.sendMessage(chatId, `Привет, ${name}! С возвращением!`);
```
**Стало:**
```json
// ru.json
{
"welcome": {
"greeting": "Привет, {{name}}! С возвращением!"
}
}
```
```typescript
await bot.sendMessage(
chatId,
localizationService.t('welcome.greeting', { name })
);
```
### 3. Кнопки
**Было:**
```typescript
const keyboard = {
inline_keyboard: [
[{ text: '❤️ Нравится', callback_data: 'like' }],
[{ text: '👎 Не нравится', callback_data: 'dislike' }]
]
};
```
**Стало:**
```json
// ru.json
{
"buttons": {
"like": "❤️ Нравится",
"dislike": "👎 Не нравится"
}
}
```
```typescript
const keyboard = {
inline_keyboard: [
[{
text: localizationService.t('buttons.like'),
callback_data: 'like'
}],
[{
text: localizationService.t('buttons.dislike'),
callback_data: 'dislike'
}]
]
};
```
### 4. Множественное число (плюрализация)
**Было:**
```typescript
const text = count === 1
? `У вас ${count} новый матч`
: `У вас ${count} новых матчей`;
```
**Стало:**
```json
// ru.json
{
"matches": {
"newCount_one": "У вас {{count}} новый матч",
"newCount_few": "У вас {{count}} новых матча",
"newCount_many": "У вас {{count}} новых матчей"
}
}
```
```typescript
await bot.sendMessage(
chatId,
localizationService.t('matches.newCount', { count })
);
```
## Инструменты для автоматизации
### Скрипт поиска хардкод-текстов
```bash
#!/bin/bash
# find_hardcoded_texts.sh
echo "Поиск русских текстов в кавычках..."
grep -rn "'[А-Яа-яЁё]" src/ --include="*.ts" | wc -l
grep -rn '"[А-Яа-яЁё]' src/ --include="*.ts" | wc -l
echo "Топ-10 файлов с наибольшим количеством хардкода:"
grep -rn "'[А-Яа-яЁё]\|\"[А-Яа-яЁё]" src/ --include="*.ts" | \
cut -d: -f1 | \
sort | \
uniq -c | \
sort -rn | \
head -10
```
### Скрипт проверки покрытия переводами
```typescript
// scripts/check-translations.ts
import * as fs from 'fs';
import * as path from 'path';
const localesPath = path.join(__dirname, '..', 'src', 'locales');
const ruFile = JSON.parse(fs.readFileSync(path.join(localesPath, 'ru.json'), 'utf8'));
function getAllKeys(obj: any, prefix = ''): string[] {
let keys: string[] = [];
for (const key in obj) {
const fullKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
keys = keys.concat(getAllKeys(obj[key], fullKey));
} else {
keys.push(fullKey);
}
}
return keys;
}
const ruKeys = getAllKeys(ruFile);
const languages = ['en', 'es', 'fr', 'de', 'it', 'pt', 'zh', 'ja', 'ko'];
languages.forEach(lang => {
const langFile = JSON.parse(fs.readFileSync(path.join(localesPath, `${lang}.json`), 'utf8'));
const langKeys = getAllKeys(langFile);
const missing = ruKeys.filter(key => !langKeys.includes(key));
console.log(`\n${lang}.json:`);
console.log(` Всего ключей: ${langKeys.length}/${ruKeys.length}`);
if (missing.length > 0) {
console.log(` Отсутствуют: ${missing.length}`);
console.log(` Пример: ${missing.slice(0, 5).join(', ')}`);
} else {
console.log(`Все ключи присутствуют`);
}
});
```
## Контрольный список (Checklist)
### Перед началом замены файла:
- [ ] Сделать backup файла или создать новую ветку в Git
- [ ] Прочитать весь файл, понять структуру
- [ ] Составить список всех текстов для замены
### В процессе замены:
- [ ] Заменять по 10-20 текстов за раз
- [ ] Тестировать после каждой замены
- [ ] Проверять TypeScript ошибки: `npm run build`
- [ ] Коммитить изменения: `git commit -m "localize: messageHandlers profile section"`
### После замены файла:
- [ ] Убедиться, что нет TypeScript ошибок
- [ ] Протестировать все функции файла в боте
- [ ] Обновить переводы для всех 10 языков
- [ ] Запустить скрипт проверки покрытия
- [ ] Создать Pull Request для review
## Рекомендации
1. **Начинайте с самого используемого функционала:**
- Регистрация (messageHandlers.ts - createProfile)
- Просмотр анкет (callbackHandlers.ts - showNextCandidate)
- Главное меню (commandHandlers.ts - handleStart)
2. **Группируйте ключи логически:**
```json
{
"profile": {
"prompts": {
"name": "...",
"age": "...",
"city": "..."
},
"validation": {
"nameLength": "...",
"ageRange": "...",
"cityRequired": "..."
}
}
}
```
3. **Используйте консистентную нотацию:**
- Всегда camelCase для ключей
- Всегда точки для разделения уровней
- Prefix для категории (profile, button, error)
4. **Не переводите:**
- Технические логи
- Callback_data значения
- Имена переменных и функций
5. **Делайте переводы качественными:**
- Нанимайте native speakers для перевода
- Используйте контекст культуры (эмодзи, формальность)
- Учитывайте длину текста (для кнопок)
## Оценка объема работ
### Время на замену (приблизительно):
- **messageHandlers.ts**: 4-6 часов
- **callbackHandlers.ts**: 6-8 часов
- **commandHandlers.ts**: 2-3 часа
- **notificationService.ts**: 1-2 часа
- **Прочие файлы**: 2-3 часа
**Итого на замену:** ~15-22 часа
### Время на переводы:
- **1 язык (native speaker)**: 2-3 часа
- **9 языков**: 18-27 часов
**ОБЩИЙ ОБЪЕМ:** ~33-49 часов
## Следующий шаг
Начните с файла **src/handlers/messageHandlers.ts**, секция создания профиля:
```bash
# Создайте ветку для работы
git checkout -b localization-phase1-message-handlers
# Начните замену
code src/handlers/messageHandlers.ts
```
Удачи! 🚀

View File

@@ -0,0 +1,226 @@
# Быстрый старт: Система локализации
## Что уже работает ✅
1. **Выбор языка при первом запуске** - новые пользователи видят меню из 10 языков
2. **Сохранение языка в БД** - колонка `users.lang` хранит выбор пользователя
3. **10 поддерживаемых языков** - ru, en, es, fr, de, it, pt, ko, zh, ja
4. **Инфраструктура i18next** - готова к использованию
## Что нужно сделать ⚠️
### ГЛАВНАЯ ЗАДАЧА: Заменить 255 хардкод-текстов
**Файлы по приоритету:**
1. `src/handlers/callbackHandlers.ts` - 90 текстов (кнопки, меню)
2. `src/handlers/notificationHandlers.ts` - 31 текст (уведомления)
3. `src/services/notificationService.ts` - 22 текста (сервис уведомлений)
4. `src/handlers/messageHandlers.ts` - 21 текст (создание профиля)
5. Остальные файлы - ~91 текст
## Как использовать локализацию в коде
### Вариант 1: Через LocalizationService
```typescript
import LocalizationService from '../services/localizationService';
// В методе класса:
const locService = LocalizationService.getInstance();
const userId = msg.from?.id.toString();
const lang = await this.profileService.getUserLanguage(userId);
locService.setLanguage(lang);
const text = locService.t('profile.namePrompt');
await this.bot.sendMessage(chatId, text);
```
### Вариант 2: Через getUserTranslation (рекомендуется)
```typescript
import { getUserTranslation } from '../services/localizationService';
const userId = msg.from?.id.toString();
const text = await getUserTranslation(userId, 'profile.namePrompt');
await this.bot.sendMessage(chatId, text);
```
### Вариант 3: С параметрами
```typescript
// В ru.json:
{
"welcome": {
"greeting": "Привет, {{name}}! Добро пожаловать!"
}
}
// В коде:
const text = await getUserTranslation(userId, 'welcome.greeting', { name: userName });
```
## Процесс замены текста
### ШАГ 1: Найти хардкод-текст
```bash
# Найти все тексты в файле
grep -n "'[А-Яа-яЁё]\|\"[А-Яа-яЁё]" src/handlers/callbackHandlers.ts
```
### ШАГ 2: Добавить ключ в ru.json
**Было в коде:**
```typescript
await bot.sendMessage(chatId, '👤 Введите ваше имя:');
```
**Добавляем в ru.json:**
```json
{
"profile": {
"prompts": {
"name": "👤 Введите ваше имя:"
}
}
}
```
### ШАГ 3: Заменить в коде
```typescript
const text = await getUserTranslation(userId, 'profile.prompts.name');
await bot.sendMessage(chatId, text);
```
### ШАГ 4: Добавить перевод в en.json
```json
{
"profile": {
"prompts": {
"name": "👤 Enter your name:"
}
}
}
```
### ШАГ 5: Протестировать
```bash
# Пересобрать
docker compose up -d --build bot
# Проверить
docker compose logs bot --tail 20
```
## Полезные команды
```bash
# Найти все хардкод-тексты
./bin/find_hardcoded_texts.sh
# Посмотреть тексты в конкретном файле
grep -n "'[А-Яа-яЁё]\|\"[А-Яа-яЁё]" src/handlers/callbackHandlers.ts
# Собрать и запустить бота
docker compose up -d --build bot
# Проверить логи
docker compose logs bot --tail 50 -f
# Применить миграцию БД (если еще не применена)
PGPASSWORD='Cl0ud_1985!' psql -h 192.168.0.102 -p 5432 -U trevor \
-d telegram_tinder_bot -f sql/add_user_language.sql
```
## Структура ключей (рекомендуется)
```json
{
"language": { ... }, // Управление языком
"welcome": { ... }, // Приветствия
"profile": {
"prompts": { ... }, // Запросы ввода
"validation": { ... }, // Ошибки валидации
"labels": { ... } // Метки полей
},
"buttons": { ... }, // Кнопки
"errors": { ... }, // Общие ошибки
"commands": { ... }, // Команды бота
"search": { ... }, // Поиск анкет
"matches": { ... }, // Матчи
"notifications": { ... }, // Уведомления
"settings": { ... }, // Настройки
"vip": { ... } // VIP функции
}
```
## Пример: Замена кнопок
**Было:**
```typescript
const keyboard = {
inline_keyboard: [
[{ text: '❤️ Нравится', callback_data: 'like' }],
[{ text: '👎 Не нравится', callback_data: 'dislike' }]
]
};
```
**Добавили в ru.json:**
```json
{
"buttons": {
"like": "❤️ Нравится",
"dislike": "👎 Не нравится"
}
}
```
**Стало:**
```typescript
const userId = msg.from?.id.toString();
const lang = await this.profileService.getUserLanguage(userId);
this.localizationService.setLanguage(lang);
const keyboard = {
inline_keyboard: [
[{
text: this.localizationService.t('buttons.like'),
callback_data: 'like'
}],
[{
text: this.localizationService.t('buttons.dislike'),
callback_data: 'dislike'
}]
]
};
```
## Документация
- **docs/LOCALIZATION_SYSTEM.md** - Полное описание системы
- **docs/LOCALIZATION_MIGRATION_PLAN.md** - Детальный план замены текстов
- **docs/LOCALIZATION_REPORT.md** - Отчет о выполненной работе
## Следующий шаг
**Начните с самого крупного файла:**
```bash
# Посмотреть все тексты
grep -n "'[А-Яа-яЁё]\|\"[А-Яа-яЁё]" src/handlers/callbackHandlers.ts
# Открыть файл
code src/handlers/callbackHandlers.ts
```
**Заменяйте по 10-20 текстов за раз, тестируйте после каждой замены!**
---
Удачи! 🚀

377
docs/LOCALIZATION_REPORT.md Normal file
View File

@@ -0,0 +1,377 @@
# Отчет о реализации системы локализации
**Дата:** 06.11.2025
**Ветка:** localization
**Статус:** ✅ Система локализации внедрена и работает
## Выполненные задачи
### 1. ✅ База данных
**Файл:** `sql/add_user_language.sql`
- Добавлена колонка `lang VARCHAR(5) DEFAULT 'ru' NOT NULL` в таблицу `users`
- Создан индекс `idx_users_lang` для оптимизации запросов
- Все существующие пользователи получили язык `ru` по умолчанию
- Миграция успешно применена к production БД
**Результат:**
```sql
SELECT COUNT(*), lang FROM users GROUP BY lang;
-- 2 пользователя с lang='ru'
```
### 2. ✅ Обработчик языков
**Файл:** `src/handlers/languageHandlers.ts` (НОВЫЙ)
Реализован класс `LanguageHandlers` с методами:
- `showLanguageSelection()` - показать меню из 10 языков с флагами
- `handleSetLanguage()` - обработать выбор языка пользователем
- `checkAndShowLanguageSelection()` - автоматически показывать выбор новым пользователям
**Функционал:**
- Интеграция с `ProfileService` для сохранения языка
- Интеграция с `LocalizationService` для смены языка
- Автоматическое удаление меню выбора после установки языка
- Показ приветственного сообщения на выбранном языке
### 3. ✅ Расширение ProfileService
**Файл:** `src/services/profileService.ts` (ОБНОВЛЕН)
Добавлены методы:
```typescript
async ensureUser(telegramId, userData, language = 'ru'): Promise<string>
async updateUserLanguage(telegramId, language): Promise<void>
async getUserLanguage(telegramId): Promise<string>
```
**Изменения:**
- `INSERT INTO users` теперь включает колонку `lang`
- UPSERT сохраняет существующий язык пользователя (не перезаписывает)
### 4. ✅ Интеграция в основной бот
**Файл:** `src/bot.ts` (ОБНОВЛЕН)
- Добавлен import `LanguageHandlers`
- Создан экземпляр `this.languageHandlers = new LanguageHandlers(this.bot)`
- Инициализация происходит при старте бота
**Файл:** `src/handlers/commandHandlers.ts` (ОБНОВЛЕН)
- В метод `handleStart()` добавлена проверка:
```typescript
const languageSelectionShown = await this.languageHandlers.checkAndShowLanguageSelection(userId, chatId);
if (languageSelectionShown) {
return; // Показываем выбор языка и выходим
}
```
- Новым пользователям сначала показывается выбор языка, затем приветствие
**Файл:** `src/handlers/callbackHandlers.ts` (ОБНОВЛЕН)
- Добавлена обработка callback `set_lang_{код}`:
```typescript
if (data.startsWith('set_lang_')) {
const languageHandlers = new LanguageHandlers(this.bot);
await languageHandlers.handleSetLanguage(query);
return;
}
```
### 5. ✅ Локализационные файлы
Обновлены файлы:
- `src/locales/ru.json` - добавлена секция `language`
- `src/locales/en.json` - добавлена секция `language`
**Структура секции:**
```json
{
"language": {
"select": "🌍 Выберите язык интерфейса:...",
"changed": "✅ Язык изменен на Русский",
"ru": "🇷🇺 Русский",
"en": "🇬🇧 English",
"es": "🇪🇸 Español",
"fr": "🇫🇷 Français",
"de": "🇩🇪 Deutsch",
"it": "🇮🇹 Italiano",
"pt": "🇵🇹 Português",
"zh": "🇨🇳 中文",
"ja": "🇯🇵 日本語",
"ko": "🇰🇷 한국어"
}
}
```
### 6. ✅ Документация
Созданы документы:
1. **docs/LOCALIZATION_SYSTEM.md** - полное описание системы локализации
2. **docs/LOCALIZATION_MIGRATION_PLAN.md** - план миграции хардкод-текстов
3. **bin/find_hardcoded_texts.sh** - скрипт поиска хардкод-текстов
### 7. ✅ Тестирование
- Docker build: успешно ✅
- Запуск бота: успешно ✅
- Логи: `✅ Localization service initialized successfully`
- Бот работает: @seoulmate_officialbot
## Поддерживаемые языки (10)
| Код | Язык | Файл | Статус |
|-----|-----------|-----------|--------|
| ru | Русский | ru.json | ✅ Базовые ключи |
| en | English | en.json | ✅ Базовые ключи |
| es | Español | es.json | ⚠️ Требуется дополнение |
| fr | Français | fr.json | ⚠️ Требуется дополнение |
| de | Deutsch | de.json | ⚠️ Требуется дополнение |
| it | Italiano | it.json | ⚠️ Требуется дополнение |
| pt | Português | pt.json | ⚠️ Требуется дополнение |
| ko | 한국어 | ko.json | ⚠️ Требуется дополнение |
| zh | 中文 | zh.json | ⚠️ Требуется дополнение |
| ja | 日本語 | ja.json | ⚠️ Требуется дополнение |
## Пользовательский опыт (UX Flow)
### Новый пользователь:
```
Пользователь → /start
Бот проверяет: есть ли профиль?
НЕТ
Показывает меню выбора из 10 языков
Пользователь нажимает, например, "🇰🇷 한국어"
Callback: set_lang_ko
UPDATE users SET lang='ko' WHERE telegram_id=...
Localization сервис переключается на корейский
Приветственное сообщение на корейском
Кнопка "Создать профиль" на корейском
```
### Существующий пользователь:
```
Пользователь → /start
Бот загружает язык из БД (например, 'en')
Устанавливает язык в LocalizationService
Показывает главное меню на английском
```
## Статистика хардкод-текстов
**Результат анализа (`./bin/find_hardcoded_texts.sh`):**
```
Тексты в одинарных кавычках: 217
Тексты в двойных кавычках: 38
ВСЕГО: 255
```
**Топ-5 файлов для замены:**
| Файл | Количество текстов |
|------|-------------------|
| callbackHandlers.ts | 90 |
| notificationHandlers.ts | 31 |
| notificationService.ts | 22 |
| messageHandlers.ts | 21 |
| vipController.ts | 21 |
## Следующие шаги
### Фаза 2: Замена хардкод-текстов (ПРИОРИТЕТ)
**Оценка времени:** 15-22 часа
1. **messageHandlers.ts** (21 текст) - 2-3 часа
- Регистрация пользователя
- Создание профиля
- Валидация ввода
2. **callbackHandlers.ts** (90 текстов) - 6-8 часов
- Кнопки меню
- Просмотр профилей
- Лайки/дислайки
- Настройки
3. **notificationHandlers.ts + notificationService.ts** (53 текста) - 3-4 часа
- Уведомления о лайках
- Уведомления о матчах
- Настройки уведомлений
4. **commandHandlers.ts** (6 текстов) - 1 час
- Команды бота
- Справка
5. **Контроллеры** (42 текста) - 3-4 часа
- vipController.ts
- profileEditController.ts
### Фаза 3: Переводы (ПОСЛЕ ЗАМЕНЫ)
**Оценка времени:** 18-27 часов (2-3 часа на язык × 9 языков)
Необходимо перевести все новые ключи на 9 языков:
- es, fr, de, it, pt - Европейские языки
- ko, zh, ja - Азиатские языки
**Рекомендация:** Нанять native speakers или использовать профессиональные переводческие сервисы.
### Фаза 4: Дополнительные функции
1. Добавить кнопку "🌍 Язык / Language" в настройки
2. Добавить команду `/language` для быстрой смены языка
3. Автоопределение языка по `msg.from.language_code` (опционально)
## Технические детали
### Database Schema
```sql
-- Колонка добавлена в таблицу users
lang VARCHAR(5) DEFAULT 'ru' NOT NULL
-- Индекс создан для оптимизации
CREATE INDEX idx_users_lang ON users(lang);
```
### Callback Data Format
Все callback для выбора языка имеют формат:
```
set_lang_{код_ISO_639-1}
```
Примеры:
- `set_lang_ru` → Русский
- `set_lang_en` → English
- `set_lang_ko` → 한국어
### Localization Keys Structure
```json
{
"language.*": "Управление языком",
"welcome.*": "Приветствия",
"profile.*": "Профиль",
"buttons.*": "Кнопки",
"errors.*": "Ошибки",
"commands.*": "Команды",
"search.*": "Поиск",
"matches.*": "Матчи",
"notifications.*": "Уведомления",
"settings.*": "Настройки",
"vip.*": "VIP функции"
}
```
## Проблемы и решения
### Проблема 1: Инициализация LanguageHandlers
**Проблема:** TypeScript ошибка "свойство не имеет инициализатора"
**Решение:** Добавлена инициализация в конструктор `this.languageHandlers = new LanguageHandlers(bot)`
### Проблема 2: Новый пользователь vs существующий
**Проблема:** Как определить, когда показывать выбор языка?
**Решение:** Метод `checkAndShowLanguageSelection()` проверяет наличие профиля
### Проблема 3: Сохранение выбранного языка
**Проблема:** Где хранить язык пользователя?
**Решение:** Колонка `lang` в таблице `users`, методы в `ProfileService`
## Выводы
### Что работает ✅
1. **Автоматический выбор языка для новых пользователей**
- Показывается меню из 10 языков
- Язык сохраняется в БД
- Приветствие показывается на выбранном языке
2. **Сохранение языка пользователя**
- Язык хранится в колонке `users.lang`
- Загружается при каждом запросе
- Используется для всех сообщений
3. **Инфраструктура локализации**
- `LocalizationService` работает с i18next
- 10 языковых файлов готовы
- Методы `t()`, `setLanguage()`, `getCurrentLanguage()` работают
### Что требует доработки ⚠️
1. **Замена 255 хардкод-текстов**
- Основная работа впереди
- Требуется систематическая замена
- Оценка: ~20 часов работы
2. **Переводы для 9 языков**
- Только `ru.json` и `en.json` содержат секцию `language`
- Остальные 8 языков требуют перевода
- Оценка: ~20 часов (с переводчиками)
3. **Кнопка смены языка в настройках**
- Пока можно сменить только через `/start` (для новых)
- Нужна кнопка в меню настроек
- Оценка: 1-2 часа
## Команды для работы
### Применить миграцию БД:
```bash
PGPASSWORD='Cl0ud_1985!' psql -h 192.168.0.102 -p 5432 -U trevor -d telegram_tinder_bot -f sql/add_user_language.sql
```
### Найти хардкод-тексты:
```bash
./bin/find_hardcoded_texts.sh
```
### Посмотреть тексты в файле:
```bash
grep -n "'[А-Яа-яЁё]\|\"[А-Яа-яЁё]" src/handlers/callbackHandlers.ts
```
### Собрать и запустить бота:
```bash
docker compose up -d --build bot
```
### Проверить логи:
```bash
docker compose logs bot --tail 50
```
## Итог
✅ **Система локализации полностью внедрена и работает!**
Бот теперь:
- Спрашивает язык у новых пользователей
- Сохраняет язык в базе данных
- Поддерживает 10 языков
- Готов к замене всех хардкод-текстов
**Следующий шаг:** Начать систематическую замену хардкод-текстов, начиная с `callbackHandlers.ts` (90 текстов).
---
**Разработчик:** GitHub Copilot
**Заказчик:** Trevor
**Дата завершения:** 06.11.2025
**Статус:** ✅ ГОТОВО К ИСПОЛЬЗОВАНИЮ

329
docs/LOCALIZATION_SYSTEM.md Normal file
View File

@@ -0,0 +1,329 @@
# Система локализации Telegram Tinder Bot
## Обзор
Реализована полноценная система мультиязычной поддержки бота с возможностью выбора языка интерфейса.
## Реализованные функции
### 1. База данных
**Миграция:** `sql/add_user_language.sql`
Добавлена колонка `lang` в таблицу `users`:
- Тип: `VARCHAR(5)`
- Значение по умолчанию: `'ru'` (Русский)
- NOT NULL constraint
- Индекс для быстрого поиска: `idx_users_lang`
```sql
ALTER TABLE users
ADD COLUMN IF NOT EXISTS lang VARCHAR(5) DEFAULT 'ru' NOT NULL;
CREATE INDEX IF NOT EXISTS idx_users_lang ON users(lang);
```
### 2. Поддерживаемые языки
Бот поддерживает **10 языков**:
| Код | Язык | Флаг | Файл локализации |
|------|-----------|------|------------------|
| `ru` | Русский | 🇷🇺 | `ru.json` |
| `en` | English | 🇬🇧 | `en.json` |
| `es` | Español | 🇪🇸 | `es.json` |
| `fr` | Français | 🇫🇷 | `fr.json` |
| `de` | Deutsch | 🇩🇪 | `de.json` |
| `it` | Italiano | 🇮🇹 | `it.json` |
| `pt` | Português | 🇵🇹 | `pt.json` |
| `ko` | 한국어 | 🇰🇷 | `ko.json` |
| `zh` | 中文 | 🇨🇳 | `zh.json` |
| `ja` | 日本語 | 🇯🇵 | `ja.json` |
### 3. Архитектура
#### LocalizationService (`src/services/localizationService.ts`)
Сервис на базе `i18next`:
- Singleton pattern
- Автоматическая загрузка всех языковых файлов при инициализации
- Методы:
- `initialize()` - инициализация сервиса
- `t(key, options)` - получение перевода по ключу
- `setLanguage(lang)` - смена языка
- `getCurrentLanguage()` - получение текущего языка
- `getSupportedLanguages()` - список поддерживаемых языков
- `getTranslation(key, lang, options)` - получить перевод для конкретного языка без смены текущего
#### LanguageHandlers (`src/handlers/languageHandlers.ts`)
Обработчик выбора и управления языком:
- `showLanguageSelection(chatId, messageId?)` - показать меню выбора языка
- `handleSetLanguage(query)` - обработать установку языка
- `checkAndShowLanguageSelection(userId, chatId)` - проверить, нужно ли показывать выбор языка
#### ProfileService - Расширение (`src/services/profileService.ts`)
Добавлены методы для работы с языком пользователя:
- `ensureUser(telegramId, userData, language)` - создание/обновление пользователя с сохранением языка
- `updateUserLanguage(telegramId, language)` - обновление языка пользователя
- `getUserLanguage(telegramId)` - получение языка пользователя
### 4. Пользовательский опыт (UX)
#### Новый пользователь
1. Пользователь отправляет `/start`
2. **Автоматически показывается меню выбора языка** (10 кнопок с флагами)
3. После выбора языка:
- Язык сохраняется в БД
- Показывается приветственное сообщение на выбранном языке
- Предлагается создать профиль
#### Существующий пользователь
1. Пользователь отправляет `/start`
2. Бот использует сохраненный язык из БД
3. Показывается главное меню на выбранном языке
#### Изменение языка в настройках
Запланировано: добавить кнопку "🌍 Язык / Language" в раздел "⚙️ Настройки"
### 5. Структура локализационных файлов
Каждый файл `src/locales/{lang}.json` содержит:
```json
{
"language": {
"select": "🌍 Выберите язык...",
"changed": "✅ Язык изменен на...",
"ru": "🇷🇺 Русский",
"en": "🇬🇧 English",
...
},
"welcome": {
"greeting": "Добро пожаловать...",
"description": "...",
"getStarted": "..."
},
"profile": { ... },
"search": { ... },
"buttons": { ... },
"errors": { ... },
...
}
```
### 6. Интеграция в код
#### Импорт функции перевода
```typescript
import { getUserTranslation } from '../services/localizationService';
```
#### Использование в коде
```typescript
// Асинхронный вызов
const text = await getUserTranslation(userId, 'welcome.greeting');
// Или через сервис
const locService = LocalizationService.getInstance();
const text = locService.t('welcome.greeting');
```
#### Callback для выбора языка
Все callback_data для выбора языка имеют формат:
```
set_lang_{код_языка}
```
Например:
- `set_lang_ru` - установить русский
- `set_lang_en` - установить английский
- `set_lang_ko` - установить корейский
### 7. Запуск миграции
```bash
# Применить миграцию добавления колонки lang
PGPASSWORD='Cl0ud_1985!' psql -h 192.168.0.102 -p 5432 -U trevor -d telegram_tinder_bot -f sql/add_user_language.sql
```
### 8. Тестирование
#### Проверка выбора языка для нового пользователя:
1. Удалите свой профиль из БД:
```sql
DELETE FROM profiles WHERE user_id IN (
SELECT id FROM users WHERE telegram_id = YOUR_TELEGRAM_ID
);
DELETE FROM users WHERE telegram_id = YOUR_TELEGRAM_ID;
```
2. Отправьте `/start` боту
3. Должно появиться меню выбора из 10 языков
4. Выберите любой язык
5. Проверьте, что приветствие отображается на выбранном языке
#### Проверка сохранения языка:
```sql
SELECT telegram_id, username, lang FROM users;
```
## Следующие шаги
### Приоритет 1: Замена всех хардкод-текстов
Необходимо заменить все хардкод-тексты в следующих файлах:
1. **`src/handlers/messageHandlers.ts`** (профиль, регистрация)
2. **`src/handlers/callbackHandlers.ts`** (кнопки, меню)
3. **`src/handlers/commandHandlers.ts`** (команды)
4. **`src/services/notificationService.ts`** (уведомления)
### Приоритет 2: Дополнение языковых файлов
Текущие файлы содержат только базовые ключи. Нужно:
1. Извлечь все существующие тексты из кода
2. Добавить ключи в `ru.json` (эталонный файл)
3. Перевести ключи для остальных 9 языков
### Приоритет 3: Кнопка смены языка в настройках
Добавить в меню "⚙️ Настройки" кнопку:
```typescript
{ text: '🌍 Язык / Language', callback_data: 'change_language' }
```
## Состояние реализации
**Выполнено:**
- Добавлена колонка `lang` в таблицу `users`
- Создан `LanguageHandlers` для управления языком
- Интегрирован выбор языка в `/start` для новых пользователей
- Обновлен `LocalizationService`
- Добавлены секции `language` в `ru.json` и `en.json`
- Методы работы с языком в `ProfileService`
⚠️ **В процессе:**
- Замена хардкод-текстов на локализационные ключи
- Дополнение всех языковых файлов
📋 **Планируется:**
- Кнопка смены языка в настройках
- Полный перевод всех 10 языков
- Тесты локализации
## Технические детали
### Callback Query Flow
```
Пользователь нажимает кнопку "🇷🇺 Русский"
callback_data: 'set_lang_ru'
callbackHandlers.handleCallback() перехватывает
if (data.startsWith('set_lang_'))
languageHandlers.handleSetLanguage(query)
profileService.updateUserLanguage(userId, 'ru')
localizationService.setLanguage('ru')
Показ приветственного сообщения на русском
```
### Database Schema
```sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
telegram_id BIGINT UNIQUE NOT NULL,
username VARCHAR(255),
first_name VARCHAR(255),
last_name VARCHAR(255),
lang VARCHAR(5) DEFAULT 'ru' NOT NULL, -- ← НОВАЯ КОЛОНКА
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW(),
last_active_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_users_lang ON users(lang);
```
## Примеры использования
### Пример 1: Приветственное сообщение
```typescript
const lang = await profileService.getUserLanguage(userId);
localizationService.setLanguage(lang);
const greeting = localizationService.t('welcome.greeting');
const description = localizationService.t('welcome.description');
await bot.sendMessage(chatId, `${greeting}\n\n${description}`);
```
### Пример 2: Кнопки с переводом
```typescript
const keyboard: InlineKeyboardMarkup = {
inline_keyboard: [
[{
text: localizationService.t('buttons.save'),
callback_data: 'save_profile'
}],
[{
text: localizationService.t('buttons.cancel'),
callback_data: 'cancel'
}]
]
};
```
### Пример 3: Ошибки
```typescript
try {
// ... код
} catch (error) {
const errorMsg = localizationService.t('errors.serverError');
await bot.sendMessage(chatId, errorMsg);
}
```
## Поддержка
Если нужно добавить новый язык:
1. Создайте файл `src/locales/{код}.json`
2. Скопируйте структуру из `ru.json`
3. Переведите все ключи
4. Добавьте язык в `LocalizationService.initialize()`:
```typescript
const newLangTranslations = JSON.parse(
fs.readFileSync(path.join(localesPath, овый_код.json'), 'utf8')
);
```
5. Добавьте в `resources` объект
6. Добавьте в `getSupportedLanguages()`
7. Добавьте кнопку в `LanguageHandlers.showLanguageSelection()`
---
**Статус:** ✅ Система локализации активна и работает
**Версия:** 1.0.0
**Дата:** 06.11.2025