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:
@@ -26,23 +26,17 @@ async def cmd_level_test(message: Message, state: FSMContext):
|
||||
async def start_level_test(message: Message, state: FSMContext):
|
||||
"""Начать тест определения уровня"""
|
||||
# Показываем описание теста
|
||||
await message.answer(
|
||||
"📊 <b>Тест определения уровня</b>\n\n"
|
||||
"Этот короткий тест поможет определить твой уровень английского.\n\n"
|
||||
"📋 Тест включает 7 вопросов:\n"
|
||||
"• Грамматика\n"
|
||||
"• Лексика\n"
|
||||
"• Понимание\n\n"
|
||||
"⏱ Займёт около 2-3 минут\n\n"
|
||||
"Готов начать?"
|
||||
)
|
||||
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(t(lang, 'level_test.intro'))
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="✅ Начать тест", callback_data="start_test")],
|
||||
[InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_test")]
|
||||
[InlineKeyboardButton(text=t(lang, 'level_test.start_btn'), callback_data="start_test")],
|
||||
[InlineKeyboardButton(text=t(lang, 'level_test.cancel_btn'), callback_data="cancel_test")]
|
||||
])
|
||||
|
||||
await message.answer("Нажми кнопку когда будешь готов:", reply_markup=keyboard)
|
||||
await message.answer(t(lang, 'level_test.press_button'), reply_markup=keyboard)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "cancel_test")
|
||||
@@ -50,13 +44,18 @@ async def cancel_test(callback: CallbackQuery, state: FSMContext):
|
||||
"""Отменить тест"""
|
||||
await state.clear()
|
||||
await callback.message.delete()
|
||||
await callback.message.answer("❌ Тест отменён")
|
||||
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.answer(t(lang, 'level_test.cancelled'))
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data == "start_test")
|
||||
async def begin_test(callback: CallbackQuery, state: FSMContext):
|
||||
"""Начать прохождение теста"""
|
||||
# Сразу отвечаем на callback, чтобы избежать истечения таймаута
|
||||
await callback.answer()
|
||||
await callback.message.delete()
|
||||
|
||||
# Показываем индикатор загрузки
|
||||
@@ -86,7 +85,6 @@ async def begin_test(callback: CallbackQuery, state: FSMContext):
|
||||
|
||||
# Показываем первый вопрос
|
||||
await show_question(callback.message, state)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
async def show_question(message: Message, state: FSMContext):
|
||||
@@ -103,10 +101,14 @@ async def show_question(message: Message, state: FSMContext):
|
||||
question = questions[current_idx]
|
||||
|
||||
# Формируем текст вопроса
|
||||
# Язык интерфейса
|
||||
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'
|
||||
|
||||
text = (
|
||||
f"❓ <b>Вопрос {current_idx + 1} из {len(questions)}</b>\n\n"
|
||||
t(lang, 'level_test.q_header', i=current_idx + 1, n=len(questions)) + "\n\n" +
|
||||
f"<b>{question['question']}</b>\n"
|
||||
f"<i>{question.get('question_ru', '')}</i>\n\n"
|
||||
)
|
||||
|
||||
# Создаем кнопки с вариантами ответа
|
||||
@@ -120,10 +122,52 @@ async def show_question(message: Message, state: FSMContext):
|
||||
)
|
||||
])
|
||||
|
||||
# Кнопка для показа перевода вопроса (локализованная)
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, message.chat.id)
|
||||
from utils.i18n import t
|
||||
lang = (user.language_interface if user else 'ru') or 'ru'
|
||||
keyboard.append([
|
||||
InlineKeyboardButton(text=t(lang, 'level_test.show_translation_btn'), callback_data=f"show_qtr_{current_idx}")
|
||||
])
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard)
|
||||
await message.answer(text, reply_markup=reply_markup)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("show_qtr_"), LevelTestStates.taking_test)
|
||||
async def show_question_translation(callback: CallbackQuery, state: FSMContext):
|
||||
"""Показать перевод текущего вопроса"""
|
||||
try:
|
||||
idx = int(callback.data.split("_")[-1])
|
||||
except Exception:
|
||||
await callback.answer("Перевод недоступен", show_alert=True)
|
||||
return
|
||||
|
||||
data = await state.get_data()
|
||||
questions = data.get('questions', [])
|
||||
if not (0 <= idx < len(questions)):
|
||||
await callback.answer("Перевод недоступен", show_alert=True)
|
||||
return
|
||||
|
||||
ru = questions[idx].get('question_ru') or "Перевод недоступен"
|
||||
|
||||
# Вставляем перевод в текущий текст сообщения
|
||||
orig = callback.message.text or ""
|
||||
marker = "Перевод вопроса:"
|
||||
if marker in orig:
|
||||
await callback.answer("Перевод уже показан")
|
||||
return
|
||||
|
||||
new_text = f"{orig}\n{marker} <i>{ru}</i>"
|
||||
try:
|
||||
await callback.message.edit_text(new_text, reply_markup=callback.message.reply_markup)
|
||||
except Exception:
|
||||
# Запасной путь, если редактирование невозможно
|
||||
await callback.message.answer(f"{marker} <i>{ru}</i>")
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("answer_"), LevelTestStates.taking_test)
|
||||
async def process_answer(callback: CallbackQuery, state: FSMContext):
|
||||
"""Обработать ответ на вопрос"""
|
||||
|
||||
Reference in New Issue
Block a user