From 5d6762734c938144cabb629fdada1edb5f0e49b8 Mon Sep 17 00:00:00 2001 From: James Addison Date: Tue, 11 Apr 2023 17:54:25 +0100 Subject: [PATCH] Revert "Refactor ``_TranslationProxy``" This reverts commit 2c83af0aab7080e0b78d4a16981eed878b2cac4c. --- sphinx/locale/__init__.py | 74 +++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index 8bc7a267320..2291fc00bf3 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -10,22 +10,29 @@ class _TranslationProxy: """ + Class for proxy strings from gettext translations. This is a helper for the + lazy_* functions from this module. + The proxy implementation attempts to be as complete as possible, so that the lazy objects should mostly work as expected, for example for sorting. """ - __slots__ = '_catalogue', '_namespace', '_message' + __slots__ = ('_func', '_args') + + def __new__(cls, func: Callable[..., str], *args: str) -> _TranslationProxy: + if not args: + # not called with "function" and "arguments", but a plain string + return str(func) # type: ignore[return-value] + return object.__new__(cls) + + def __getnewargs__(self) -> tuple[str]: + return (self._func,) + self._args # type: ignore - def __init__(self, catalogue: str, namespace: str, message: str) -> None: - self._catalogue = catalogue - self._namespace = namespace - self._message = message + def __init__(self, func: Callable[..., str], *args: str) -> None: + self._func = func + self._args = args def __str__(self) -> str: - try: - return translators[self._namespace, self._catalogue].gettext(self._message) - except KeyError: - # NullTranslations().gettext(self._message) == self._message - return self._message + return str(self._func(*self._args)) def __dir__(self) -> list[str]: return dir(str) @@ -33,21 +40,20 @@ def __dir__(self) -> list[str]: def __getattr__(self, name: str) -> Any: return getattr(self.__str__(), name) - def __getstate__(self) -> tuple[str, str, str]: - return self._catalogue, self._namespace, self._message + def __getstate__(self) -> tuple[Callable[..., str], tuple[str, ...]]: + return self._func, self._args - def __setstate__(self, tup: tuple[str, str, str]) -> None: - self._catalogue, self._namespace, self._message = tup + def __setstate__(self, tup: tuple[Callable[..., str], tuple[str]]) -> None: + self._func, self._args = tup def __copy__(self) -> _TranslationProxy: - return _TranslationProxy(self._catalogue, self._namespace, self._message) + return _TranslationProxy(self._func, *self._args) def __repr__(self) -> str: try: - return f'i{self.__str__()!r}' + return 'i' + repr(str(self.__str__())) except Exception: - return (self.__class__.__name__ - + f'({self._catalogue}, {self._namespace}, {self._message})') + return f'<{self.__class__.__name__} broken>' def __add__(self, other: str) -> str: return self.__str__() + other @@ -104,6 +110,8 @@ def init( # ignore previously failed attempts to find message catalogs if translator.__class__ is NullTranslations: translator = None + # the None entry is the system's default locale path + has_translation = True if getenv('SOURCE_DATE_EPOCH') is not None: # Disable localization during reproducible source builds @@ -117,17 +125,15 @@ def init( # To achieve that, specify the ISO-639-3 'undetermined' language code, # which should not match any translation catalogs. languages: list[str] | None = ['und'] + elif language and '_' in language: + # for language having country code (like "de_AT") + languages = [language, language.split('_')[0]] elif language: - if '_' in language: - # for language having country code (like "de_AT") - languages = [language, language.split('_')[0]] - else: - languages = [language] + languages = [language] else: languages = None # loading - # the None entry is the system's default locale path for dir_ in locale_dirs: try: trans = translation(catalog, localedir=dir_, languages=languages) @@ -138,13 +144,11 @@ def init( except Exception: # Language couldn't be found in the specified path pass - if translator is not None: - has_translation = True - else: + # guarantee translators[(namespace, catalog)] exists + if translator is None: translator = NullTranslations() has_translation = False - # guarantee translators[(namespace, catalog)] exists - translators[namespace, catalog] = translator + translators[(namespace, catalog)] = translator return translator, has_translation @@ -177,6 +181,14 @@ def is_translator_registered(catalog: str = 'sphinx', namespace: str = 'general' return (namespace, catalog) in translators +def _lazy_translate(catalog: str, namespace: str, message: str, *args: Any) -> str: + """Used instead of _ when creating TranslationProxy, because _ is + not bound yet at that time. + """ + translator = get_translator(catalog, namespace) + return translator.gettext(message) + + def get_translation(catalog: str, namespace: str = 'general') -> Callable[[str], str]: """Get a translation function based on the *catalog* and *namespace*. @@ -202,8 +214,8 @@ def setup(app): .. versionadded:: 1.8 """ - def gettext(message: str) -> str: - return _TranslationProxy(catalog, namespace, message) # type: ignore[return-value] + def gettext(message: str, *args: Any) -> str: + return _TranslationProxy(_lazy_translate, catalog, namespace, message, *args) # type: ignore[return-value] # NOQA return gettext