feat(i18n): localize start/help/menu, practice, words, import, reminder, vocabulary, tasks/stats for RU/EN/JA; add JSON-based i18n helper\n\nfeat(lang): support learning/translation languages across AI flows; hide translations with buttons; store examples per lang\n\nfeat(vocab): add source_lang and translation_lang to Vocabulary, unique constraint (user_id, source_lang, word_original); filter /vocabulary by user.learning_language\n\nchore(migrations): add Alembic setup + migration to add vocab lang columns; env.py reads app settings and supports asyncpg URLs\n\nfix(words/import): pass learning_lang + translation_lang everywhere; fix menu themes generation\n\nfeat(settings): add learning language selector; update main menu on language change

This commit is contained in:
2025-12-04 19:40:01 +03:00
parent 6223351ccf
commit 472771229f
22 changed files with 1587 additions and 471 deletions

View File

@@ -6,6 +6,7 @@ from aiogram.fsm.state import State, StatesGroup
from database.db import async_session_maker
from services.user_service import UserService
from utils.i18n import t
router = Router()
@@ -22,19 +23,19 @@ async def cmd_reminder(message: Message):
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
if not user:
await message.answer("Сначала запусти бота командой /start")
await message.answer(t('ru', 'common.start_first'))
return
# Формируем текст
status = "✅ Включены" if user.reminders_enabled else "❌ Выключены"
time_text = user.daily_task_time if user.daily_task_time else "Не установлено"
lang = (user.language_interface if user else 'ru') or 'ru'
status = t(lang, 'reminder.status_on') if user.reminders_enabled else t(lang, 'reminder.status_off')
time_text = user.daily_task_time if user.daily_task_time else t(lang, 'reminder.time_not_set')
text = (
f"⏰ <b>Напоминания</b>\n\n"
f"Статус: {status}\n"
f"Время: {time_text} UTC\n\n"
f"Напоминания помогут не забывать о ежедневной практике.\n"
f"Бот будет присылать сообщение в выбранное время каждый день."
t(lang, 'reminder.title') + "\n\n" +
t(lang, 'reminder.status_line', status=status) + "\n" +
t(lang, 'reminder.time_line', time=time_text) + "\n\n" +
t(lang, 'reminder.desc1') + "\n" +
t(lang, 'reminder.desc2')
)
# Создаем кнопки
@@ -42,15 +43,15 @@ async def cmd_reminder(message: Message):
if user.reminders_enabled:
keyboard.append([
InlineKeyboardButton(text="❌ Выключить", callback_data="reminder_disable")
InlineKeyboardButton(text=t(lang, 'reminder.btn_disable'), callback_data="reminder_disable")
])
else:
keyboard.append([
InlineKeyboardButton(text="✅ Включить", callback_data="reminder_enable")
InlineKeyboardButton(text=t(lang, 'reminder.btn_enable'), callback_data="reminder_enable")
])
keyboard.append([
InlineKeyboardButton(text="⏰ Изменить время", callback_data="reminder_set_time")
InlineKeyboardButton(text=t(lang, 'reminder.btn_change_time'), callback_data="reminder_set_time")
])
reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard)
@@ -65,7 +66,7 @@ async def enable_reminders(callback: CallbackQuery):
if not user.daily_task_time:
await callback.answer(
"Сначала установи время напоминаний!",
t(user.language_interface or 'ru', 'reminder.set_time_first'),
show_alert=True
)
return
@@ -73,11 +74,12 @@ async def enable_reminders(callback: CallbackQuery):
user.reminders_enabled = True
await session.commit()
await callback.answer("✅ Напоминания включены!")
lang = (user.language_interface if user else 'ru') or 'ru'
await callback.answer(t(lang, 'reminder.enabled_toast'))
await callback.message.edit_text(
f"✅ <b>Напоминания включены!</b>\n\n"
f"Время: {user.daily_task_time} UTC\n\n"
f"Ты будешь получать ежедневные напоминания о практике."
t(lang, 'reminder.enabled_title') + "\n\n" +
t(lang, 'reminder.time_line', time=user.daily_task_time) + "\n\n" +
t(lang, 'reminder.enabled_desc')
)
@@ -90,10 +92,11 @@ async def disable_reminders(callback: CallbackQuery):
user.reminders_enabled = False
await session.commit()
await callback.answer("❌ Напоминания выключены")
lang = (user.language_interface if user else 'ru') or 'ru'
await callback.answer(t(lang, 'reminder.disabled_toast'))
await callback.message.edit_text(
"❌ <b>Напоминания выключены</b>\n\n"
"Используй /reminder чтобы включить их снова."
t(lang, 'reminder.disabled_title') + "\n\n" +
t(lang, 'reminder.disabled_desc')
)
@@ -102,16 +105,15 @@ async def set_reminder_time_prompt(callback: CallbackQuery, state: FSMContext):
"""Запросить время для напоминаний"""
await state.set_state(ReminderStates.waiting_for_time)
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
await callback.message.edit_text(
"⏰ <b>Установка времени напоминаний</b>\n\n"
"Отправь время в формате <b>HH:MM</b> (UTC)\n\n"
"Примеры:\n"
"• <code>09:00</code> - 9 утра по UTC\n"
"• <code>18:30</code> - 18:30 по UTC\n"
"• <code>20:00</code> - 8 вечера по UTC\n\n"
"💡 UTC = МСК - 3 часа\n"
"(если хочешь 12:00 по МСК, введи 09:00)\n\n"
"Отправь /cancel для отмены"
t(lang, 'reminder.set_title') + "\n\n" +
t(lang, 'reminder.set_desc') + "\n\n" +
t(lang, 'reminder.set_examples') + "\n\n" +
t(lang, 'reminder.set_utc_hint') + "\n\n" +
t(lang, 'reminder.cancel_hint')
)
await callback.answer()
@@ -120,7 +122,7 @@ async def set_reminder_time_prompt(callback: CallbackQuery, state: FSMContext):
async def cancel_set_time(message: Message, state: FSMContext):
"""Отменить установку времени"""
await state.clear()
await message.answer("❌ Установка времени отменена")
await message.answer(t('ru', 'reminder.cancelled'))
@router.message(ReminderStates.waiting_for_time)
@@ -143,11 +145,7 @@ async def process_reminder_time(message: Message, state: FSMContext):
formatted_time = f"{hour:02d}:{minute:02d}"
except:
await message.answer(
"❌ Неверный формат времени!\n\n"
"Используй формат <b>HH:MM</b> (например, 09:00 или 18:30)\n"
"Или отправь /cancel для отмены"
)
await message.answer(t('ru', 'reminder.invalid_format'))
return
# Сохраняем время
@@ -163,10 +161,13 @@ async def process_reminder_time(message: Message, state: FSMContext):
await state.clear()
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
await message.answer(
f"✅ <b>Время установлено!</b>\n\n"
f"Напоминания: <b>{formatted_time} UTC</b>\n"
f"Статус: <b>Включены</b>\n\n"
f"Ты будешь получать ежедневные напоминания о практике.\n"
f"Используй /reminder для изменения настроек."
t(lang, 'reminder.time_set_title') + "\n\n" +
t(lang, 'reminder.time_line', time=formatted_time) + "\n" +
t(lang, 'reminder.status_on_line') + "\n\n" +
t(lang, 'reminder.enabled_desc') + "\n" +
t(lang, 'reminder.use_settings')
)