lib.i18n

Internacionalización: función _() para CLI y GUI.

Prioridad de idioma: config (~/.config/temario/temario.yaml) → TEMARIO_LANG (variable de entorno) → LANG del sistema → fallback (español, idioma fuente)

Uso:

from lib.i18n import _ # en cualquier módulo import lib.i18n as i18n; i18n.setup() # una vez al inicio de la app (main.py / __main__) print(_("hola"))

 1# Copyright (C) 2026  David Vaquero <pepesan@gmail.com>
 2#
 3# This program is free software: you can redistribute it and/or modify
 4# it under the terms of the GNU General Public License as published by
 5# the Free Software Foundation, either version 3 of the License, or
 6# (at your option) any later version.
 7#
 8# This program is distributed in the hope that it will be useful,
 9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16"""
17Internacionalización: función _() para CLI y GUI.
18
19Prioridad de idioma: config (~/.config/temario/temario.yaml)
20                   → TEMARIO_LANG (variable de entorno)
21                   → LANG del sistema
22                   → fallback (español, idioma fuente)
23
24Uso:
25    from lib.i18n import _           # en cualquier módulo
26    import lib.i18n as i18n; i18n.setup()   # una vez al inicio de la app (main.py / __main__)
27    print(_("hola"))
28"""
29
30import builtins
31import gettext
32import os
33from pathlib import Path
34
35_LOCALES_DIR = Path(__file__).parent.parent / 'locales'
36_DOMAIN      = 'escriba'
37
38_gettext_func = lambda s: s   # identidad hasta que setup() se llame
39
40
41def _(s: str) -> str:  # noqa: E743  (nombre corto intencionado)
42    """Traduce s al idioma activo. No-op hasta que setup() sea llamado."""
43    return _gettext_func(s)
44
45
46def _detect_lang() -> str | None:
47    """Detecta el idioma a usar: config → ESCRIBA_LANG → LANG del sistema."""
48    try:
49        from lib.slides_config import get_language
50        lang = get_language()
51        if lang:
52            return lang
53    except Exception:
54        pass
55    if env := os.environ.get('ESCRIBA_LANG', '').strip():
56        return env
57    system = os.environ.get('LANG', '').split('_')[0].split('.')[0].lower()
58    return system or None
59
60
61def setup(lang: str | None = None) -> None:
62    """Activa la traducción. Llamar una vez al inicio de la app."""
63    global _gettext_func
64    if lang is None:
65        lang = _detect_lang()
66    t = gettext.translation(
67        _DOMAIN,
68        localedir=str(_LOCALES_DIR),
69        languages=[lang] if lang else None,
70        fallback=True,
71    )
72    _gettext_func = t.gettext
73    builtins._ = _   # disponible globalmente para módulos que no importen i18n
74
75
76# Instalar identidad en builtins como fallback (antes de setup())
77if not hasattr(builtins, '_'):
78    builtins._ = _
def setup(lang: str | None = None) -> None:
62def setup(lang: str | None = None) -> None:
63    """Activa la traducción. Llamar una vez al inicio de la app."""
64    global _gettext_func
65    if lang is None:
66        lang = _detect_lang()
67    t = gettext.translation(
68        _DOMAIN,
69        localedir=str(_LOCALES_DIR),
70        languages=[lang] if lang else None,
71        fallback=True,
72    )
73    _gettext_func = t.gettext
74    builtins._ = _   # disponible globalmente para módulos que no importen i18n

Activa la traducción. Llamar una vez al inicio de la app.