|
|
| import re |
| from typing import Dict, Tuple, Optional |
| from sentence_transformers import SentenceTransformer |
| from langdetect import detect, detect_langs |
| from langdetect.lang_detect_exception import LangDetectException |
| from config.settings import settings |
|
|
| class MultilingualManager: |
| def __init__(self): |
| self.vietnamese_model = None |
| self.multilingual_model = None |
| self.current_language = 'vi' |
| self._initialize_models() |
|
|
| def _initialize_models(self): |
| """Khởi tạo các mô hình đa ngôn ngữ""" |
| try: |
| print("🔄 Đang tải mô hình embedding tiếng Việt...") |
| self.vietnamese_model = SentenceTransformer(settings.VIETNAMESE_EMBEDDING_MODEL) |
| print("✅ Đã tải mô hình embedding tiếng Việt") |
| except Exception as e: |
| print(f"❌ Lỗi tải mô hình embedding tiếng Việt: {e}") |
| |
| self.vietnamese_model = None |
| |
| try: |
| print("🔄 Đang tải mô hình embedding đa ngôn ngữ...") |
| self.multilingual_model = SentenceTransformer( |
| settings.MULTILINGUAL_EMBEDDING_MODEL, |
| trust_remote_code=True |
| ) |
| print("✅ Đã tải mô hình embedding đa ngôn ngữ") |
| except Exception as e: |
| print(f"❌ Lỗi tải mô hình embedding đa ngôn ngữ: {e}") |
| |
| try: |
| self.multilingual_model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2') |
| print("✅ Đã tải mô hình fallback đa ngôn ngữ") |
| except Exception as fallback_error: |
| print(f"❌ Lỗi tải mô hình fallback: {fallback_error}") |
| self.multilingual_model = None |
|
|
| def detect_language(self, text: str, fallback_method: bool = True) -> str: |
| """ |
| Phát hiện ngôn ngữ với độ chính xác cao sử dụng langdetect |
| """ |
| if not text or len(text.strip()) == 0: |
| return 'vi' |
| |
| |
| text = self._clean_text_for_detection(text) |
| |
| if len(text.strip()) < 3: |
| return 'vi' |
| |
| try: |
| |
| languages = detect_langs(text) |
| |
| |
| best_lang = str(languages[0]).split(':')[0] |
| |
| |
| lang_map = { |
| 'vi': 'vi', 'en': 'en', 'fr': 'fr', 'es': 'es', |
| 'de': 'de', 'ja': 'ja', 'ko': 'ko', 'zh-cn': 'zh', 'zh-tw': 'zh', |
| 'it': 'en', 'pt': 'en', 'ru': 'en', 'ar': 'en' |
| } |
| |
| detected_lang = lang_map.get(best_lang, 'en') |
| |
| |
| confidence = float(str(languages[0]).split(':')[1]) |
| if confidence < 0.6 and fallback_method: |
| |
| return self._fallback_language_detection(text) |
| |
| print(f"🔍 Phát hiện ngôn ngữ: {detected_lang} (độ tin cậy: {confidence:.2f})") |
| return detected_lang |
| |
| except LangDetectException as e: |
| print(f"⚠️ LangDetect lỗi, sử dụng fallback: {e}") |
| return self._fallback_language_detection(text) if fallback_method else 'vi' |
| except Exception as e: |
| print(f"⚠️ Lỗi phát hiện ngôn ngữ: {e}") |
| return self._fallback_language_detection(text) if fallback_method else 'vi' |
|
|
| def _fallback_language_detection(self, text: str) -> str: |
| """ |
| Fallback method sử dụng các quy tắc heuristic |
| """ |
| text_lower = text.lower() |
| |
| |
| vietnamese_indicators = [ |
| 'của', 'và', 'là', 'có', 'được', 'trong', 'cho', 'với', 'như', 'tôi', |
| 'bạn', 'ông', 'bà', 'anh', 'chị', 'em', 'này', 'kia', 'đó', 'đây', |
| 'không', 'có', 'phải', 'rất', 'nhất', 'các', 'những', 'một', 'hai', 'ba' |
| ] |
| |
| english_indicators = [ |
| 'the', 'and', 'is', 'are', 'for', 'with', 'this', 'that', 'you', 'they', |
| 'what', 'where', 'when', 'why', 'how', 'which', 'who', 'their', 'have', 'has', |
| 'from', 'your', 'will', 'would', 'could', 'should', 'about', 'into', 'through' |
| ] |
| |
| |
| vi_count = sum(1 for word in vietnamese_indicators if word in text_lower) |
| en_count = sum(1 for word in english_indicators if word in text_lower) |
| |
| |
| vietnamese_chars = set('àáâãèéêìíòóôõùúýăđĩũơưạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ') |
| vi_char_count = sum(1 for char in text if char in vietnamese_chars) |
| |
| |
| if vi_count > en_count or vi_char_count > 2: |
| return 'vi' |
| elif en_count > vi_count: |
| return 'en' |
| else: |
| |
| if any(char in text for char in 'あいうえおぁ-んァ-ン'): |
| return 'ja' |
| elif any(char in text for char in '你好'): |
| return 'zh' |
| elif any(char in text for char in '안녕'): |
| return 'ko' |
| elif any(char in text for char in 'àâæçèéêëîïôœùûüÿ'): |
| return 'fr' |
| elif any(char in text for char in 'áéíóúñü'): |
| return 'es' |
| elif any(char in text for char in 'äöüß'): |
| return 'de' |
| else: |
| return 'en' |
|
|
| def _clean_text_for_detection(self, text: str) -> str: |
| """Làm sạch văn bản để phát hiện ngôn ngữ chính xác hơn""" |
| |
| text = re.sub(r'http\S+', '', text) |
| text = re.sub(r'[0-9]+', '', text) |
| text = re.sub(r'[^\w\s]', ' ', text) |
| text = re.sub(r'\s+', ' ', text) |
| return text.strip() |
|
|
| def detect_language_with_confidence(self, text: str) -> Tuple[str, float]: |
| """ |
| Phát hiện ngôn ngữ với điểm tin cậy |
| """ |
| if not text or len(text.strip()) < 3: |
| return 'vi', 0.0 |
| |
| try: |
| languages = detect_langs(text) |
| best_lang = str(languages[0]) |
| lang_code, confidence = best_lang.split(':') |
| |
| lang_map = { |
| 'vi': 'vi', 'en': 'en', 'fr': 'fr', 'es': 'es', |
| 'de': 'de', 'ja': 'ja', 'ko': 'ko', 'zh-cn': 'zh', 'zh-tw': 'zh' |
| } |
| |
| detected_lang = lang_map.get(lang_code, 'en') |
| confidence_score = float(confidence) |
| |
| return detected_lang, confidence_score |
| |
| except LangDetectException as e: |
| print(f"⚠️ Lỗi phát hiện ngôn ngữ với confidence: {e}") |
| return 'vi', 0.5 |
| except Exception as e: |
| print(f"⚠️ Lỗi phát hiện ngôn ngữ với confidence: {e}") |
| return 'vi', 0.5 |
|
|
| def detect_language_simple(self, text: str) -> str: |
| """ |
| Phát hiện ngôn ngữ đơn giản (nhanh hơn, ít chính xác hơn) |
| """ |
| if not text or len(text.strip()) < 3: |
| return 'vi' |
| |
| try: |
| |
| lang_code = detect(text) |
| |
| lang_map = { |
| 'vi': 'vi', 'en': 'en', 'fr': 'fr', 'es': 'es', |
| 'de': 'de', 'ja': 'ja', 'ko': 'ko', 'zh-cn': 'zh', 'zh-tw': 'zh' |
| } |
| |
| return lang_map.get(lang_code, 'en') |
| |
| except LangDetectException: |
| return self._fallback_language_detection(text) |
| except Exception: |
| return 'vi' |
|
|
| def get_embedding_model(self, language: str = None) -> Optional[SentenceTransformer]: |
| """Lấy mô hình embedding dựa trên ngôn ngữ đã phát hiện""" |
| if language and language in settings.SUPPORTED_LANGUAGES: |
| lang = language |
| else: |
| lang = self.current_language |
| |
| if lang == 'vi' and self.vietnamese_model is not None: |
| return self.vietnamese_model |
| else: |
| return self.multilingual_model |
|
|
| def get_llm_model(self, language: str = None) -> str: |
| """Lấy tên mô hình LLM dựa trên ngôn ngữ đã phát hiện""" |
| if language and language in settings.SUPPORTED_LANGUAGES: |
| lang = language |
| else: |
| lang = self.current_language |
| |
| if lang == 'vi': |
| return settings.VIETNAMESE_LLM_MODEL |
| else: |
| return settings.MULTILINGUAL_LLM_MODEL |
|
|
| def get_language_info(self, language: str = None) -> Dict: |
| """Lấy thông tin ngôn ngữ bao gồm mã và tên đầy đủ""" |
| if language and language in settings.SUPPORTED_LANGUAGES: |
| lang = language |
| else: |
| lang = self.current_language |
| |
| language_names = { |
| 'vi': 'Tiếng Việt', |
| 'en': 'English', |
| 'fr': 'Français', |
| 'es': 'Español', |
| 'de': 'Deutsch', |
| 'ja': '日本語', |
| 'ko': '한국어', |
| 'zh': '中文' |
| } |
| |
| return { |
| 'code': lang, |
| 'name': language_names.get(lang, 'Unknown'), |
| 'embedding_model': settings.VIETNAMESE_EMBEDDING_MODEL if lang == 'vi' else settings.MULTILINGUAL_EMBEDDING_MODEL, |
| 'llm_model': settings.VIETNAMESE_LLM_MODEL if lang == 'vi' else settings.MULTILINGUAL_LLM_MODEL, |
| 'embedding_status': 'active' if (self.vietnamese_model if lang == 'vi' else self.multilingual_model) else 'inactive' |
| } |
|
|
| def get_supported_languages(self) -> Dict[str, str]: |
| """Lấy danh sách ngôn ngữ được hỗ trợ""" |
| return { |
| 'vi': 'Tiếng Việt', |
| 'en': 'English', |
| 'fr': 'Français', |
| 'es': 'Español', |
| 'de': 'Deutsch', |
| 'ja': '日本語', |
| 'ko': '한국어', |
| 'zh': '中文' |
| } |
|
|
| def is_language_supported(self, language: str) -> bool: |
| """Kiểm tra xem ngôn ngữ có được hỗ trợ không""" |
| return language in self.get_supported_languages() |