""" Translation Service Abstraction Provides a unified interface for different translation providers """ from abc import ABC, abstractmethod from typing import Optional, List import requests from deep_translator import GoogleTranslator, DeeplTranslator, LibreTranslator from config import config class TranslationProvider(ABC): """Abstract base class for translation providers""" @abstractmethod def translate(self, text: str, target_language: str, source_language: str = 'auto') -> str: """Translate text from source to target language""" pass class GoogleTranslationProvider(TranslationProvider): """Google Translate implementation""" def translate(self, text: str, target_language: str, source_language: str = 'auto') -> str: if not text or not text.strip(): return text try: translator = GoogleTranslator(source=source_language, target=target_language) return translator.translate(text) except Exception as e: print(f"Translation error: {e}") return text class DeepLTranslationProvider(TranslationProvider): """DeepL Translate implementation""" def __init__(self, api_key: str): self.api_key = api_key def translate(self, text: str, target_language: str, source_language: str = 'auto') -> str: if not text or not text.strip(): return text try: translator = DeeplTranslator(api_key=self.api_key, source=source_language, target=target_language) return translator.translate(text) except Exception as e: print(f"Translation error: {e}") return text class LibreTranslationProvider(TranslationProvider): """LibreTranslate implementation""" def translate(self, text: str, target_language: str, source_language: str = 'auto') -> str: if not text or not text.strip(): return text try: translator = LibreTranslator(source=source_language, target=target_language) return translator.translate(text) except Exception as e: print(f"Translation error: {e}") return text class OllamaTranslationProvider(TranslationProvider): """Ollama LLM translation implementation""" def __init__(self, base_url: str = "http://localhost:11434", model: str = "llama3"): self.base_url = base_url.rstrip('/') self.model = model def translate(self, text: str, target_language: str, source_language: str = 'auto') -> str: if not text or not text.strip(): return text try: prompt = f"Translate the following text to {target_language}. Return ONLY the translation, nothing else:\n\n{text}" response = requests.post( f"{self.base_url}/api/generate", json={ "model": self.model, "prompt": prompt, "stream": False }, timeout=30 ) response.raise_for_status() result = response.json() return result.get("response", text).strip() except Exception as e: print(f"Ollama translation error: {e}") return text @staticmethod def list_models(base_url: str = "http://localhost:11434") -> List[str]: """List available Ollama models""" try: response = requests.get(f"{base_url.rstrip('/')}/api/tags", timeout=5) response.raise_for_status() models = response.json().get("models", []) return [model["name"] for model in models] except Exception as e: print(f"Error listing Ollama models: {e}") return [] class TranslationService: """Main translation service that delegates to the configured provider""" def __init__(self, provider: Optional[TranslationProvider] = None): if provider: self.provider = provider else: # Auto-select provider based on configuration self.provider = self._get_default_provider() def _get_default_provider(self) -> TranslationProvider: """Get the default translation provider from configuration""" service_type = config.TRANSLATION_SERVICE.lower() if service_type == "deepl": if not config.DEEPL_API_KEY: raise ValueError("DeepL API key not configured") return DeepLTranslationProvider(config.DEEPL_API_KEY) elif service_type == "libre": return LibreTranslationProvider() elif service_type == "ollama": ollama_url = getattr(config, 'OLLAMA_BASE_URL', 'http://localhost:11434') ollama_model = getattr(config, 'OLLAMA_MODEL', 'llama3') return OllamaTranslationProvider(base_url=ollama_url, model=ollama_model) else: # Default to Google return GoogleTranslationProvider() def translate_text(self, text: str, target_language: str, source_language: str = 'auto') -> str: """ Translate a single text string Args: text: Text to translate target_language: Target language code (e.g., 'es', 'fr', 'de') source_language: Source language code (default: 'auto' for auto-detection) Returns: Translated text """ if not text or not text.strip(): return text return self.provider.translate(text, target_language, source_language) def translate_batch(self, texts: list[str], target_language: str, source_language: str = 'auto') -> list[str]: """ Translate multiple text strings Args: texts: List of texts to translate target_language: Target language code source_language: Source language code (default: 'auto') Returns: List of translated texts """ return [self.translate_text(text, target_language, source_language) for text in texts] # Global translation service instance translation_service = TranslationService()