|
from __future__ import annotations |
|
|
|
import re |
|
import warnings |
|
from typing import Any |
|
|
|
|
|
class I18nData: |
|
""" |
|
A class that wraps a translation key with metadata. |
|
|
|
This object will be serialized and sent to the frontend, where the actual |
|
translation will happen using the frontend's i18n system. |
|
""" |
|
|
|
def __init__(self, key: str): |
|
""" |
|
Initialize a I18nData object. |
|
|
|
Args: |
|
key: The translation key to be translated in the frontend. |
|
""" |
|
self.key = key |
|
self._type = "translation_metadata" |
|
|
|
def to_dict(self) -> dict[str, Any]: |
|
""" |
|
Convert the I18nData object to a dictionary for serialization. |
|
This allows the frontend to recognize it as a translatable object. |
|
""" |
|
return {"__type__": self._type, "key": self.key} |
|
|
|
def __str__(self) -> str: |
|
""" |
|
String representation of the I18nData object. |
|
Used when the object is converted to a string. |
|
This returns a special format that can be recognized by the frontend |
|
as needing translation. |
|
""" |
|
import json |
|
|
|
return f"__i18n__{json.dumps(self.to_dict())}" |
|
|
|
def __repr__(self) -> str: |
|
""" |
|
Representation of the I18nData object for debugging. |
|
""" |
|
return self.__str__() |
|
|
|
def __add__(self, other): |
|
""" |
|
Handle string concatenation (self + other). |
|
""" |
|
return str(self) + str(other) |
|
|
|
def __radd__(self, other): |
|
""" |
|
Handle string concatenation (other + self). |
|
""" |
|
return str(other) + str(self) |
|
|
|
def __getattr__(self, name): |
|
""" |
|
Handle attribute access for I18nData. |
|
This makes it possible to use I18nData objects in contexts |
|
that expect strings with methods. |
|
""" |
|
if name.startswith("__") and name.endswith("__"): |
|
raise AttributeError(f"{self.__class__.__name__} has no attribute {name}") |
|
|
|
def method(*_args, **_kwargs): |
|
return self |
|
|
|
return method |
|
|
|
def tojson(self) -> dict[str, Any]: |
|
""" |
|
Convert the I18nData object to a JSON-serializable dictionary. |
|
This is used by the default Python JSON serializer. |
|
""" |
|
return self.to_dict() |
|
|
|
|
|
class I18n: |
|
""" |
|
Handles internationalization (i18n) for Gradio applications. |
|
|
|
Stores translation dictionaries and provides a method to retrieve translation keys. |
|
The translation lookup happens on the frontend based on the browser's locale |
|
and the provided translation dictionaries. |
|
""" |
|
|
|
|
|
_LOCALE_PATTERN = re.compile(r"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$") |
|
|
|
def __init__(self, **translations: dict[str, str]): |
|
""" |
|
Initializes the I18n class. |
|
|
|
Args: |
|
**translations: Each keyword argument should be a locale code (e.g., "en", "fr") with a |
|
dictionary value, which maps translation keys to translated strings. |
|
Example: gr.I18n(en={"greeting": "Hello"}, es={"greeting": "Hola"}) |
|
|
|
These translations can be passed to the frontend for use there. |
|
""" |
|
self.translations = {} |
|
|
|
for locale, translation_dict in translations.items(): |
|
if not self._is_valid_locale(locale): |
|
warnings.warn( |
|
f"Invalid locale code: '{locale}'. Locale codes should follow BCP 47 format (e.g., 'en', 'en-US'). " |
|
f"This locale will still be included, but may not work correctly.", |
|
UserWarning, |
|
) |
|
self.translations[locale] = translation_dict |
|
|
|
def _is_valid_locale(self, locale: str) -> bool: |
|
return bool(self._LOCALE_PATTERN.match(locale)) |
|
|
|
def __call__(self, key: str) -> I18nData: |
|
""" |
|
Returns a I18nData object containing the translation key. |
|
|
|
This metadata object will be serialized and sent to the frontend, |
|
where it will be translated by the frontend's i18n system. |
|
|
|
Args: |
|
key: The key to identify the translation string (e.g., "submit_button"). |
|
|
|
Returns: |
|
A I18nData object containing the translation key. |
|
""" |
|
return I18nData(key) |
|
|
|
@property |
|
def translations_dict(self) -> dict[str, dict[str, str]]: |
|
""" |
|
Returns the dictionary of translations provided during initialization. |
|
These can be passed to the frontend for use in its translation system. |
|
""" |
|
return self.translations |
|
|