25a392df48
178 archivos: módulo core.py actualizado + ~80 funciones nuevas con tests. Incluye: parse_llm_json, extract_text_from_file, retry_with_backoff, circuit_breaker, from_csv/to_csv, from_jsonl/to_jsonl, html_to_markdown, pdf_to_markdown, docx/epub/excel converters, cache_decorator, react_loop, task_manager, template rendering, entre otros. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
92 lines
2.8 KiB
Python
92 lines
2.8 KiB
Python
"""Traduccion de claves con dot-path notation e interpolacion de variables."""
|
|
|
|
import threading
|
|
|
|
_locale_local = threading.local()
|
|
_translations: dict = {}
|
|
_default_locale: str = "en"
|
|
|
|
|
|
def _set_translations(translations: dict, default_locale: str = "en") -> None:
|
|
"""Configura el diccionario global de traducciones y el locale default.
|
|
|
|
Llamar desde load_translations o al inicio de la aplicacion.
|
|
"""
|
|
global _translations, _default_locale
|
|
_translations = translations
|
|
_default_locale = default_locale
|
|
|
|
|
|
def _set_locale(locale: str) -> None:
|
|
"""Establece el locale para el thread actual."""
|
|
_locale_local.locale = locale
|
|
|
|
|
|
def _get_locale() -> str:
|
|
"""Retorna el locale del thread actual, o el default si no esta configurado."""
|
|
return getattr(_locale_local, "locale", _default_locale)
|
|
|
|
|
|
def _resolve_key(translations: dict, locale: str, key: str) -> str | None:
|
|
"""Navega dot-path en el diccionario de traducciones para un locale.
|
|
|
|
Returns:
|
|
El string de traduccion si existe, None si no.
|
|
"""
|
|
locale_dict = translations.get(locale)
|
|
if not locale_dict:
|
|
return None
|
|
|
|
parts = key.split(".")
|
|
node = locale_dict
|
|
for part in parts:
|
|
if not isinstance(node, dict):
|
|
return None
|
|
node = node.get(part)
|
|
if node is None:
|
|
return None
|
|
|
|
return node if isinstance(node, str) else None
|
|
|
|
|
|
def t(key: str, locale: str | None = None, **kwargs) -> str:
|
|
"""Traduce una clave con dot-path notation al idioma actual.
|
|
|
|
Determina el locale en orden de prioridad: parametro > thread-local > default.
|
|
Soporta interpolacion de variables con {nombre} en el valor traducido.
|
|
Si la clave no existe en el locale solicitado, intenta el locale default.
|
|
Si tampoco existe, retorna la clave tal cual.
|
|
|
|
Args:
|
|
key: Clave de traduccion en dot-path notation (ej: "report.taskStarted").
|
|
locale: Locale a usar. Si es None usa el locale del thread actual.
|
|
**kwargs: Variables para interpolar en el string traducido.
|
|
|
|
Returns:
|
|
String traducido con variables interpoladas, o la key si no se encontro.
|
|
|
|
Example:
|
|
>>> # translations = {"en": {"report": {"sectionStart": "Section: {title}"}}}
|
|
>>> t("report.sectionStart", locale="en", title="Introduction")
|
|
'Section: Introduction'
|
|
>>> t("nonexistent.key", locale="en")
|
|
'nonexistent.key'
|
|
"""
|
|
resolved_locale = locale if locale is not None else _get_locale()
|
|
|
|
value = _resolve_key(_translations, resolved_locale, key)
|
|
|
|
if value is None and resolved_locale != _default_locale:
|
|
value = _resolve_key(_translations, _default_locale, key)
|
|
|
|
if value is None:
|
|
return key
|
|
|
|
if kwargs:
|
|
try:
|
|
value = value.format(**kwargs)
|
|
except (KeyError, ValueError):
|
|
pass
|
|
|
|
return value
|