from aiogram import Router, F from aiogram.filters import Command from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton from aiogram.fsm.context import FSMContext from database.db import async_session_maker from bot.handlers.start import main_menu_keyboard from services.user_service import UserService from utils.i18n import t, get_user_lang from utils.levels import ( get_user_level_for_language, get_available_levels, get_level_system, get_level_key_for_i18n, ) router = Router() def get_translation_language(user) -> str: """Получить язык перевода (translation_language или language_interface как fallback)""" return getattr(user, 'translation_language', None) or getattr(user, 'language_interface', 'ru') or 'ru' def get_settings_keyboard(user) -> InlineKeyboardMarkup: """Создать клавиатуру настроек""" lang = get_user_lang(user) ui_lang_code = getattr(user, 'language_interface', 'ru') or 'ru' translation_lang_code = get_translation_language(user) current_level = get_user_level_for_language(user) keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton( text=t(lang, 'settings.level_prefix') + f"{current_level}", callback_data="settings_level" )], [InlineKeyboardButton( text=t(lang, 'settings.learning_prefix') + user.learning_language.upper(), callback_data="settings_learning" )], [InlineKeyboardButton( text=t(lang, 'settings.interface_prefix') + t(lang, f'settings.lang_name.{ui_lang_code}'), callback_data="settings_language" )], [InlineKeyboardButton( text=t(lang, 'settings.translation_prefix') + t(lang, f'settings.lang_name.{translation_lang_code}'), callback_data="settings_translation" )], [InlineKeyboardButton( text=t(lang, 'settings.close'), callback_data="settings_close" )] ]) return keyboard def get_level_keyboard(user=None) -> InlineKeyboardMarkup: """Клавиатура выбора уровня (CEFR или JLPT в зависимости от языка изучения)""" lang = get_user_lang(user) learning_lang = getattr(user, 'learning_language', 'en') or 'en' available_levels = get_available_levels(learning_lang) keyboard = [] for level in available_levels: # Ключ локализации: settings.level.a1 или settings.jlpt.n5 i18n_key = get_level_key_for_i18n(learning_lang, level) level_name = t(lang, i18n_key) keyboard.append([InlineKeyboardButton(text=level_name, callback_data=f"set_level_{level}")]) keyboard.append([InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")]) return InlineKeyboardMarkup(inline_keyboard=keyboard) def get_language_keyboard(user=None) -> InlineKeyboardMarkup: """Клавиатура выбора языка интерфейса""" lang = get_user_lang(user) keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text=t(lang, 'settings.lang_name.ru'), callback_data="set_lang_ru")], [InlineKeyboardButton(text=t(lang, 'settings.lang_name.en'), callback_data="set_lang_en")], [InlineKeyboardButton(text=t(lang, 'settings.lang_name.ja'), callback_data="set_lang_ja")], [InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")] ]) return keyboard def get_translation_language_keyboard(user=None) -> InlineKeyboardMarkup: """Клавиатура выбора языка перевода""" lang = get_user_lang(user) keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text=t(lang, 'settings.lang_name.ru'), callback_data="set_translation_ru")], [InlineKeyboardButton(text=t(lang, 'settings.lang_name.en'), callback_data="set_translation_en")], [InlineKeyboardButton(text=t(lang, 'settings.lang_name.ja'), callback_data="set_translation_ja")], [InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")] ]) return keyboard def get_learning_language_keyboard(user=None) -> InlineKeyboardMarkup: """Клавиатура выбора языка изучения""" lang = get_user_lang(user) options = [ ("en", t(lang, 'settings.learning_lang.en')), ("ja", t(lang, 'settings.learning_lang.ja')), # TODO: добавить позже # ("es", t(lang, 'settings.learning_lang.es')), # ("de", t(lang, 'settings.learning_lang.de')), # ("fr", t(lang, 'settings.learning_lang.fr')), ] keyboard = [[InlineKeyboardButton(text=label, callback_data=f"set_learning_{code}")] for code, label in options] keyboard.append([InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")]) return InlineKeyboardMarkup(inline_keyboard=keyboard) @router.message(Command("settings")) async def cmd_settings(message: Message): """Обработчик команды /settings""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, message.from_user.id) if not user: await message.answer(t('ru', 'common.start_first')) return lang = get_user_lang(user) ui_lang_code = getattr(user, 'language_interface', 'ru') or 'ru' lang_value = t(lang, f'settings.lang_name.{ui_lang_code}') current_level = get_user_level_for_language(user) settings_text = ( t(lang, 'settings.title') + t(lang, 'settings.level_prefix') + f"{current_level}\n" + t(lang, 'settings.interface_prefix') + f"{lang_value}\n\n" + t(lang, 'settings.choose') ) await message.answer(settings_text, reply_markup=get_settings_keyboard(user)) @router.callback_query(F.data == "settings_level") async def settings_level(callback: CallbackQuery): """Показать выбор уровня""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) lang = get_user_lang(user) learning_lang = getattr(user, 'learning_language', 'en') or 'en' level_system = get_level_system(learning_lang) # Выбираем правильное описание групп уровней groups_key = 'settings.jlpt_groups' if level_system == 'jlpt' else 'settings.level_groups' text = t(lang, 'settings.level_title') + t(lang, groups_key) + t(lang, 'settings.level_hint') await callback.message.edit_text(text, reply_markup=get_level_keyboard(user)) await callback.answer() @router.callback_query(F.data == "settings_learning") async def settings_learning(callback: CallbackQuery): """Показать выбор языка изучения""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) lang = get_user_lang(user) await callback.message.edit_text(t(lang, 'settings.learning_title'), reply_markup=get_learning_language_keyboard(user)) await callback.answer() @router.callback_query(F.data.startswith("set_learning_")) async def set_learning_language(callback: CallbackQuery): """Установить язык изучения""" code = callback.data.split("_")[-1] async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) if user: await UserService.update_user_learning_language(session, user.id, code) lang = get_user_lang(user) text = t(lang, 'settings.learning_changed', code=code.upper()) await callback.message.edit_text( text, reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(lang, 'settings.back_to_settings'), callback_data="settings_back")]]) ) await callback.answer() @router.callback_query(F.data.startswith("set_level_")) async def set_level(callback: CallbackQuery): """Установить уровень (CEFR или JLPT)""" level_str = callback.data.split("_")[-1] # A1, A2, B1, B2, C1, C2 или N5, N4, N3, N2, N1 async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) if user: # Передаём строковый уровень, UserService сам разберётся с системой await UserService.update_user_level(session, user.id, level_str) lang = get_user_lang(user) msg = t(lang, 'settings.level_changed', level=level_str) + t(lang, 'settings.level_changed_hint') await callback.message.edit_text( msg, reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(lang, 'settings.back_to_settings'), callback_data="settings_back")]]) ) await callback.answer() @router.callback_query(F.data == "settings_language") async def settings_language(callback: CallbackQuery): """Показать выбор языка""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) lang = get_user_lang(user) await callback.message.edit_text( t(lang, 'settings.lang_title') + t(lang, 'settings.lang_desc'), reply_markup=get_language_keyboard(user) ) await callback.answer() @router.callback_query(F.data.startswith("set_lang_")) async def set_language(callback: CallbackQuery): """Установить язык""" new_lang = callback.data.split("_")[-1] # ru | en | ja async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) if user: await UserService.update_user_language(session, user.id, new_lang) # Используем новый язык для сообщений text = t(new_lang, 'settings.lang_changed') await callback.message.edit_text( text, reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(new_lang, 'settings.back'), callback_data="settings_back")]]) ) # Обновляем клавиатуру чата на выбранный язык await callback.message.answer(t(new_lang, 'settings.menu_updated'), reply_markup=main_menu_keyboard(new_lang)) await callback.answer() @router.callback_query(F.data == "settings_translation") async def settings_translation(callback: CallbackQuery): """Показать выбор языка перевода""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) lang = get_user_lang(user) await callback.message.edit_text( t(lang, 'settings.translation_title') + t(lang, 'settings.translation_desc'), reply_markup=get_translation_language_keyboard(user) ) await callback.answer() @router.callback_query(F.data.startswith("set_translation_")) async def set_translation_language(callback: CallbackQuery): """Установить язык перевода""" new_translation_lang = callback.data.split("_")[-1] # ru | en | ja async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) if user: await UserService.update_user_translation_language(session, user.id, new_translation_lang) lang = get_user_lang(user) lang_name = t(lang, f'settings.lang_name.{new_translation_lang}') text = t(lang, 'settings.translation_changed', lang_name=lang_name) await callback.message.edit_text( text, reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")]]) ) await callback.answer() @router.callback_query(F.data == "settings_back") async def settings_back(callback: CallbackQuery): """Вернуться к настройкам""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) if user: lang = get_user_lang(user) ui_lang_code = getattr(user, 'language_interface', 'ru') or 'ru' lang_value = t(lang, f'settings.lang_name.{ui_lang_code}') current_level = get_user_level_for_language(user) settings_text = ( t(lang, 'settings.title') + t(lang, 'settings.level_prefix') + f"{current_level}\n" + t(lang, 'settings.interface_prefix') + f"{lang_value}\n\n" + t(lang, 'settings.choose') ) await callback.message.edit_text(settings_text, reply_markup=get_settings_keyboard(user)) await callback.answer() @router.callback_query(F.data == "settings_close") async def settings_close(callback: CallbackQuery): """Закрыть настройки""" await callback.message.delete() await callback.answer()