diff options
Diffstat (limited to 'pendulum/locales/locale.py')
-rw-r--r-- | pendulum/locales/locale.py | 206 |
1 files changed, 102 insertions, 104 deletions
diff --git a/pendulum/locales/locale.py b/pendulum/locales/locale.py index 154db42..637509a 100644 --- a/pendulum/locales/locale.py +++ b/pendulum/locales/locale.py @@ -1,104 +1,102 @@ -# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import os
-import re
-
-from importlib import import_module
-from typing import Any
-from typing import Optional
-from typing import Union
-
-from pendulum.utils._compat import basestring
-from pendulum.utils._compat import decode
-
-
-class Locale:
- """
- Represent a specific locale.
- """
-
- _cache = {}
-
- def __init__(self, locale, data): # type: (str, Any) -> None
- self._locale = locale
- self._data = data
- self._key_cache = {}
-
- @classmethod
- def load(cls, locale): # type: (Union[str, Locale]) -> Locale
- if isinstance(locale, Locale):
- return locale
-
- locale = cls.normalize_locale(locale)
- if locale in cls._cache:
- return cls._cache[locale]
-
- # Checking locale existence
- actual_locale = locale
- locale_path = os.path.join(os.path.dirname(__file__), actual_locale)
- while not os.path.exists(locale_path):
- if actual_locale == locale:
- raise ValueError("Locale [{}] does not exist.".format(locale))
-
- actual_locale = actual_locale.split("_")[0]
-
- m = import_module("pendulum.locales.{}.locale".format(actual_locale))
-
- cls._cache[locale] = cls(locale, m.locale)
-
- return cls._cache[locale]
-
- @classmethod
- def normalize_locale(cls, locale): # type: (str) -> str
- m = re.match("([a-z]{2})[-_]([a-z]{2})", locale, re.I)
- if m:
- return "{}_{}".format(m.group(1).lower(), m.group(2).lower())
- else:
- return locale.lower()
-
- def get(self, key, default=None): # type: (str, Optional[Any]) -> Any
- if key in self._key_cache:
- return self._key_cache[key]
-
- parts = key.split(".")
- try:
- result = self._data[parts[0]]
- for part in parts[1:]:
- result = result[part]
- except KeyError:
- result = default
-
- if isinstance(result, basestring):
- result = decode(result)
-
- self._key_cache[key] = result
-
- return self._key_cache[key]
-
- def translation(self, key): # type: (str) -> Any
- return self.get("translations.{}".format(key))
-
- def plural(self, number): # type: (int) -> str
- return decode(self._data["plural"](number))
-
- def ordinal(self, number): # type: (int) -> str
- return decode(self._data["ordinal"](number))
-
- def ordinalize(self, number): # type: (int) -> str
- ordinal = self.get("custom.ordinal.{}".format(self.ordinal(number)))
-
- if not ordinal:
- return decode("{}".format(number))
-
- return decode("{}{}".format(number, ordinal))
-
- def match_translation(self, key, value):
- translations = self.translation(key)
- if value not in translations.values():
- return None
-
- return {v: k for k, v in translations.items()}[value]
-
- def __repr__(self):
- return "{}('{}')".format(self.__class__.__name__, self._locale)
+from __future__ import annotations + +from importlib import import_module +from pathlib import Path + +import re +import sys +from typing import Any, cast +from typing import Dict + +if sys.version_info >= (3, 9): + from importlib import resources +else: + import importlib_resources as resources + + +class Locale: + """ + Represent a specific locale. + """ + + _cache: dict[str, Locale] = {} + + def __init__(self, locale: str, data: Any) -> None: + self._locale: str = locale + self._data: Any = data + self._key_cache: dict[str, str] = {} + + @classmethod + def load(cls, locale: str | Locale) -> Locale: + if isinstance(locale, Locale): + return locale + + locale = cls.normalize_locale(locale) + if locale in cls._cache: + return cls._cache[locale] + + # Checking locale existence + actual_locale = locale + locale_path = cast(Path, resources.files(__package__).joinpath(actual_locale)) + while not locale_path.exists(): + if actual_locale == locale: + raise ValueError(f"Locale [{locale}] does not exist.") + + actual_locale = actual_locale.split("_")[0] + + m = import_module(f"pendulum.locales.{actual_locale}.locale") + + cls._cache[locale] = cls(locale, m.locale) + + return cls._cache[locale] + + @classmethod + def normalize_locale(cls, locale: str) -> str: + m = re.match("([a-z]{2})[-_]([a-z]{2})", locale, re.I) + if m: + return f"{m.group(1).lower()}_{m.group(2).lower()}" + else: + return locale.lower() + + def get(self, key: str, default: Any | None = None) -> Any: + if key in self._key_cache: + return self._key_cache[key] + + parts = key.split(".") + try: + result = self._data[parts[0]] + for part in parts[1:]: + result = result[part] + except KeyError: + result = default + + self._key_cache[key] = result + + return self._key_cache[key] + + def translation(self, key: str) -> Any: + return self.get(f"translations.{key}") + + def plural(self, number: int) -> str: + return cast(str, self._data["plural"](number)) + + def ordinal(self, number: int) -> str: + return cast(str, self._data["ordinal"](number)) + + def ordinalize(self, number: int) -> str: + ordinal = self.get(f"custom.ordinal.{self.ordinal(number)}") + + if not ordinal: + return f"{number}" + + return f"{number}{ordinal}" + + def match_translation(self, key: str, value: Any) -> dict[str, str] | None: + translations = self.translation(key) + if value not in translations.values(): + return None + + return cast(Dict[str, str], {v: k for k, v in translations.items()}[value]) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}('{self._locale}')" |