diff --git a/botdiscord/translate.py b/botdiscord/translate.py
index c2fb748..ba20d45 100644
--- a/botdiscord/translate.py
+++ b/botdiscord/translate.py
@@ -1,4 +1,6 @@
import aiohttp
+import re
+import asyncio
from botdiscord.config import get_libretranslate_url, get_languages
from botdiscord.database import get_available_languages, get_bot_languages
@@ -47,14 +49,6 @@ REVERSE_MAPPING = {}
FLAG_MAPPING = {}
NAME_TO_CODE = {}
-import aiohttp
-import re
-import asyncio
-from botdiscord.config import get_libretranslate_url, get_languages
-from botdiscord.database import get_available_languages, get_bot_languages
-
-# ... (mantener funciones load_lang_mappings y getters de mapping iguales)
-
async def _do_translate_request(session, url, text, target_code):
"""Función interna para realizar una única petición de traducción."""
# Verificamos si el segmento tiene al menos una letra de cualquier idioma
@@ -73,11 +67,8 @@ async def _do_translate_request(session, url, text, target_code):
if resp.status == 200:
data = await resp.json()
translated = data.get("translatedText", text)
- # Si el resultado es igual al original, puede ser que el motor fallara,
- # devolvemos el texto original tal cual.
return translated
else:
- # En caso de error de API (404, 500, etc) devolvemos el original
return text
except Exception:
return text
@@ -85,17 +76,12 @@ async def _do_translate_request(session, url, text, target_code):
async def translate_text(text: str, target_lang: str) -> str:
url = get_libretranslate_url()
if not url:
- return "Error: URL de LibreTranslate no configurada"
+ return text
target_code = NAME_TO_CODE.get(target_lang, target_lang)
- # Segmentación mejorada:
- # Divide por signos de puntuación (. ! ?) o saltos de línea,
- # permitiendo que después haya espacios, comas u otros signos.
- # Usamos un grupo de captura para no perder los delimitadores.
+ # Segmentación mejorada
segments = re.split(r'([.!?]+\s*|\n+)', text)
-
- # Filtramos segmentos vacíos que puedan quedar tras el split
segments = [s for s in segments if s]
async with aiohttp.ClientSession() as session:
@@ -107,6 +93,26 @@ async def translate_text(text: str, target_lang: str) -> str:
return "".join(translated_segments)
+def translate_text_sync(text: str, target_lang: str) -> str:
+ """Versión síncrona de translate_text para usar en filtros de Jinja2."""
+ if not text or not target_lang or target_lang == "es":
+ return text
+
+ try:
+ import nest_asyncio
+ nest_asyncio.apply()
+ except ImportError:
+ # Si no está instalado, no podemos usar el bridge en FastAPI
+ return text
+
+ try:
+ loop = asyncio.get_event_loop()
+ except RuntimeError:
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+
+ return loop.run_until_complete(translate_text(text, target_lang))
+
def get_lang_mapping(bot_type: str = None) -> dict:
load_lang_mappings(bot_type)
return LANG_MAPPING.copy()
@@ -121,7 +127,6 @@ def get_flag_mapping(bot_type: str = None) -> dict:
def get_name_to_code(bot_type: str = None) -> dict:
load_lang_mappings(bot_type)
- print(f"[DEBUG] get_name_to_code returning: {NAME_TO_CODE}")
return NAME_TO_CODE.copy()
def get_lang_flag(lang_code: str) -> str:
diff --git a/panel/main.py b/panel/main.py
index 443604b..8bd6b90 100644
--- a/panel/main.py
+++ b/panel/main.py
@@ -10,6 +10,8 @@ from fastapi.responses import RedirectResponse
from pydantic import BaseModel
from botdiscord.config import load_config, get_web_config, get_libretranslate_url, get_db_type
+from botdiscord.database import get_ui_translation, save_ui_translation
+from botdiscord.translate import translate_text
app = FastAPI(title="Panel de Configuración - Bots de Traducción")
@@ -17,6 +19,45 @@ SCRIPT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
templates = Jinja2Templates(directory=os.path.join(SCRIPT_DIR, "panel", "templates"))
+# Filtro de traducción para Jinja2
+def translate_filter(text, lang="es"):
+ if lang == "es" or not text:
+ return text
+
+ # Buscamos en caché de la base de datos (acceso rápido)
+ from botdiscord.database import get_ui_translation, save_ui_translation
+ cached = get_ui_translation(text, lang)
+ if cached:
+ return cached
+
+ # Si no está en caché, traducimos de forma síncrona para la UI
+ from botdiscord.translate import translate_text_sync
+ translated = translate_text_sync(text, lang)
+
+ if translated and translated != text:
+ save_ui_translation(text, lang, translated)
+
+ return translated
+
+templates.env.filters["translate"] = translate_filter
+
+@app.get("/set-lang/{lang}")
+async def set_lang(lang: str, request: Request):
+ # Validamos que el idioma sea de 2 letras
+ if len(lang) != 2:
+ return RedirectResponse(url="/dashboard")
+
+ response = RedirectResponse(url=request.headers.get("referer", "/dashboard"))
+ response.set_cookie(key="panel_lang", value=lang, max_age=31536000) # 1 año
+ return response
+
+def get_panel_lang(request: Request):
+ lang = request.cookies.get("panel_lang")
+ if not lang:
+ accept_lang = request.headers.get("accept-language", "es")
+ lang = accept_lang.split(",")[0].split("-")[0]
+ return lang
+
class LoginForm(BaseModel):
username: str
password: str
diff --git a/panel/templates/config.html b/panel/templates/config.html
index 37eaa60..a978a9f 100644
--- a/panel/templates/config.html
+++ b/panel/templates/config.html
@@ -1,9 +1,10 @@
+{% set lang = request.cookies.get('panel_lang', 'es') %}
-
+
- Configuración - Bots de Traducción
+ {{ "Configuración - Bots de Traducción" | translate(lang) }}
@@ -11,41 +12,51 @@