NOTICE
Open WebUI Community is currently undergoing a major revamp to improve user experience and performance. Expected completion by year-end! ✨

Tool
v1.0.0
WebUI Auto Translator
Automatically translates both messages and UI elements
Tool ID
webui_auto_translator
Creator
@nnaoycurt
Downloads
413+

Tool Content
python
"""
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",
    }