- Frontend: Next.js 14 with TypeScript - Backend: FastAPI with SQLAlchemy - Agent: Carmodoo sync agent - Deployment: Docker Compose based staging/production setup - Scripts: Automated deployment with rollback support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
175 lines
6.0 KiB
Python
175 lines
6.0 KiB
Python
"""
|
|
Azure Translator Service for dealer descriptions
|
|
Supports Korean → English, Mongolian, Russian direct translation
|
|
"""
|
|
import os
|
|
import httpx
|
|
from typing import Optional, Dict
|
|
import json
|
|
|
|
|
|
class AzureTranslationService:
|
|
"""Microsoft Azure Translator API Service"""
|
|
|
|
AZURE_ENDPOINT = "https://api.cognitive.microsofttranslator.com"
|
|
API_VERSION = "3.0"
|
|
|
|
def __init__(self):
|
|
self.api_key = os.getenv("AZURE_TRANSLATOR_KEY", "")
|
|
self.region = os.getenv("AZURE_TRANSLATOR_REGION", "koreacentral")
|
|
self._is_configured = bool(self.api_key)
|
|
|
|
@property
|
|
def is_configured(self) -> bool:
|
|
"""Check if Azure Translator API is configured"""
|
|
return self._is_configured
|
|
|
|
async def translate(self, text: str, target_lang: str, source_lang: str = "ko") -> Optional[str]:
|
|
"""
|
|
Translate text from source language to target language
|
|
|
|
Args:
|
|
text: Text to translate (Korean)
|
|
target_lang: Target language code (en, mn, ru)
|
|
source_lang: Source language code (default: ko)
|
|
|
|
Returns:
|
|
Translated text or None if failed
|
|
"""
|
|
if not self._is_configured:
|
|
print("[Translation] Azure Translator API not configured")
|
|
return None
|
|
|
|
if not text or not text.strip():
|
|
return ""
|
|
|
|
try:
|
|
url = f"{self.AZURE_ENDPOINT}/translate"
|
|
params = {
|
|
"api-version": self.API_VERSION,
|
|
"from": source_lang,
|
|
"to": target_lang
|
|
}
|
|
headers = {
|
|
"Ocp-Apim-Subscription-Key": self.api_key,
|
|
"Ocp-Apim-Subscription-Region": self.region,
|
|
"Content-Type": "application/json"
|
|
}
|
|
body = [{"text": text}]
|
|
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
response = await client.post(
|
|
url,
|
|
params=params,
|
|
headers=headers,
|
|
json=body
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result and len(result) > 0 and "translations" in result[0]:
|
|
translated = result[0]["translations"][0]["text"]
|
|
print(f"[Translation] Success: {source_lang} -> {target_lang}")
|
|
return translated
|
|
else:
|
|
error_msg = response.text
|
|
print(f"[Translation] API Error ({response.status_code}): {error_msg}")
|
|
return None
|
|
|
|
except Exception as e:
|
|
print(f"[Translation] Exception: {e}")
|
|
return None
|
|
|
|
async def translate_all_languages(self, text: str) -> Dict[str, Optional[str]]:
|
|
"""
|
|
Translate text to all supported languages (en, mn, ru) in a single API call
|
|
|
|
Args:
|
|
text: Korean text to translate
|
|
|
|
Returns:
|
|
Dictionary with translations: {
|
|
'en': '...',
|
|
'mn': '...',
|
|
'ru': '...'
|
|
}
|
|
"""
|
|
if not text or not text.strip():
|
|
return {'en': '', 'mn': '', 'ru': ''}
|
|
|
|
if not self._is_configured:
|
|
print("[Translation] Azure Translator API not configured")
|
|
return {'en': None, 'mn': None, 'ru': None}
|
|
|
|
try:
|
|
# Azure supports multiple target languages in a single call
|
|
url = f"{self.AZURE_ENDPOINT}/translate"
|
|
params = {
|
|
"api-version": self.API_VERSION,
|
|
"from": "ko",
|
|
"to": ["en", "mn", "ru"] # All three languages at once
|
|
}
|
|
headers = {
|
|
"Ocp-Apim-Subscription-Key": self.api_key,
|
|
"Ocp-Apim-Subscription-Region": self.region,
|
|
"Content-Type": "application/json"
|
|
}
|
|
body = [{"text": text}]
|
|
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
response = await client.post(
|
|
url,
|
|
params=params,
|
|
headers=headers,
|
|
json=body
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result and len(result) > 0 and "translations" in result[0]:
|
|
translations = result[0]["translations"]
|
|
result_dict = {'en': None, 'mn': None, 'ru': None}
|
|
|
|
for trans in translations:
|
|
lang = trans.get("to")
|
|
text_translated = trans.get("text")
|
|
if lang in result_dict:
|
|
result_dict[lang] = text_translated
|
|
|
|
print(f"[Translation] Success: ko -> en, mn, ru (batch)")
|
|
return result_dict
|
|
else:
|
|
error_msg = response.text
|
|
print(f"[Translation] API Error ({response.status_code}): {error_msg}")
|
|
return {'en': None, 'mn': None, 'ru': None}
|
|
|
|
except Exception as e:
|
|
print(f"[Translation] Exception: {e}")
|
|
return {'en': None, 'mn': None, 'ru': None}
|
|
|
|
|
|
# Singleton instance
|
|
_translation_service: Optional[AzureTranslationService] = None
|
|
|
|
|
|
def get_translation_service() -> AzureTranslationService:
|
|
"""Get or create the translation service singleton"""
|
|
global _translation_service
|
|
if _translation_service is None:
|
|
_translation_service = AzureTranslationService()
|
|
return _translation_service
|
|
|
|
|
|
async def translate_dealer_description(text: str) -> Dict[str, Optional[str]]:
|
|
"""
|
|
Convenience function to translate dealer description to all languages
|
|
|
|
Args:
|
|
text: Korean dealer description
|
|
|
|
Returns:
|
|
Dictionary with translations for en, mn, ru
|
|
"""
|
|
service = get_translation_service()
|
|
return await service.translate_all_languages(text)
|