1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
import atexit
import gettext
import os
from functools import lru_cache
from gettext import NullTranslations, GNUTranslations
from tempfile import TemporaryDirectory
from typing import Optional, Union, Set, TYPE_CHECKING
from collections.abc import Iterable
from debputy import DEBPUTY_IS_RUN_FROM_SOURCE, DEBPUTY_ROOT_DIR
from debputy.util import _debug_log
try:
import polib
HAS_POLIB = True
except ImportError:
HAS_POLIB = False
if TYPE_CHECKING:
import polib
Translations = Union[NullTranslations, GNUTranslations]
def N_(v: str) -> str:
return v
@lru_cache
def _temp_messages_dir() -> str:
temp_dir = TemporaryDirectory(delete=False, ignore_cleanup_errors=True)
atexit.register(lambda: temp_dir.cleanup())
return temp_dir.name
_GENERATED_MO_FILES_FOR: set[str] = set()
def translation(
domain: str,
*,
languages: Iterable[str] | None = None,
) -> Translations:
if DEBPUTY_IS_RUN_FROM_SOURCE and HAS_POLIB:
po_domain_dir = DEBPUTY_ROOT_DIR / "po" / domain
locale_dir = _temp_messages_dir()
if po_domain_dir.is_dir() and domain not in _GENERATED_MO_FILES_FOR:
for child in po_domain_dir.iterdir():
if not child.is_file() or not child.name.endswith(".po"):
continue
language = child.name[:-3]
mo_dir = os.path.join(locale_dir, language, "LC_MESSAGES")
os.makedirs(mo_dir, exist_ok=True)
mo_path = os.path.join(mo_dir, f"{domain}.mo")
parsed_po_file = polib.pofile(child)
parsed_po_file.save_as_mofile(mo_path)
_GENERATED_MO_FILES_FOR.add(domain)
try:
r = gettext.translation(
domain,
localedir=locale_dir,
languages=languages,
fallback=True,
)
_debug_log(f"Found in-source translation for {domain}")
return r
except FileNotFoundError:
# Fall through
_debug_log(
f"No in-source translation for {domain}, trying to installed translations"
)
# For some reason, the type checking thinks that `fallback` must be `Literal[False]` which
# defeats the purpose of being able to provide a different value than the default for it.
#
# So `type: ignore` to "fix" that for now.
return gettext.translation(
domain,
languages=languages,
fallback=True,
)
__all__ = ["N_", "translation", "Translations"]
|