feat(translate): refonte du design de la page de traduction et du sélecteur de moteurs (Etapes 1-3)
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m12s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m12s
This commit is contained in:
@@ -886,6 +886,70 @@ RULES:
|
||||
|
||||
return results
|
||||
|
||||
def translate_image(self, image_path: str, target_language: str) -> str:
|
||||
"""Translate text within an image using OpenRouter vision model"""
|
||||
import base64
|
||||
|
||||
try:
|
||||
# Read and encode image
|
||||
with open(image_path, "rb") as img_file:
|
||||
image_data = base64.b64encode(img_file.read()).decode("utf-8")
|
||||
|
||||
# Determine image type from extension
|
||||
ext = image_path.lower().split(".")[-1]
|
||||
media_type = (
|
||||
f"image/{ext}"
|
||||
if ext in ["png", "jpg", "jpeg", "gif", "webp"]
|
||||
else "image/png"
|
||||
)
|
||||
|
||||
# Determine a vision model. If the current model doesn't support vision,
|
||||
# use a fast vision fallback model like google/gemini-2.0-flash-001
|
||||
vision_model = self.model
|
||||
if "deepseek" in vision_model:
|
||||
vision_model = "google/gemini-2.0-flash-001"
|
||||
|
||||
session = self._get_session()
|
||||
payload = {
|
||||
"model": vision_model,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"Extract all text from this image and translate it to {target_language}. Return ONLY the translated text, preserving the structure and formatting.",
|
||||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:{media_type};base64,{image_data}"
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
"max_tokens": 1000,
|
||||
}
|
||||
|
||||
response = session.post(
|
||||
f"{self.base_url}/chat/completions",
|
||||
json=payload,
|
||||
timeout=60,
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
translated = (
|
||||
result.get("choices", [{}])[0]
|
||||
.get("message", {})
|
||||
.get("content", "")
|
||||
.strip()
|
||||
)
|
||||
return translated
|
||||
except Exception as e:
|
||||
logger.warning("openrouter_vision_error", error_type=type(e).__name__, error=str(e))
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def list_recommended_models() -> List[dict]:
|
||||
"""List recommended models for translation with pricing"""
|
||||
@@ -1111,11 +1175,13 @@ class TranslationService:
|
||||
if not self.translate_images:
|
||||
return ""
|
||||
|
||||
# Ollama and OpenAI support image translation
|
||||
# Ollama, OpenAI, and OpenRouter support image translation
|
||||
if isinstance(self.provider, OllamaTranslationProvider):
|
||||
return self.provider.translate_image(image_path, target_language)
|
||||
elif isinstance(self.provider, OpenAITranslationProvider):
|
||||
return self.provider.translate_image(image_path, target_language)
|
||||
elif isinstance(self.provider, OpenRouterTranslationProvider):
|
||||
return self.provider.translate_image(image_path, target_language)
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user