feat(panel): panel web multilingüe con caché de traducciones en MySQL y soporte nest-asyncio

This commit is contained in:
2026-03-06 20:53:20 -06:00
parent 0c0a1811ef
commit 05858e26ab
7 changed files with 230 additions and 131 deletions

View File

@@ -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: