feat: JLPT levels for Japanese, custom practice scenarios, UI improvements

- Add separate level systems: CEFR (A1-C2) for European languages, JLPT (N5-N1) for Japanese
- Store levels per language in new `levels_by_language` JSON field
- Add custom scenario option in AI practice mode
- Show action buttons after practice ends (new dialogue, tasks, words)
- Fix level display across all handlers to use correct level system
- Add Alembic migration for levels_by_language field
- Update all locale files (ru, en, ja) with new keys

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-05 14:30:24 +03:00
parent 8bf3504d8d
commit 99deaafcbf
17 changed files with 983 additions and 308 deletions

View File

@@ -81,7 +81,15 @@
"end_keep": "Great job! Keep practicing.",
"end_hint": "Use /practice to start a new dialogue.",
"translation_unavailable": "Translation unavailable",
"translation_already": "Translation already shown"
"translation_already": "Translation already shown",
"custom_scenario_btn": "✏️ Custom scenario",
"custom_scenario_prompt": "✏️ <b>Describe your scenario</b>\n\nWrite a topic or situation for the conversation.\n\nExamples:\n• Job interview for a programmer position\n• Ordering pizza by phone\n• Discussing a movie with a friend\n• Planning a trip to Japan",
"custom_scenario_too_short": "⚠️ Description too short. Write at least a few words about the scenario.",
"new_practice_btn": "🔄 New dialogue",
"to_tasks_btn": "🧠 Tasks",
"to_words_btn": "🎯 Words",
"go_tasks_hint": "Use /task to practice words",
"go_words_hint": "Use /words [topic] for word sets"
},
"tasks": {
"no_words": "📚 You don't have words to practice yet!\n\nAdd some words with /add and come back.",
@@ -159,6 +167,87 @@
"cancelled": "❌ Test cancelled",
"q_header": "❓ <b>Question {i} of {n}</b>"
},
"settings": {
"title": "⚙️ <b>Settings</b>\n\n",
"level_prefix": "📊 Level: ",
"learning_prefix": "🎯 Learning language: ",
"interface_prefix": "🌐 Interface language: ",
"choose": "Choose what to change:",
"close": "❌ Close",
"back": "⬅️ Back",
"back_to_settings": "⬅️ Back to settings",
"level_title": "📊 <b>Choose your level:</b>\n\n",
"level_groups": "<b>A1-A2</b> - Beginner\n<b>B1-B2</b> - Intermediate\n<b>C1-C2</b> - Advanced\n\n",
"level_hint": "This affects difficulty of suggested words and tasks.",
"level": {
"a1": "A1 - Beginner",
"a2": "A2 - Elementary",
"b1": "B1 - Intermediate",
"b2": "B2 - Upper-intermediate",
"c1": "C1 - Advanced",
"c2": "C2 - Proficient"
},
"jlpt": {
"n5": "N5 - Basic",
"n4": "N4 - Elementary",
"n3": "N3 - Intermediate",
"n2": "N2 - Advanced",
"n1": "N1 - Fluent"
},
"jlpt_groups": "<b>N5-N4</b> - Beginner\n<b>N3</b> - Intermediate\n<b>N2-N1</b> - Advanced\n\n",
"level_changed": "✅ Level changed to <b>{level}</b>\n\n",
"level_changed_hint": "You will now receive words and tasks matching your level!",
"lang_title": "🌐 <b>Select interface language:</b>\n\n",
"lang_desc": "This will change the language of bot messages.",
"lang_changed": "✅ Interface language: <b>English</b>",
"learning_title": "🎯 <b>Select learning language:</b>\n\n",
"learning_changed": "✅ Learning language: <b>{code}</b>",
"menu_updated": "Main menu updated ⤵️",
"lang_name": {
"ru": "🇷🇺 Русский",
"en": "🇬🇧 English",
"ja": "🇯🇵 日本語"
},
"learning_lang": {
"en": "🇬🇧 English",
"es": "🇪🇸 Spanish",
"de": "🇩🇪 German",
"fr": "🇫🇷 French",
"ja": "🇯🇵 Japanese"
}
},
"import_extra": {
"cancelled": "❌ Import cancelled."
},
"level_test_extra": {
"generating": "🔄 Generating questions...",
"generate_failed": "❌ Failed to generate test. Try later or use /settings to set level manually.",
"translation_unavailable": "Translation unavailable",
"translation_marker": "Question translation:",
"translation_already": "Translation already shown",
"correct": "✅ Correct!",
"incorrect": "❌ Incorrect",
"correct_answer": "Correct answer: <b>{answer}</b>",
"result_title": "🎉 <b>Test completed!</b>\n\n",
"results_header": "📊 Results:\n",
"correct_count": "Correct answers: <b>{correct}</b> of {total}\n",
"accuracy": "Accuracy: <b>{accuracy}%</b>\n\n",
"your_level": "🎯 Your level: <b>{level}</b>\n",
"level_set_hint": "Tasks and materials will now be tailored to your level!\nYou can change the level anytime via /settings",
"level_desc": {
"A1": "Beginner - understand basic phrases and can introduce yourself",
"A2": "Elementary - can communicate on simple topics",
"B1": "Intermediate - can maintain conversations on familiar topics",
"B2": "Upper-intermediate - fluent in most situations",
"C1": "Advanced - use language flexibly and effectively",
"C2": "Proficient - mastery at native level",
"N5": "Basic - understand hiragana, katakana and basic kanji",
"N4": "Elementary - understand everyday conversations",
"N3": "Intermediate - understand common texts and conversations",
"N2": "Advanced - understand most content",
"N1": "Fluent - full proficiency in Japanese"
}
},
"words": {
"generating": "🔄 Generating words for topic '{theme}'...",
"generate_failed": "❌ Failed to generate words. Please try again later.",

View File

@@ -73,7 +73,15 @@
"end_keep": "素晴らしい!練習を続けましょう。",
"end_hint": "/practice で新しい会話を始められます。",
"translation_unavailable": "翻訳は利用できません",
"translation_already": "翻訳はすでに表示されています"
"translation_already": "翻訳はすでに表示されています",
"custom_scenario_btn": "✏️ カスタムシナリオ",
"custom_scenario_prompt": "✏️ <b>シナリオを入力してください</b>\n\n会話のトピックや状況を書いてください。\n\n例:\n• プログラマーの就職面接\n• 電話でピザを注文\n• 友達と映画について話す\n• 日本旅行の計画",
"custom_scenario_too_short": "⚠️ 説明が短すぎます。シナリオについてもう少し詳しく書いてください。",
"new_practice_btn": "🔄 新しい会話",
"to_tasks_btn": "🧠 課題",
"to_words_btn": "🎯 単語",
"go_tasks_hint": "/task で単語を練習できます",
"go_words_hint": "/words [テーマ] で単語セットを取得できます"
},
"tasks": {
"no_words": "📚 まだ練習用の単語がありません!\n\n/add で単語を追加してから戻ってきてください。",
@@ -151,6 +159,87 @@
"cancelled": "❌ テストを中止しました",
"q_header": "❓ <b>{n}問中 {i} 問目</b>"
},
"settings": {
"title": "⚙️ <b>設定</b>\n\n",
"level_prefix": "📊 レベル: ",
"learning_prefix": "🎯 学習言語: ",
"interface_prefix": "🌐 インターフェース言語: ",
"choose": "変更したい項目を選択:",
"close": "❌ 閉じる",
"back": "⬅️ 戻る",
"back_to_settings": "⬅️ 設定に戻る",
"level_title": "📊 <b>レベルを選択:</b>\n\n",
"level_groups": "<b>A1-A2</b> - 初級\n<b>B1-B2</b> - 中級\n<b>C1-C2</b> - 上級\n\n",
"level_hint": "これは提案される単語や課題の難易度に影響します。",
"level": {
"a1": "A1 - 初級",
"a2": "A2 - 初級(上)",
"b1": "B1 - 中級",
"b2": "B2 - 中級(上)",
"c1": "C1 - 上級",
"c2": "C2 - ネイティブ"
},
"jlpt": {
"n5": "N5 - 基礎",
"n4": "N4 - 初級",
"n3": "N3 - 中級",
"n2": "N2 - 上級",
"n1": "N1 - 流暢"
},
"jlpt_groups": "<b>N5-N4</b> - 初級\n<b>N3</b> - 中級\n<b>N2-N1</b> - 上級\n\n",
"level_changed": "✅ レベルが変更されました: <b>{level}</b>\n\n",
"level_changed_hint": "これからレベルに合った単語と課題が出題されます!",
"lang_title": "🌐 <b>インターフェース言語を選択:</b>\n\n",
"lang_desc": "ボットの表示言語が変更されます。",
"lang_changed": "✅ インターフェース言語: <b>日本語</b>",
"learning_title": "🎯 <b>学習言語を選択:</b>\n\n",
"learning_changed": "✅ 学習言語: <b>{code}</b>",
"menu_updated": "メインメニューを更新しました ⤵️",
"lang_name": {
"ru": "🇷🇺 Русский",
"en": "🇬🇧 English",
"ja": "🇯🇵 日本語"
},
"learning_lang": {
"en": "🇬🇧 英語",
"es": "🇪🇸 スペイン語",
"de": "🇩🇪 ドイツ語",
"fr": "🇫🇷 フランス語",
"ja": "🇯🇵 日本語"
}
},
"import_extra": {
"cancelled": "❌ インポートを中止しました。"
},
"level_test_extra": {
"generating": "🔄 質問を生成しています...",
"generate_failed": "❌ テストの生成に失敗しました。後でもう一度試すか、/settings でレベルを手動設定してください。",
"translation_unavailable": "翻訳は利用できません",
"translation_marker": "質問の翻訳:",
"translation_already": "翻訳はすでに表示されています",
"correct": "✅ 正解!",
"incorrect": "❌ 不正解",
"correct_answer": "正解: <b>{answer}</b>",
"result_title": "🎉 <b>テスト完了!</b>\n\n",
"results_header": "📊 結果:\n",
"correct_count": "正解数: <b>{correct}</b> / {total}\n",
"accuracy": "正答率: <b>{accuracy}%</b>\n\n",
"your_level": "🎯 あなたのレベル: <b>{level}</b>\n",
"level_set_hint": "これから課題や教材があなたのレベルに合わせて出題されます!\n/settings でいつでもレベルを変更できます",
"level_desc": {
"A1": "初級 - 基本的なフレーズを理解し、自己紹介ができる",
"A2": "初級(上) - 簡単なトピックでコミュニケーションできる",
"B1": "中級 - 慣れた話題で会話を続けられる",
"B2": "中級(上) - ほとんどの状況で流暢に話せる",
"C1": "上級 - 言語を柔軟かつ効果的に使える",
"C2": "ネイティブ - ネイティブレベルの言語力",
"N5": "基礎 - ひらがな、カタカナ、基本漢字を理解できる",
"N4": "初級 - 日常会話を理解できる",
"N3": "中級 - 一般的な文章や会話を理解できる",
"N2": "上級 - ほとんどのコンテンツを理解できる",
"N1": "流暢 - 日本語を完全に習得している"
}
},
"words": {
"generating": "🔄 テーマ『{theme}』の単語を生成中...",
"generate_failed": "❌ 単語の生成に失敗しました。後でもう一度お試しください。",

View File

@@ -81,7 +81,15 @@
"end_keep": "Отличная работа! Продолжай практиковаться.",
"end_hint": "Используй /practice для нового диалога.",
"translation_unavailable": "Перевод недоступен",
"translation_already": "Перевод уже показан"
"translation_already": "Перевод уже показан",
"custom_scenario_btn": "✏️ Свой сценарий",
"custom_scenario_prompt": "✏️ <b>Опиши свой сценарий</b>\n\nНапиши тему или ситуацию для разговора.\n\nПримеры:\n• Собеседование на работу программистом\n• Заказ пиццы по телефону\n• Обсуждение фильма с другом\n• Планирование путешествия в Японию",
"custom_scenario_too_short": "⚠️ Слишком короткое описание. Напиши хотя бы несколько слов о сценарии.",
"new_practice_btn": "🔄 Новый диалог",
"to_tasks_btn": "🧠 Задания",
"to_words_btn": "🎯 Слова",
"go_tasks_hint": "Используй /task для тренировки слов",
"go_words_hint": "Используй /words [тема] для подборки слов"
},
"tasks": {
"no_words": "📚 У тебя пока нет слов для практики!\n\nДобавь несколько слов командой /add, а затем возвращайся.",
@@ -159,6 +167,87 @@
"cancelled": "❌ Тест отменён",
"q_header": "❓ <b>Вопрос {i} из {n}</b>"
},
"settings": {
"title": "⚙️ <b>Настройки</b>\n\n",
"level_prefix": "📊 Уровень: ",
"learning_prefix": "🎯 Язык изучения: ",
"interface_prefix": "🌐 Язык интерфейса: ",
"choose": "Выбери, что хочешь изменить:",
"close": "❌ Закрыть",
"back": "⬅️ Назад",
"back_to_settings": "⬅️ К настройкам",
"level_title": "📊 <b>Выбери свой уровень:</b>\n\n",
"level_groups": "<b>A1-A2</b> - Начинающий\n<b>B1-B2</b> - Средний\n<b>C1-C2</b> - Продвинутый\n\n",
"level_hint": "Это влияет на сложность предлагаемых слов и заданий.",
"level": {
"a1": "A1 - Начальный",
"a2": "A2 - Элементарный",
"b1": "B1 - Средний",
"b2": "B2 - Выше среднего",
"c1": "C1 - Продвинутый",
"c2": "C2 - Профессиональный"
},
"jlpt": {
"n5": "N5 - Базовый",
"n4": "N4 - Начальный",
"n3": "N3 - Средний",
"n2": "N2 - Продвинутый",
"n1": "N1 - Свободный"
},
"jlpt_groups": "<b>N5-N4</b> - Начинающий\n<b>N3</b> - Средний\n<b>N2-N1</b> - Продвинутый\n\n",
"level_changed": "✅ Уровень изменен на <b>{level}</b>\n\n",
"level_changed_hint": "Теперь ты будешь получать слова и задания, соответствующие твоему уровню!",
"lang_title": "🌐 <b>Выбери язык интерфейса:</b>\n\n",
"lang_desc": "Это изменит язык всех сообщений бота.",
"lang_changed": "✅ Язык интерфейса: <b>Русский</b>",
"learning_title": "🎯 <b>Выбери язык изучения:</b>\n\n",
"learning_changed": "✅ Язык изучения: <b>{code}</b>",
"menu_updated": "Клавиатура обновлена ⤵️",
"lang_name": {
"ru": "🇷🇺 Русский",
"en": "🇬🇧 English",
"ja": "🇯🇵 日本語"
},
"learning_lang": {
"en": "🇬🇧 Английский",
"es": "🇪🇸 Испанский",
"de": "🇩🇪 Немецкий",
"fr": "🇫🇷 Французский",
"ja": "🇯🇵 Японский"
}
},
"import_extra": {
"cancelled": "❌ Импорт отменён."
},
"level_test_extra": {
"generating": "🔄 Генерирую вопросы...",
"generate_failed": "❌ Не удалось сгенерировать тест. Попробуй позже или используй /settings для ручной установки уровня.",
"translation_unavailable": "Перевод недоступен",
"translation_marker": "Перевод вопроса:",
"translation_already": "Перевод уже показан",
"correct": "✅ Правильно!",
"incorrect": "❌ Неправильно",
"correct_answer": "Правильный ответ: <b>{answer}</b>",
"result_title": "🎉 <b>Тест завершён!</b>\n\n",
"results_header": "📊 Результаты:\n",
"correct_count": "Правильных ответов: <b>{correct}</b> из {total}\n",
"accuracy": "Точность: <b>{accuracy}%</b>\n\n",
"your_level": "🎯 Твой уровень: <b>{level}</b>\n",
"level_set_hint": "Теперь задания и материалы будут подбираться под твой уровень!\nТы можешь изменить уровень в любое время через /settings",
"level_desc": {
"A1": "Начальный - понимаешь основные фразы и можешь представиться",
"A2": "Элементарный - можешь общаться на простые темы",
"B1": "Средний - можешь поддержать беседу на знакомые темы",
"B2": "Выше среднего - свободно общаешься в большинстве ситуаций",
"C1": "Продвинутый - используешь язык гибко и эффективно",
"C2": "Профессиональный - владеешь языком на уровне носителя",
"N5": "Базовый - понимаешь хирагану, катакану и базовые кандзи",
"N4": "Начальный - понимаешь повседневные разговоры",
"N3": "Средний - понимаешь обычные тексты и разговоры",
"N2": "Продвинутый - понимаешь большинство контента",
"N1": "Свободный - полное владение японским языком"
}
},
"words": {
"generating": "🔄 Генерирую подборку слов по теме '{theme}'...",
"generate_failed": "❌ Не удалось сгенерировать подборку. Попробуй позже.",