Coverage for src/debputy/l10n.py: 43%

46 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2025-01-27 13:59 +0000

1import atexit 

2import gettext 

3import os 

4from functools import lru_cache 

5from gettext import NullTranslations, GNUTranslations 

6from tempfile import TemporaryDirectory 

7from typing import Optional, Union, Set, Iterable, TYPE_CHECKING 

8 

9from debputy import DEBPUTY_IS_RUN_FROM_SOURCE, DEBPUTY_ROOT_DIR 

10from debputy.util import _debug_log 

11 

12try: 

13 import polib 

14 

15 HAS_POLIB = True 

16except ImportError: 

17 HAS_POLIB = False 

18 

19if TYPE_CHECKING: 

20 import polib 

21 

22Translations = Union[NullTranslations, GNUTranslations] 

23 

24 

25def N_(v: str) -> str: 

26 return v 

27 

28 

29@lru_cache 

30def _temp_messages_dir() -> str: 

31 temp_dir = TemporaryDirectory(delete=False, ignore_cleanup_errors=True) 

32 atexit.register(lambda: temp_dir.cleanup()) 

33 return temp_dir.name 

34 

35 

36_GENERATED_MO_FILES_FOR: Set[str] = set() 

37 

38 

39def translation( 

40 domain: str, 

41 *, 

42 languages: Optional[Iterable[str]] = None, 

43) -> Translations: 

44 if DEBPUTY_IS_RUN_FROM_SOURCE and HAS_POLIB: 44 ↛ 45line 44 didn't jump to line 45 because the condition on line 44 was never true

45 po_domain_dir = DEBPUTY_ROOT_DIR / "po" / domain 

46 locale_dir = _temp_messages_dir() 

47 if po_domain_dir.is_dir() and domain not in _GENERATED_MO_FILES_FOR: 

48 for child in po_domain_dir.iterdir(): 

49 if not child.is_file() or not child.name.endswith(".po"): 

50 continue 

51 language = child.name[:-3] 

52 mo_dir = os.path.join(locale_dir, language, "LC_MESSAGES") 

53 os.makedirs(mo_dir, exist_ok=True) 

54 mo_path = os.path.join(mo_dir, f"{domain}.mo") 

55 parsed_po_file = polib.pofile(child) 

56 parsed_po_file.save_as_mofile(mo_path) 

57 _GENERATED_MO_FILES_FOR.add(domain) 

58 try: 

59 r = gettext.translation( 

60 domain, 

61 localedir=locale_dir, 

62 languages=languages, 

63 fallback=True, 

64 ) 

65 _debug_log(f"Found in-source translation for {domain}") 

66 return r 

67 except FileNotFoundError: 

68 # Fall through 

69 _debug_log( 

70 f"No in-source translation for {domain}, trying to installed translations" 

71 ) 

72 

73 # For some reason, the type checking thinks that `fallback` must be `Literal[False]` which 

74 # defeats the purpose of being able to provide a different value than the default for it. 

75 # 

76 # So `type: ignore` to "fix" that for now. 

77 return gettext.translation( 

78 domain, 

79 languages=languages, 

80 fallback=True, 

81 ) 

82 

83 

84__all__ = ["N_", "translation", "Translations"]