feat: unify multimodels translation providers, remove self-hosting (Ollama/LibreTranslate), and fix local SQLite configuration
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m21s

This commit is contained in:
2026-06-14 10:44:46 +02:00
parent feea02033b
commit 5fd087979b
21 changed files with 157 additions and 1942 deletions

View File

@@ -1030,6 +1030,13 @@ async def _run_translation_job(
source_lang=glossary_source_lang, target_lang=target_lang,
)
from services.providers.google_provider import GoogleTranslationProvider
from services.providers.google_cloud_provider import GoogleCloudTranslationProvider
from services.providers.deepl_provider import DeepLTranslationProvider
from services.providers.openai_provider import OpenAITranslationProvider
from services.providers.deepseek_provider import DeepSeekTranslationProvider
from services.providers.minimax_provider import MinimaxTranslationProvider
translation_provider = None
_p = provider.lower()
@@ -1037,24 +1044,34 @@ async def _run_translation_job(
# If the Cloud API key is invalid or the API is not enabled, fall back
# to the free legacy Google Translate (deep_translator) instead of failing.
if _p == "google":
# the user might have set GOOGLE_API_KEY instead of GOOGLE_CLOUD_API_KEY
gc_key = _cfg(
getattr(_admin_cfg.google_cloud, "api_key", None),
"GOOGLE_CLOUD_API_KEY",
) or os.getenv("GOOGLE_API_KEY", "").strip()
if gc_key and _google_cloud_key_valid(gc_key, job_id):
from services.providers.google_cloud_provider import LegacyGoogleCloudAdapter
translation_provider = LegacyGoogleCloudAdapter(gc_key)
translation_provider = GoogleCloudTranslationProvider(
api_key=gc_key,
timeout=int(os.getenv("GOOGLE_CLOUD_TIMEOUT", "30")),
max_retries=int(os.getenv("GOOGLE_CLOUD_MAX_RETRIES", "3")),
retry_delay=float(os.getenv("GOOGLE_CLOUD_RETRY_DELAY", "1.0")),
)
logger.info("google_provider_using_cloud_api", extra={"job_id": job_id})
else:
from services.translation_service import GoogleTranslationProvider
translation_provider = GoogleTranslationProvider()
translation_provider = GoogleTranslationProvider(
use_cache=True,
timeout=int(os.getenv("GOOGLE_TRANSLATE_TIMEOUT", "30")),
max_retries=int(os.getenv("GOOGLE_TRANSLATE_MAX_RETRIES", "3")),
retry_delay=float(os.getenv("GOOGLE_TRANSLATE_RETRY_DELAY", "1.0")),
)
logger.info("google_provider_using_legacy", extra={"job_id": job_id})
elif _p in ("openrouter", "llm") and api_key:
translation_provider = OpenRouterTranslationProvider(
api_key, model, full_prompt
translation_provider = OpenAITranslationProvider(
api_key=api_key,
model=model,
base_url="https://openrouter.ai/api/v1",
timeout=int(os.getenv("OPENROUTER_TIMEOUT", "60")),
)
elif _p == "openrouter_premium":
premium_key = _cfg(_admin_cfg.openrouter_premium.api_key, "OPENROUTER_API_KEY")
@@ -1062,71 +1079,58 @@ async def _run_translation_job(
if not premium_key:
premium_key = api_key # fall back to main openrouter key
if premium_key:
translation_provider = OpenRouterTranslationProvider(
premium_key, premium_model, full_prompt
translation_provider = OpenAITranslationProvider(
api_key=premium_key,
model=premium_model,
base_url="https://openrouter.ai/api/v1",
timeout=int(os.getenv("OPENROUTER_TIMEOUT", "60")),
)
elif _p == "openai":
from services.translation_service import OpenAITranslationProvider
openai_key = _cfg(_admin_cfg.openai.api_key, "OPENAI_API_KEY")
openai_model = _cfg(_admin_cfg.openai.model, "OPENAI_MODEL", "gpt-4o-mini")
if openai_key:
translation_provider = OpenAITranslationProvider(
api_key=openai_key,
model=openai_model,
system_prompt=full_prompt,
timeout=int(os.getenv("OPENAI_TIMEOUT", "60")),
)
elif _p == "deepseek":
from services.translation_service import OpenAITranslationProvider as _OAI
ds_key = _cfg(getattr(_admin_cfg, "deepseek", None) and _admin_cfg.deepseek.api_key, "DEEPSEEK_API_KEY")
ds_model = _cfg(getattr(_admin_cfg, "deepseek", None) and _admin_cfg.deepseek.model, "DEEPSEEK_MODEL", "deepseek-chat")
ds_url = "https://api.deepseek.com/v1"
if ds_key:
translation_provider = _OAI(
translation_provider = DeepSeekTranslationProvider(
api_key=ds_key,
model=ds_model,
base_url=ds_url,
system_prompt=full_prompt,
timeout=int(os.getenv("DEEPSEEK_TIMEOUT", "60")),
)
elif _p == "minimax":
from services.translation_service import OpenAITranslationProvider as _OAI
mm_key = _cfg(getattr(_admin_cfg, "minimax", None) and _admin_cfg.minimax.api_key, "MINIMAX_API_KEY")
mm_model = _cfg(getattr(_admin_cfg, "minimax", None) and _admin_cfg.minimax.model, "MINIMAX_MODEL", "abab6.5s-chat")
mm_url = "https://api.minimax.chat/v1"
mm_model = _cfg(getattr(_admin_cfg, "minimax", None) and _admin_cfg.minimax.model, "MINIMAX_MODEL", "MiniMax-M1")
if mm_key:
translation_provider = _OAI(
translation_provider = MinimaxTranslationProvider(
api_key=mm_key,
model=mm_model,
base_url=mm_url,
system_prompt=full_prompt,
timeout=int(os.getenv("MINIMAX_TIMEOUT", "60")),
)
elif _p == "deepl":
deepl_key = _cfg(_admin_cfg.deepl.api_key, "DEEPL_API_KEY")
if deepl_key:
from services.translation_service import DeepLTranslationProvider
translation_provider = DeepLTranslationProvider(deepl_key)
translation_provider = DeepLTranslationProvider(
api_key=deepl_key,
timeout=int(os.getenv("DEEPL_TIMEOUT", "30")),
)
elif _p == "zai":
from services.translation_service import OpenAITranslationProvider as _OAI
zai_key = _cfg(_admin_cfg.zai.api_key, "ZAI_API_KEY")
zai_model = _cfg(_admin_cfg.zai.model, "ZAI_MODEL", "grok-2-1212")
zai_url = _cfg(_admin_cfg.zai.base_url, "ZAI_BASE_URL", "https://api.x.ai/v1")
if zai_key:
translation_provider = _OAI(
translation_provider = OpenAITranslationProvider(
api_key=zai_key,
model=zai_model,
base_url=zai_url,
system_prompt=full_prompt,
timeout=int(os.getenv("ZAI_TIMEOUT", "60")),
)
elif _p == "ollama":
ollama_url = _cfg(_admin_cfg.ollama.base_url, "OLLAMA_BASE_URL", "http://localhost:11434")
ollama_model = _cfg(_admin_cfg.ollama.model, "OLLAMA_MODEL", "llama3")
translation_provider = OllamaTranslationProvider(
ollama_url,
ollama_model,
ollama_model,
full_prompt,
)
elif _p == "google_cloud":
from services.providers.google_cloud_provider import GoogleCloudTranslationProvider
gc_key = _cfg(
getattr(_admin_cfg.google_cloud, "api_key", None),
"GOOGLE_CLOUD_API_KEY",
@@ -1147,7 +1151,6 @@ async def _run_translation_job(
"google_cloud_key_missing_fallback_to_google",
extra={"job_id": job_id},
)
# translation_provider reste None → legacy Google gratuit
tracker.update(20, "Preparing translation")
@@ -1194,6 +1197,8 @@ async def _run_translation_job(
if file_extension == ".xlsx":
logger.info(f"DEBUG: ExcelTranslator class is {ExcelTranslator} and translate_file is {ExcelTranslator.translate_file}")
job_translator = ExcelTranslator(provider=translation_provider)
if hasattr(job_translator, "set_custom_prompt"):
job_translator.set_custom_prompt(full_prompt)
await asyncio.to_thread(
job_translator.translate_file,
input_path,
@@ -1205,6 +1210,8 @@ async def _run_translation_job(
)
elif file_extension == ".docx":
job_translator = WordTranslator(provider=translation_provider)
if hasattr(job_translator, "set_custom_prompt"):
job_translator.set_custom_prompt(full_prompt)
await asyncio.to_thread(
job_translator.translate_file,
input_path,
@@ -1216,6 +1223,8 @@ async def _run_translation_job(
)
elif file_extension == ".pptx":
job_translator = PowerPointTranslator(provider=translation_provider)
if hasattr(job_translator, "set_custom_prompt"):
job_translator.set_custom_prompt(full_prompt)
await asyncio.to_thread(
job_translator.translate_file,
input_path,
@@ -1228,6 +1237,8 @@ async def _run_translation_job(
elif file_extension == ".pdf":
from translators.pdf_translator import PDFTranslator
job_translator = PDFTranslator(provider=translation_provider)
if hasattr(job_translator, "set_custom_prompt"):
job_translator.set_custom_prompt(full_prompt)
actual_output = await asyncio.to_thread(
job_translator.translate_file,
input_path,