173 lines
6.1 KiB
Python
173 lines
6.1 KiB
Python
"""
|
|
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()
|