File size: 6,071 Bytes
93f4eb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from googletrans import Translator, LANGUAGES
import asyncio
from typing import List, Dict, Optional, Tuple
import logging

logger = logging.getLogger(__name__)

class TranslationService:
    def __init__(self):
        self.translator = Translator()
        self.languages = LANGUAGES
        # Кэш для обратного поиска языков
        self._name_to_code_cache = None
        
    def _build_name_cache(self):
        """Построить кэш для поиска кода языка по названию"""
        if self._name_to_code_cache is None:
            self._name_to_code_cache = {}
            for code, name in self.languages.items():
                self._name_to_code_cache[name.lower()] = code
                # Добавляем русские названия
                russian_names = {
                    'russian': 'ru', 'английский': 'en', 'немецкий': 'de',
                    'французский': 'fr', 'испанский': 'es', 'итальянский': 'it',
                    'португальский': 'pt', 'китайский': 'zh-cn', 'японский': 'ja',
                    'корейский': 'ko', 'арабский': 'ar', 'турецкий': 'tr'
                }
                self._name_to_code_cache.update(russian_names)
        return self._name_to_code_cache
    
    def get_language_code(self, lang: str) -> str:
        """Получить код языка из названия или кода"""
        if not lang or lang == 'auto':
            return 'auto'
        
        # Если это уже код языка
        if lang in self.languages:
            return lang
        
        # Ищем по названию
        name_cache = self._build_name_cache()
        return name_cache.get(lang.lower(), 'auto')
    
    def get_language_name(self, code: str) -> str:
        """Получить название языка по коду"""
        return self.languages.get(code, code)
    
    async def translate(self, text: str, dest: str = 'ru', src: str = 'auto') -> Dict:
        """Асинхронный перевод текста"""
        try:
            # Получаем коды языков
            dest_code = self.get_language_code(dest)
            src_code = self.get_language_code(src) if src != 'auto' else 'auto'
            
            # Выполняем перевод в отдельном потоке, так как googletrans не асинхронный
            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(
                None,
                lambda: self.translator.translate(text, src=src_code, dest=dest_code)
            )
            
            # Получаем альтернативные переводы (если есть)
            alternatives = []
            if hasattr(result, 'extra_data') and result.extra_data:
                possible_translations = result.extra_data.get('possible-translations', [])
                if possible_translations and len(possible_translations) > 0:
                    for translation_group in possible_translations[:3]:
                        if translation_group and len(translation_group) > 0:
                            for translation_item in translation_group[:2]:
                                if isinstance(translation_item, list) and len(translation_item) > 1:
                                    alternatives.append(translation_item[1])
            
            return {
                'original_text': result.origin,
                'translated_text': result.text,
                'source_lang': result.src,
                'target_lang': dest_code,
                'source_lang_name': self.get_language_name(result.src),
                'target_lang_name': self.get_language_name(dest_code),
                'pronunciation': result.pronunciation,
                'alternatives': alternatives[:5] if alternatives else None
            }
        except Exception as e:
            logger.error(f"Translation error: {str(e)}")
            raise Exception(f"Ошибка перевода: {str(e)}")
    
    async def translate_batch(self, texts: List[str], dest: str = 'ru', src: str = 'auto') -> List[Dict]:
        """Пакетный перевод текстов"""
        tasks = [self.translate(text, dest, src) for text in texts]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        translations = []
        for result in results:
            if isinstance(result, Exception):
                translations.append({
                    'error': str(result),
                    'original_text': '',
                    'translated_text': ''
                })
            else:
                translations.append(result)
        
        return translations
    
    async def detect_language(self, text: str) -> Dict:
        """Определение языка текста"""
        try:
            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(
                None,
                lambda: self.translator.detect(text)
            )
            
            return {
                'lang': result.lang,
                'lang_name': self.get_language_name(result.lang),
                'confidence': result.confidence
            }
        except Exception as e:
            logger.error(f"Language detection error: {str(e)}")
            raise Exception(f"Ошибка определения языка: {str(e)}")
    
    def get_supported_languages(self) -> List[Dict]:
        """Получить список поддерживаемых языков"""
        languages = []
        for code, name in sorted(self.languages.items()):
            languages.append({
                'code': code,
                'name': name,
                'native_name': name  # Можно добавить маппинг на родные названия
            })
        return languages