|
|
@@ -0,0 +1,270 @@
|
|
|
+import pathlib
|
|
|
+import json
|
|
|
+import typing
|
|
|
+
|
|
|
+from .loader import loader
|
|
|
+from .phrasebook import phrasebook
|
|
|
+
|
|
|
+class languages:
|
|
|
+ """ It manage languages in the project.
|
|
|
+
|
|
|
+ It represents languages library, store location of languages directory,
|
|
|
+ and also names of laguages in the POSIX locale format, with paths to it
|
|
|
+ in the directory.
|
|
|
+
|
|
|
+ POSIX locale format mean that first two letters comes from ISO 639-1. and
|
|
|
+ second two letters come from ISO 3166-1. For example: "en_US", "pl_PL".
|
|
|
+
|
|
|
+ Methods
|
|
|
+ -------
|
|
|
+ add(name: str, file: pathlib.Path) -> languages
|
|
|
+ This add new languages file to library.
|
|
|
+ load(index: pathlib.Path) -> languages
|
|
|
+ This load languages library from index file.
|
|
|
+ select(name: str) -> phrasebook
|
|
|
+ This load phrasebook for given locales.
|
|
|
+
|
|
|
+ Properties
|
|
|
+ ----------
|
|
|
+ avairable : typing.Iterable[str]
|
|
|
+ List of avairable languages names.
|
|
|
+ default : str
|
|
|
+ Default language name.
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self, path: pathlib.Path) -> None:
|
|
|
+ """ This create new languages library from path to languages library.
|
|
|
+
|
|
|
+ Parameters
|
|
|
+ ----------
|
|
|
+ path : pathlib.Path
|
|
|
+ Path to the languages directory.
|
|
|
+
|
|
|
+ Raises
|
|
|
+ ------
|
|
|
+ RuntimeError
|
|
|
+ When path is not directory or not exists.
|
|
|
+ """
|
|
|
+
|
|
|
+ if not path.is_dir():
|
|
|
+ raise RuntimeError("Path \"" + str(path) + "\" is not directory.")
|
|
|
+
|
|
|
+ if not path.exists():
|
|
|
+ raise RuntimeError("Directory \"" + str(path) + "\" not exists.")
|
|
|
+
|
|
|
+ self.__languages = dict()
|
|
|
+ self.__path = path
|
|
|
+
|
|
|
+ def add(self, name: str, file: pathlib.Path) -> object:
|
|
|
+ """ This add new language to languages library.
|
|
|
+
|
|
|
+ Parameters
|
|
|
+ ----------
|
|
|
+ name : str
|
|
|
+ Name of the language in standard POSIX format, like "en_US",
|
|
|
+ or "pl_PL".
|
|
|
+ file : pathlib.Path
|
|
|
+ Path to the file in the languages. It must be relative path.
|
|
|
+
|
|
|
+ Raises
|
|
|
+ ------
|
|
|
+ RuntimeError
|
|
|
+ When location of the language is not relavice.
|
|
|
+ Exception
|
|
|
+ When language already exists.
|
|
|
+ TypeError
|
|
|
+ When name of the language is not in property POSIX format.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ languages
|
|
|
+ Self to chain loading.
|
|
|
+ """
|
|
|
+
|
|
|
+ if name in self.__languages:
|
|
|
+ raise Exception("Language \"" + name + "\" already exists.")
|
|
|
+
|
|
|
+ if not self.__valid_locale(name):
|
|
|
+ raise TypeError("Name \"" + name + "\" is not property locale.")
|
|
|
+
|
|
|
+ if file.is_absolute():
|
|
|
+ raise RuntimeError(
|
|
|
+ "Location of the \"" + \
|
|
|
+ name + \
|
|
|
+ "\" must be relative."
|
|
|
+ )
|
|
|
+
|
|
|
+ self.__languages[name] = file
|
|
|
+ return self
|
|
|
+
|
|
|
+ @property
|
|
|
+ def avairable(self) -> typing.Iterable[str]:
|
|
|
+ """ It returns avairable languages.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ typing.Iterable[str]
|
|
|
+ Languages which currently exists in the library.
|
|
|
+ """
|
|
|
+
|
|
|
+ return self.__languages.keys()
|
|
|
+
|
|
|
+ @property
|
|
|
+ def default(self) -> str:
|
|
|
+ """ This return default language name.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ str
|
|
|
+ Default language name.
|
|
|
+ """
|
|
|
+
|
|
|
+ if len(self.avairable) == 0:
|
|
|
+ raise RuntimeError("Load any language first.")
|
|
|
+
|
|
|
+ for count in self.avairable:
|
|
|
+ return count
|
|
|
+
|
|
|
+ def __valid_locale(self, name: str) -> bool:
|
|
|
+ """ Check that language name is in property POSIX format.
|
|
|
+
|
|
|
+ Parameters
|
|
|
+ ----------
|
|
|
+ name : str
|
|
|
+ Name of the languages to check.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ bool
|
|
|
+ True when name is property formater, False when not.
|
|
|
+ """
|
|
|
+
|
|
|
+ splited = name.split("_")
|
|
|
+
|
|
|
+ if len(splited) != 2:
|
|
|
+ return False
|
|
|
+
|
|
|
+ first = splited[0]
|
|
|
+ second = splited[1]
|
|
|
+
|
|
|
+ if len(first) != 2 or len(second) != 2:
|
|
|
+ return False
|
|
|
+
|
|
|
+ if first != first.lower():
|
|
|
+ return False
|
|
|
+
|
|
|
+ if second != second.upper():
|
|
|
+ return False
|
|
|
+
|
|
|
+ return True
|
|
|
+
|
|
|
+ def load(self, index: pathlib.Path) -> object:
|
|
|
+ """ That load index of the languages.
|
|
|
+
|
|
|
+ To minimalize use of add function, it is avairable to create index
|
|
|
+ of the languages file. Index must be simple JSON file, with dict where
|
|
|
+ keys are POSIX formated languages names, and values are relative path
|
|
|
+ to the phrasebook files for that language. For example:
|
|
|
+
|
|
|
+ ```JSON
|
|
|
+ {
|
|
|
+ "en_US": "english.json",
|
|
|
+ "pl_PL": "polish.json"
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+ Parameters
|
|
|
+ ----------
|
|
|
+ index : pathlib.Path
|
|
|
+ Relative to the index file from languages directory.
|
|
|
+
|
|
|
+ Raises
|
|
|
+ ------
|
|
|
+ SyntaxError
|
|
|
+ When index file has invalid syntax.
|
|
|
+ RuntimeError
|
|
|
+ When index file not exists or is not path to the file, or path
|
|
|
+ is not relative.
|
|
|
+ TypeError
|
|
|
+ When any item in index JSON file is not str.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ languages
|
|
|
+ Self to the chain loading.
|
|
|
+ """
|
|
|
+
|
|
|
+ if index.is_absolute():
|
|
|
+ raise RuntimeError(
|
|
|
+ "Index path \"" + \
|
|
|
+ str(intex) + \
|
|
|
+ "\" is absolute."
|
|
|
+ )
|
|
|
+
|
|
|
+ store = self.__path / index
|
|
|
+
|
|
|
+ if not store.is_file() or not store.exists():
|
|
|
+ raise RuntimeError("Index \"" + str(store) + "\" not exists.")
|
|
|
+
|
|
|
+ with store.open() as handle:
|
|
|
+ try:
|
|
|
+ loaded = json.loads(handle.read())
|
|
|
+
|
|
|
+ except Exception as error:
|
|
|
+ raise SyntaxError(
|
|
|
+ "Index file \"" + \
|
|
|
+ str(self.__path) + \
|
|
|
+ "\" has invalid syntax.\n" + \
|
|
|
+ str(error)
|
|
|
+ )
|
|
|
+
|
|
|
+ for name, file in loaded.items():
|
|
|
+ if type(name) is not str or not self.__valid_locale(name):
|
|
|
+ raise TypeError("Invalid \"" + str(name) + "\" locale.")
|
|
|
+
|
|
|
+ if type(file) is not str:
|
|
|
+ raise TypeError("Invalid file for \"" + name + "\".")
|
|
|
+
|
|
|
+ self.add(name, pathlib.Path(file))
|
|
|
+
|
|
|
+ return self
|
|
|
+
|
|
|
+ def __get_lang_file(self, name: str) -> pathlib.Path:
|
|
|
+ """ This returns full path to the language with given name.
|
|
|
+
|
|
|
+ Parameters
|
|
|
+ ----------
|
|
|
+ name : str
|
|
|
+ Name of the file to get language of.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ pathlib.Path
|
|
|
+ Full path to that file.
|
|
|
+ """
|
|
|
+
|
|
|
+ return self.__path / self.__languages[name]
|
|
|
+
|
|
|
+ def select(self, name: str) -> phrasebook:
|
|
|
+ """ That load phrasebook from languages directory
|
|
|
+
|
|
|
+ Parameters
|
|
|
+ ----------
|
|
|
+ name : str
|
|
|
+ Name of the language to load phrasebook for.
|
|
|
+
|
|
|
+ Raises
|
|
|
+ ------
|
|
|
+ ValueError
|
|
|
+ When not exists in library.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ phrasebook
|
|
|
+ Loaded phrasebook for that language.
|
|
|
+ """
|
|
|
+
|
|
|
+ if not name in self.__languages:
|
|
|
+ raise ValueError("Language \"" + name + "\" not exists.")
|
|
|
+
|
|
|
+ return loader(self.__get_lang_file(name)).load()
|