feat: add translation language setting & onboarding flow

- Add separate translation_language setting (independent from interface language)
- Implement 3-step onboarding for new users:
  1. Choose interface language
  2. Choose learning language
  3. Choose translation language
- Fix localization issues when using callback.message (user_id from state)
- Add UserService.get_user_by_id() method
- Add get_user_translation_lang() helper in i18n
- Update all handlers to use correct translation language
- Add localization keys for onboarding (ru/en/ja)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-07 16:35:08 +03:00
parent d937b37a3b
commit 3e5c1be464
14 changed files with 360 additions and 81 deletions

View File

@@ -9,7 +9,7 @@ from database.models import WordSource
from services.user_service import UserService
from services.vocabulary_service import VocabularyService
from services.ai_service import ai_service
from utils.i18n import t
from utils.i18n import t, get_user_lang, get_user_translation_lang
from utils.levels import get_user_level_for_language
router = Router()
@@ -64,7 +64,7 @@ async def generate_words_for_theme(message: Message, state: FSMContext, theme: s
level=current_level,
count=10,
learning_lang=user.learning_language,
translation_lang=user.language_interface,
translation_lang=get_user_translation_lang(user),
)
await generating_msg.delete()
@@ -83,15 +83,15 @@ async def generate_words_for_theme(message: Message, state: FSMContext, theme: s
await state.set_state(WordsStates.viewing_words)
# Показываем подборку
await show_words_list(message, words, theme)
await show_words_list(message, words, theme, user_id)
async def show_words_list(message: Message, words: list, theme: str):
async def show_words_list(message: Message, words: list, theme: str, user_id: int):
"""Показать список слов с кнопками для добавления"""
# Определяем язык интерфейса для заголовка/подсказок
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
user = await UserService.get_user_by_telegram_id(session, user_id)
lang = (user.language_interface if user else 'ru') or 'ru'
text = t(lang, 'words.header', theme=theme) + "\n\n"
@@ -171,9 +171,9 @@ async def add_single_word(callback: CallbackQuery, state: FSMContext):
# Добавляем слово
# Формируем examples с учётом языков
learn = user.learning_language if user else 'en'
ui = user.language_interface if user else 'ru'
translation_lang = get_user_translation_lang(user)
ex = word_data.get('example')
examples = ([{learn: ex, ui: ''}] if ex else [])
examples = ([{learn: ex, translation_lang: ''}] if ex else [])
await VocabularyService.add_word(
session=session,
@@ -181,7 +181,7 @@ async def add_single_word(callback: CallbackQuery, state: FSMContext):
word_original=word_data['word'],
word_translation=word_data['translation'],
source_lang=user.learning_language if user else None,
translation_lang=user.language_interface if user else None,
translation_lang=translation_lang,
transcription=word_data.get('transcription'),
examples=examples,
source=WordSource.SUGGESTED,
@@ -222,9 +222,9 @@ async def add_all_words(callback: CallbackQuery, state: FSMContext):
# Добавляем слово
learn = user.learning_language if user else 'en'
ui = user.language_interface if user else 'ru'
translation_lang = get_user_translation_lang(user)
ex = word_data.get('example')
examples = ([{learn: ex, ui: ''}] if ex else [])
examples = ([{learn: ex, translation_lang: ''}] if ex else [])
await VocabularyService.add_word(
session=session,
@@ -232,7 +232,7 @@ async def add_all_words(callback: CallbackQuery, state: FSMContext):
word_original=word_data['word'],
word_translation=word_data['translation'],
source_lang=user.learning_language if user else None,
translation_lang=user.language_interface if user else None,
translation_lang=translation_lang,
transcription=word_data.get('transcription'),
examples=examples,
source=WordSource.SUGGESTED,
@@ -241,9 +241,10 @@ async def add_all_words(callback: CallbackQuery, state: FSMContext):
)
added_count += 1
result_text = f"✅ Добавлено слов: <b>{added_count}</b>"
lang = (user.language_interface if user else 'ru') or 'ru'
result_text = t(lang, 'import.added_count', n=added_count)
if skipped_count > 0:
result_text += f"\n⚠️ Пропущено (уже в словаре): {skipped_count}"
result_text += "\n" + t(lang, 'import.skipped_count', n=skipped_count)
await callback.message.edit_reply_markup(reply_markup=None)
await callback.message.answer(result_text)