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:
@@ -56,27 +56,27 @@ class AIService:
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
async def translate_word(self, word: str, target_lang: str = "ru") -> Dict:
|
||||
async def translate_word(self, word: str, source_lang: str = "en", translation_lang: str = "ru") -> Dict:
|
||||
"""
|
||||
Перевести слово и получить дополнительную информацию
|
||||
|
||||
Args:
|
||||
word: Слово для перевода
|
||||
target_lang: Язык перевода (по умолчанию русский)
|
||||
source_lang: Язык исходного слова (ISO2)
|
||||
translation_lang: Язык перевода (ISO2)
|
||||
|
||||
Returns:
|
||||
Dict с переводом, транскрипцией и примерами
|
||||
"""
|
||||
prompt = f"""Переведи английское слово/фразу "{word}" на русский язык.
|
||||
prompt = f"""Переведи слово/фразу "{word}" с языка {source_lang} на {translation_lang}.
|
||||
|
||||
Верни ответ строго в формате JSON:
|
||||
{{
|
||||
"word": "{word}",
|
||||
"translation": "перевод",
|
||||
"transcription": "транскрипция в IPA",
|
||||
"word": "исходное слово на {source_lang}",
|
||||
"translation": "перевод на {translation_lang}",
|
||||
"transcription": "транскрипция в IPA (если применимо)",
|
||||
"examples": [
|
||||
{{"en": "пример на английском", "ru": "перевод примера"}},
|
||||
{{"en": "ещё один пример", "ru": "перевод примера"}}
|
||||
{{"{source_lang}": "пример на языке обучения", "{translation_lang}": "перевод примера"}}
|
||||
],
|
||||
"category": "категория слова (работа, еда, путешествия и т.д.)",
|
||||
"difficulty": "уровень сложности (A1/A2/B1/B2/C1/C2)"
|
||||
@@ -85,10 +85,10 @@ class AIService:
|
||||
Важно: верни только JSON, без дополнительного текста."""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] translate_word: word='{word}', target_lang='{target_lang}'")
|
||||
logger.info(f"[GPT Request] translate_word: word='{word}', source='{source_lang}', to='{translation_lang}'")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - помощник для изучения английского языка. Отвечай только в формате JSON."},
|
||||
{"role": "system", "content": "Ты - помощник для изучения языков. Отвечай только в формате JSON."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
@@ -161,33 +161,35 @@ class AIService:
|
||||
"score": 0
|
||||
}
|
||||
|
||||
async def generate_fill_in_sentence(self, word: str) -> Dict:
|
||||
async def generate_fill_in_sentence(self, word: str, learning_lang: str = "en", translation_lang: str = "ru") -> Dict:
|
||||
"""
|
||||
Сгенерировать предложение с пропуском для заданного слова
|
||||
|
||||
Args:
|
||||
word: Слово, для которого нужно создать предложение
|
||||
word: Слово (на языке обучения), для которого нужно создать предложение
|
||||
learning_lang: Язык обучения (ISO2)
|
||||
translation_lang: Язык перевода предложения (ISO2)
|
||||
|
||||
Returns:
|
||||
Dict с предложением и правильным ответом
|
||||
"""
|
||||
prompt = f"""Создай предложение на английском языке, используя слово "{word}".
|
||||
prompt = f"""Создай предложение на языке {learning_lang}, используя слово "{word}".
|
||||
Замени это слово на пропуск "___".
|
||||
|
||||
Верни ответ в формате JSON:
|
||||
{{
|
||||
"sentence": "предложение с пропуском ___",
|
||||
"answer": "{word}",
|
||||
"translation": "перевод предложения на русский"
|
||||
"translation": "перевод предложения на {translation_lang}"
|
||||
}}
|
||||
|
||||
Предложение должно быть простым и естественным. Контекст должен четко подсказывать правильное слово."""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] generate_fill_in_sentence: word='{word}'")
|
||||
logger.info(f"[GPT Request] generate_fill_in_sentence: word='{word}', lang='{learning_lang}', to='{translation_lang}'")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - преподаватель английского языка. Создавай простые и понятные упражнения."},
|
||||
{"role": "system", "content": "Ты - преподаватель иностранных языков. Создавай простые и понятные упражнения."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
@@ -206,7 +208,7 @@ class AIService:
|
||||
"translation": f"Мне нравится {word} каждый день."
|
||||
}
|
||||
|
||||
async def generate_thematic_words(self, theme: str, level: str = "B1", count: int = 10) -> List[Dict]:
|
||||
async def generate_thematic_words(self, theme: str, level: str = "B1", count: int = 10, learning_lang: str = "en", translation_lang: str = "ru") -> List[Dict]:
|
||||
"""
|
||||
Сгенерировать подборку слов по теме
|
||||
|
||||
@@ -218,17 +220,17 @@ class AIService:
|
||||
Returns:
|
||||
Список словарей с информацией о словах
|
||||
"""
|
||||
prompt = f"""Создай подборку из {count} английских слов по теме "{theme}" для уровня {level}.
|
||||
prompt = f"""Создай подборку из {count} слов на языке {learning_lang} по теме "{theme}" для уровня {level}. Переводы дай на {translation_lang}.
|
||||
|
||||
Верни ответ в формате JSON:
|
||||
{{
|
||||
"theme": "{theme}",
|
||||
"words": [
|
||||
{{
|
||||
"word": "английское слово",
|
||||
"translation": "перевод на русский",
|
||||
"transcription": "транскрипция в IPA",
|
||||
"example": "пример использования на английском"
|
||||
"word": "слово на {learning_lang}",
|
||||
"translation": "перевод на {translation_lang}",
|
||||
"transcription": "транскрипция в IPA (если применимо)",
|
||||
"example": "пример использования на {learning_lang}"
|
||||
}}
|
||||
]
|
||||
}}
|
||||
@@ -240,10 +242,10 @@ class AIService:
|
||||
- Разнообразными (существительные, глаголы, прилагательные)"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] generate_thematic_words: theme='{theme}', level='{level}', count={count}")
|
||||
logger.info(f"[GPT Request] generate_thematic_words: theme='{theme}', level='{level}', count={count}, learn='{learning_lang}', to='{translation_lang}'")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - преподаватель английского языка. Подбирай полезные и актуальные слова."},
|
||||
{"role": "system", "content": "Ты - преподаватель иностранных языков. Подбирай полезные и актуальные слова."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
@@ -259,7 +261,7 @@ class AIService:
|
||||
logger.error(f"[GPT Error] generate_thematic_words: {type(e).__name__}: {str(e)}")
|
||||
return []
|
||||
|
||||
async def extract_words_from_text(self, text: str, level: str = "B1", max_words: int = 15) -> List[Dict]:
|
||||
async def extract_words_from_text(self, text: str, level: str = "B1", max_words: int = 15, learning_lang: str = "en", translation_lang: str = "ru") -> List[Dict]:
|
||||
"""
|
||||
Извлечь ключевые слова из текста для изучения
|
||||
|
||||
@@ -271,7 +273,7 @@ class AIService:
|
||||
Returns:
|
||||
Список словарей с информацией о словах
|
||||
"""
|
||||
prompt = f"""Проанализируй следующий английский текст и извлеки из него до {max_words} самых полезных слов для изучения на уровне {level}.
|
||||
prompt = f"""Проанализируй следующий текст на языке {learning_lang} и извлеки из него до {max_words} самых полезных слов для изучения на уровне {level}. Переводы дай на {translation_lang}.
|
||||
|
||||
Текст:
|
||||
{text}
|
||||
@@ -280,10 +282,10 @@ class AIService:
|
||||
{{
|
||||
"words": [
|
||||
{{
|
||||
"word": "английское слово (в базовой форме)",
|
||||
"translation": "перевод на русский",
|
||||
"transcription": "транскрипция в IPA",
|
||||
"context": "предложение из текста, где используется это слово"
|
||||
"word": "слово на {learning_lang} (в базовой форме)",
|
||||
"translation": "перевод на {translation_lang}",
|
||||
"transcription": "транскрипция в IPA (если применимо)",
|
||||
"context": "предложение из текста на {learning_lang}, где используется это слово"
|
||||
}}
|
||||
]
|
||||
}}
|
||||
@@ -297,10 +299,10 @@ class AIService:
|
||||
|
||||
try:
|
||||
text_preview = text[:100] + "..." if len(text) > 100 else text
|
||||
logger.info(f"[GPT Request] extract_words_from_text: text_length={len(text)}, level='{level}', max_words={max_words}")
|
||||
logger.info(f"[GPT Request] extract_words_from_text: text_length={len(text)}, level='{level}', max_words={max_words}, learn='{learning_lang}', to='{translation_lang}'")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - преподаватель английского языка. Помогаешь извлекать полезные слова для изучения из текстов."},
|
||||
{"role": "system", "content": "Ты - преподаватель иностранных языков. Помогаешь извлекать полезные слова для изучения из текстов."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
@@ -316,7 +318,7 @@ class AIService:
|
||||
logger.error(f"[GPT Error] extract_words_from_text: {type(e).__name__}: {str(e)}")
|
||||
return []
|
||||
|
||||
async def start_conversation(self, scenario: str, level: str = "B1") -> Dict:
|
||||
async def start_conversation(self, scenario: str, level: str = "B1", learning_lang: str = "en", translation_lang: str = "ru") -> Dict:
|
||||
"""
|
||||
Начать диалоговую практику с AI
|
||||
|
||||
@@ -338,14 +340,14 @@ class AIService:
|
||||
|
||||
scenario_desc = scenarios.get(scenario, "повседневный разговор")
|
||||
|
||||
prompt = f"""Ты - собеседник для практики английского языка уровня {level}.
|
||||
Начни диалог в сценарии: {scenario_desc}.
|
||||
prompt = f"""Ты - собеседник для практики языка {learning_lang} уровня {level}.
|
||||
Начни диалог в сценарии: {scenario_desc} на {learning_lang}.
|
||||
|
||||
Верни ответ в формате JSON:
|
||||
{{
|
||||
"message": "твоя первая реплика на английском",
|
||||
"translation": "перевод на русский",
|
||||
"context": "краткое описание ситуации на русском",
|
||||
"message": "твоя первая реплика на {learning_lang}",
|
||||
"translation": "перевод на {translation_lang}",
|
||||
"context": "краткое описание ситуации на {translation_lang}",
|
||||
"suggestions": ["подсказка 1", "подсказка 2", "подсказка 3"]
|
||||
}}
|
||||
|
||||
@@ -356,10 +358,10 @@ class AIService:
|
||||
- Подсказки должны помочь пользователю ответить"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] start_conversation: scenario='{scenario}', level='{level}'")
|
||||
logger.info(f"[GPT Request] start_conversation: scenario='{scenario}', level='{level}', learn='{learning_lang}', to='{translation_lang}'")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - дружелюбный собеседник для практики английского. Веди естественный диалог."},
|
||||
{"role": "system", "content": "Ты - дружелюбный собеседник для практики иностранных языков. Веди естественный диалог."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
@@ -384,7 +386,9 @@ class AIService:
|
||||
conversation_history: List[Dict],
|
||||
user_message: str,
|
||||
scenario: str,
|
||||
level: str = "B1"
|
||||
level: str = "B1",
|
||||
learning_lang: str = "en",
|
||||
translation_lang: str = "ru"
|
||||
) -> Dict:
|
||||
"""
|
||||
Продолжить диалог и проверить ответ пользователя
|
||||
@@ -404,7 +408,7 @@ class AIService:
|
||||
for msg in conversation_history[-6:] # Последние 6 сообщений
|
||||
])
|
||||
|
||||
prompt = f"""Ты ведешь диалог на английском языке уровня {level} в сценарии "{scenario}".
|
||||
prompt = f"""Ты ведешь диалог на языке {learning_lang} уровня {level} в сценарии "{scenario}".
|
||||
|
||||
История диалога:
|
||||
{history_text}
|
||||
@@ -412,8 +416,8 @@ User: {user_message}
|
||||
|
||||
Верни ответ в формате JSON:
|
||||
{{
|
||||
"response": "твой ответ на английском",
|
||||
"translation": "перевод твоего ответа на русский",
|
||||
"response": "твой ответ на {learning_lang}",
|
||||
"translation": "перевод твоего ответа на {translation_lang}",
|
||||
"feedback": {{
|
||||
"has_errors": true/false,
|
||||
"corrections": "исправления ошибок пользователя (если есть)",
|
||||
@@ -429,11 +433,11 @@ User: {user_message}
|
||||
- Используй лексику уровня {level}"""
|
||||
|
||||
try:
|
||||
logger.info(f"[GPT Request] continue_conversation: scenario='{scenario}', level='{level}', history_length={len(conversation_history)}")
|
||||
logger.info(f"[GPT Request] continue_conversation: scenario='{scenario}', level='{level}', history_length={len(conversation_history)}, learn='{learning_lang}', to='{translation_lang}'")
|
||||
|
||||
# Формируем сообщения для API
|
||||
messages = [
|
||||
{"role": "system", "content": f"Ты - дружелюбный собеседник для практики английского языка уровня {level}. Веди естественный диалог и помогай исправлять ошибки."}
|
||||
{"role": "system", "content": f"Ты - дружелюбный собеседник для практики языка {learning_lang} уровня {level}. Веди естественный диалог и помогай исправлять ошибки."}
|
||||
]
|
||||
|
||||
# Добавляем историю
|
||||
|
||||
Reference in New Issue
Block a user