1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
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}')"
|