sectasy0/pyi18n

View on GitHub
pyi18n/loaders.py

Summary

Maintainability
A
1 hr
Test Coverage
"""
This module defines the PyI18n loaders which
load translations from files in YAML or JSON format.
"""

from os.path import exists, join
from typing import Type
import json
import yaml

from pyi18n.helpers import load_locale


class LoaderType:
    """Enum for the different loader types."""

    BASE: str = "base"
    YAML: str = "yaml"
    JSON: str = "json"


class PyI18nBaseLoader:
    """PyI18n Base Loader class, supports yaml and json

    Attributes:
        load_path (str): path to translations
        namespaced (bool): tells loader should look for namespaces

        type (str): loader type
    """

    type: str = LoaderType.BASE

    def __init__(
        self,
        load_path: str = "locales/",
        namespaced: bool = False
    ) -> None:
        """Initialize loader class

        Args:
            load_path (str): path to translations
            namespaced (bool): namespaces support

        Return:
            None
        """
        self.load_path: str = load_path
        self.namespaced: bool = namespaced

    def load(self, locales: tuple, ser_mod: Type) -> dict:
        """Load translations for given locales,
            should be overridden in child classes.

        Args:
            locales (tuple): locales to load
            ser_mod (Type): serializer module

        Return:
            dict: loaded translations

        Notes:
            Custom load function should be implemented
            in child classes and return python dict
        """

        file_extension: str = ser_mod.__name__

        loaded: dict = {}
        for locale in locales:
            file_path: str = f"{self.load_path}{locale}.{file_extension}"

            if not exists(file_path):
                if file_extension == LoaderType.YAML and exists(f"{self.load_path}{locale}.yml"):
                    file_path = f"{self.load_path}{locale}.yml"
                else:
                    continue

            try:
                loaded[locale] = self.__load_file(
                    file_path, file_extension, ser_mod, locale
                )
            except (json.decoder.JSONDecodeError, yaml.YAMLError):
                continue

        return loaded

    def __load_file(
        self,
        file_path: str,
        ext: str,
        ser_mod: Type,
        locale: str
    ) -> dict:
        """loads content, should not be called directly

        Return:
            dict: loaded content
        """
        with open(file_path, "r", encoding="utf-8") as _f:
            load_params: dict = {
                "Loader": yaml.FullLoader} if ext in ("yml", "yaml") else {}

            return ser_mod.load(_f, **load_params)[locale]

    def _load_namespaced(self, locales: tuple, ser_mod: Type) -> dict:
        """Load translations from namespaces.

        Should be overridden in child classes.
        This will look for a locale (directories) and load all namespaces.

        Args:
            locales (tuple): locales to load
            ser_mod (Type): module for serialization

        Return:
            dict: loaded translations
        """
        loaded: dict = {}
        for locale in locales:
            path: str = join(self.load_path, locale)
            loaded_locale: dict = load_locale(path, ser_mod, self.type)

            if not loaded_locale:
                continue

            loaded.setdefault(locale, loaded_locale)

        return loaded

    def get_path(self) -> str:
        """Return loader path

        Return:
            str: loader path
        """
        return self.load_path


class PyI18nJsonLoader(PyI18nBaseLoader):
    """PyI18n JSON Loader class

    Attributes:
        load_path (str): path to translations
        namespaced (bool): tells loader should look for namespaces

        type (str): loader type
    """

    type: str = LoaderType.JSON

    def load(self, locales: tuple) -> dict:
        """Load translations for given locales using json

        Inherits from PyI18nBaseLoader

        Args:
            locales (tuple): locales to load

        Return:
            dict: loaded translations
        """

        if self.namespaced:
            return super()._load_namespaced(locales, json)

        return super().load(locales, json)


class PyI18nYamlLoader(PyI18nBaseLoader):
    """PyI18n YAML Loader class

    Attributes:
        load_path (str): path to translations
        namespaced (bool): tells loader should look for namespaces

        type (str): loader type
    """

    type: str = LoaderType.YAML

    def load(self, locales: tuple) -> dict:
        """Load translations for given locales using yaml

        Inherits from PyI18nBaseLoader

        Args:
            locales (tuple): locales to load

        Return:
            dict: loaded translations
        """
        if self.namespaced:
            return super()._load_namespaced(locales, yaml)

        return super().load(locales, yaml)