Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/translationhandler.py
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib/translationhandler.py')
-rw-r--r--archinstall/lib/translationhandler.py165
1 files changed, 165 insertions, 0 deletions
diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py
new file mode 100644
index 00000000..12c8da4a
--- /dev/null
+++ b/archinstall/lib/translationhandler.py
@@ -0,0 +1,165 @@
+from __future__ import annotations
+
+import json
+import logging
+import os
+import gettext
+from dataclasses import dataclass
+
+from pathlib import Path
+from typing import List, Dict, Any, TYPE_CHECKING, Optional
+from .exceptions import TranslationError
+
+if TYPE_CHECKING:
+ _: Any
+
+
+@dataclass
+class Language:
+ abbr: str
+ lang: str
+ translation: gettext.NullTranslations
+ translation_percent: int
+ translated_lang: Optional[str]
+
+ @property
+ def display_name(self) -> str:
+ if self.translated_lang:
+ name = self.translated_lang
+ else:
+ name = self.lang
+ return f'{name} ({self.translation_percent}%)'
+
+ def is_match(self, lang_or_translated_lang: str) -> bool:
+ if self.lang == lang_or_translated_lang:
+ return True
+ elif self.translated_lang == lang_or_translated_lang:
+ return True
+ return False
+
+
+class TranslationHandler:
+ _base_pot = 'base.pot'
+ _languages = 'languages.json'
+
+ def __init__(self):
+ # to display cyrillic languages correctly
+ self._set_font('UniCyr_8x16')
+
+ self._total_messages = self._get_total_messages()
+ self._translated_languages = self._get_translations()
+
+ @property
+ def translated_languages(self) -> List[Language]:
+ return self._translated_languages
+
+ def _get_translations(self) -> List[Language]:
+ mappings = self._load_language_mappings()
+ defined_languages = self._defined_languages()
+
+ languages = []
+
+ for short_form in defined_languages:
+ mapping_entry: Dict[str, Any] = next(filter(lambda x: x['abbr'] == short_form, mappings))
+ abbr = mapping_entry['abbr']
+ lang = mapping_entry['lang']
+ translated_lang = mapping_entry.get('translated_lang', None)
+
+ try:
+ translation = gettext.translation('base', localedir=self._get_locales_dir(), languages=(abbr, lang))
+
+ if abbr == 'en':
+ percent = 100
+ else:
+ num_translations = self._get_catalog_size(translation)
+ percent = int((num_translations / self._total_messages) * 100)
+
+ language = Language(abbr, lang, translation, percent, translated_lang)
+ languages.append(language)
+ except FileNotFoundError as error:
+ raise TranslationError(f"Could not locate language file for '{lang}': {error}")
+
+ return languages
+
+ def _set_font(self, font: str):
+ from archinstall import SysCommand, log
+ try:
+ log(f'Setting font: {font}', level=logging.DEBUG)
+ SysCommand(f'setfont {font}')
+ except Exception:
+ log(f'Unable to set font {font}', level=logging.ERROR)
+
+ def _load_language_mappings(self) -> List[Dict[str, Any]]:
+ locales_dir = self._get_locales_dir()
+ languages = Path.joinpath(locales_dir, self._languages)
+
+ with open(languages, 'r') as fp:
+ return json.load(fp)
+
+ def _get_catalog_size(self, translation: gettext.NullTranslations) -> int:
+ # this is a ery naughty way of retrieving the data but
+ # there's no alternative method exposed unfortunately
+ catalog = translation._catalog # type: ignore
+ messages = {k: v for k, v in catalog.items() if k and v}
+ return len(messages)
+
+ def _get_total_messages(self) -> int:
+ locales = self._get_locales_dir()
+ with open(f'{locales}/{self._base_pot}', 'r') as fp:
+ lines = fp.readlines()
+ msgid_lines = [line for line in lines if 'msgid' in line]
+ return len(msgid_lines) - 1 # don't count the first line which contains the metadata
+
+ def get_language(self, abbr: str) -> Language:
+ try:
+ return next(filter(lambda x: x.abbr == abbr, self._translated_languages))
+ except Exception:
+ raise ValueError(f'No language with abbreviation "{abbr}" found')
+
+ def activate(self, language: Language):
+ language.translation.install()
+
+ def _get_locales_dir(self) -> Path:
+ cur_path = Path(__file__).parent.parent
+ locales_dir = Path.joinpath(cur_path, 'locales')
+ return locales_dir
+
+ def _defined_languages(self) -> List[str]:
+ locales_dir = self._get_locales_dir()
+ filenames = os.listdir(locales_dir)
+ return list(filter(lambda x: len(x) == 2 or x == 'pt_BR', filenames))
+
+
+class DeferredTranslation:
+ def __init__(self, message: str):
+ self.message = message
+
+ def __len__(self) -> int:
+ return len(self.message)
+
+ def __str__(self) -> str:
+ translate = _
+ if translate is DeferredTranslation:
+ return self.message
+ return translate(self.message)
+
+ def __lt__(self, other) -> bool:
+ return self.message < other
+
+ def __gt__(self, other) -> bool:
+ return self.message > other
+
+ def __add__(self, other) -> DeferredTranslation:
+ if isinstance(other, str):
+ other = DeferredTranslation(other)
+
+ concat = self.message + other.message
+ return DeferredTranslation(concat)
+
+ def format(self, *args) -> str:
+ return self.message.format(*args)
+
+ @classmethod
+ def install(cls):
+ import builtins
+ builtins._ = cls