"""
title: WebUI Auto Translator
description: Automatically translates both messages and UI elements
version: 1.0.0
requirements: deep-translator,beautifulsoup4
"""
import json
import hashlib
from typing import Optional, Dict, Any, Union
from deep_translator import GoogleTranslator
from bs4 import BeautifulSoup
class TranslationError(Exception):
"""Custom exception for translation errors"""
pass
class Tools:
def __init__(self):
self.translation_cache = {}
self.translator = GoogleTranslator(source="auto", target="fr")
self.cache_timeout = 3600 # 1 hour
def _get_cache_key(self, text: str, target_language: str, prefix: str = "") -> str:
"""Generate a unique cache key"""
cache_key = f"{prefix}:{text}:{target_language}"
return hashlib.md5(cache_key.encode()).hexdigest()
def safe_translate(self, text: str) -> str:
"""Safely translate text with error handling"""
if not text or not isinstance(text, str):
return text
if len(text) > 5000:
# Split text into smaller chunks if needed
chunks = [text[i : i + 5000] for i in range(0, len(text), 5000)]
return " ".join(self.safe_translate(chunk) for chunk in chunks)
try:
result = self.translator.translate(text)
if not result:
raise TranslationError("Empty translation result")
return result
except Exception as e:
print(f"Translation error: {str(e)}")
return text
def translate_message(self, text: str, target_language: str = "fr") -> str:
"""Translate chat message"""
if not text:
return text
cache_key = self._get_cache_key(text, target_language, "msg")
if cache_key in self.translation_cache:
return self.translation_cache[cache_key]
try:
self.translator.target = target_language
result = self.safe_translate(text)
self.translation_cache[cache_key] = result
return result
except Exception as e:
print(f"Message translation error: {str(e)}")
return text
def process_chat_message(self, message: dict) -> dict:
"""Process and translate chat messages"""
try:
if isinstance(message, dict):
if "content" in message:
message["content"] = self.translate_message(message["content"])
return message
except Exception as e:
print(f"Chat message processing error: {str(e)}")
return message
def process_stream(self, message: str) -> str:
"""Process streaming messages"""
try:
return self.translate_message(message)
except Exception as e:
print(f"Stream processing error: {str(e)}")
return message
def translate_ui_element(
self, element_text: str, target_language: str = "fr"
) -> str:
"""Translate UI element text"""
if not element_text or not isinstance(element_text, str):
return element_text
text = element_text.strip()
if not text:
return element_text
cache_key = self._get_cache_key(text, target_language, "ui")
if cache_key in self.translation_cache:
return self.translation_cache[cache_key]
try:
self.translator.target = target_language
result = self.safe_translate(text)
self.translation_cache[cache_key] = result
return result
except Exception as e:
print(f"UI element translation error: {str(e)}")
return element_text
def translate_ui(self, html_content: str, target_language: str = "fr") -> str:
"""Translate entire UI content"""
if not html_content:
return html_content
try:
soup = BeautifulSoup(html_content, "html.parser")
skip_tags = Valves.excluded_elements
translatable_attrs = ["placeholder", "title", "aria-label"]
for text_node in soup.find_all(text=True):
if text_node.parent.name not in skip_tags:
text = text_node.strip()
if text:
translated_text = self.translate_ui_element(
text, target_language
)
if translated_text and translated_text != text:
text_node.replace_with(translated_text)
for element in soup.find_all():
if element.name not in skip_tags:
for attr in translatable_attrs:
if element.get(attr):
translated_attr = self.translate_ui_element(
element[attr], target_language
)
if translated_attr:
element[attr] = translated_attr
return str(soup)
except Exception as e:
print(f"UI translation error: {str(e)}")
return html_content
def translate_json_content(
self, json_content: Dict[str, Any], target_language: str = "fr"
) -> Dict[str, Any]:
"""Translate JSON content"""
if not json_content:
return json_content
def translate_value(value):
if isinstance(value, str):
return self.translate_ui_element(value, target_language)
elif isinstance(value, dict):
return {k: translate_value(v) for k, v in value.items()}
elif isinstance(value, list):
return [translate_value(item) for item in value]
return value
try:
return translate_value(json_content)
except Exception as e:
print(f"JSON translation error: {str(e)}")
return json_content
class Valves:
cache_duration: int = 3600
translate_ui: bool = True
excluded_elements: list = ["code", "pre", "script", "style"]
class UserValves:
default_target_language: str = "fr"
auto_translate: bool = True
translate_ui: bool = True
cache_enabled: bool = True
custom_translations: Dict[str, str] = {}
def file_handler(file):
"""Handle translation of uploaded files"""
if not file or not hasattr(file, "content_type"):
return None
try:
if file.content_type == "application/json":
content = json.loads(file.read())
tools = Tools()
translated_content = tools.translate_json_content(content)
return json.dumps(translated_content, ensure_ascii=False, indent=2)
except Exception as e:
print(f"File handler error: {str(e)}")
return None
def before_request(request):
"""Hook executed before each request"""
if not UserValves.translate_ui or not request or request.method != "GET":
return request
try:
tools = Tools()
content_type = request.headers.get("content-type", "").lower()
if "text/html" in content_type:
response_content = request.get_data(as_text=True)
return tools.translate_ui(response_content)
elif "application/json" in content_type:
response_json = request.get_json()
return tools.translate_json_content(response_json)
except Exception as e:
print(f"Before request error: {str(e)}")
return request
def after_response(response):
"""Hook executed after each response"""
if not UserValves.translate_ui or not response:
return response
try:
tools = Tools()
content_type = response.headers.get("content-type", "").lower()
if "text/html" in content_type:
response.headers["Content-Type"] = "text/html; charset=utf-8"
response.data = tools.translate_ui(response.get_data(as_text=True)).encode(
"utf-8"
)
elif "application/json" in content_type:
response.headers["Content-Type"] = "application/json; charset=utf-8"
response_json = json.loads(response.get_data(as_text=True))
response.data = json.dumps(
tools.translate_json_content(response_json), ensure_ascii=False
).encode("utf-8")
except Exception as e:
print(f"After response error: {str(e)}")
return response
def process_websocket_message(message: Union[str, dict]) -> Union[str, dict]:
"""Process WebSocket messages"""
try:
tools = Tools()
if isinstance(message, str):
return tools.translate_message(message)
elif isinstance(message, dict):
return tools.process_chat_message(message)
except Exception as e:
print(f"WebSocket message processing error: {str(e)}")
return message
def process_stream_chunk(chunk: str) -> str:
"""Process streaming response chunks"""
try:
tools = Tools()
return tools.process_stream(chunk)
except Exception as e:
print(f"Stream chunk processing error: {str(e)}")
return chunk
def citation():
return {
"name": "Deep Translator",
"url": "https://github.com/nidhaloff/deep-translator",
"license": "MIT",
"version": "1.0.0",
}