From d6d80a17444c90259c5bfdacb84c61e6bfece655 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 5 Jan 2023 11:38:41 +0100 Subject: Merging upstream version 3.0.0~a1. Signed-off-by: Daniel Baumann --- pendulum/locales/locale.py | 206 ++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 104 deletions(-) (limited to 'pendulum/locales/locale.py') 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}')" -- cgit v1.2.3