Skip to content

API reference

i18n.StringFormatter

Source code in src/i18n/formatter.py
 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
class StringFormatter:
    def __init__(self, global_context: dict[str, Any]) -> None:
        """Initializes the StringFormatter with a global context.

        Args:
            global_context (dict[str, Any]): A dictionary containing global context variables.
        """
        self.global_context = global_context
        self.pattern = re.compile(r"\{([^{}]+)\}")

    def _resolve_expr(self, expr: str, **kwargs: Any) -> str:
        """Resolves an expression based on the context.

        Args:
            expr (str): The expression to resolve.
            **kwargs: Additional context variables.

        Returns:
            str: The resolved expression or an error message if the expression cannot be resolved.
        """
        ctx = ChainMap(kwargs, self.global_context)

        if expr.startswith("func:"):
            return self._call_function(expr[5:], **kwargs)
        if expr.startswith("obj:"):
            return self._get_object_attr(expr[4:], **kwargs)
        if expr.startswith("const:"):
            return ctx.get(expr[6:], f"[Error: const `{expr[6:]}` not defined]")
        return ctx.get(expr, f"[Error: `{expr}` is not defined]")

    def _call_function(self, expr: str, **kwargs: Any) -> str:
        """Calls a function based on the expression.

        Args:
            expr (str): The function expression to call.
            **kwargs: Additional context variables.

        Returns:
            str: The result of the function call or an error message if the function cannot be called.
        """
        ctx = ChainMap(kwargs, self.global_context)

        match = re.fullmatch(r"(\w+)\((.*)\)", expr)
        if not match:
            return f"[Error: Expression '{expr}' does not match the expected pattern]"

        func_name, func_args = match.groups()

        func: Optional[Callable[..., Any]] = ctx.get(func_name)

        if not func:
            return f"[Error: func `{func_name}` is not defined]"
        if not callable(func):
            return f"[Error: `{func_name}` is not callable]"
        return str(eval(f"{func_name}({func_args})", dict(ctx), {}))

    def _get_object_attr(self, expr: str, **kwargs: Any) -> str:
        """Gets an object's attribute based on the expression.

        Args:
            expr (str): The object attribute expression to resolve.
            **kwargs: Additional context variables.

        Returns:
            str: The value of the object's attribute or an error message if the attribute cannot be resolved.
        """
        ctx = ChainMap(kwargs, self.global_context)
        parts = expr.split(".")
        obj_name = parts[0]

        obj = ctx.get(obj_name)

        if obj is None:
            return f"[Error: object `{obj_name}` is not defined]"

        return str(eval(expr, dict(ctx), {}))

    def format(self, text: str, **kwargs: Any) -> str:
        """Formats a string by replacing placeholders with context values.

        Args:
            text (str): The text containing placeholders to format.
            **kwargs: Additional context variables.

        Returns:
            str: The formatted string with placeholders replaced by context values.
        """

        def replace(match: re.Match[str]) -> str:
            return str(self._resolve_expr(match.group(1), **kwargs))

        return self.pattern.sub(replace, text)

i18n.StringFormatter.__init__

__init__(global_context: dict[str, Any]) -> None

Initializes the StringFormatter with a global context.

Parameters:

Name Type Description Default
global_context dict[str, Any]

A dictionary containing global context variables.

required
Source code in src/i18n/formatter.py
 9
10
11
12
13
14
15
16
def __init__(self, global_context: dict[str, Any]) -> None:
    """Initializes the StringFormatter with a global context.

    Args:
        global_context (dict[str, Any]): A dictionary containing global context variables.
    """
    self.global_context = global_context
    self.pattern = re.compile(r"\{([^{}]+)\}")

i18n.StringFormatter._resolve_expr

_resolve_expr(expr: str, **kwargs: Any) -> str

Resolves an expression based on the context.

Parameters:

Name Type Description Default
expr str

The expression to resolve.

required
**kwargs Any

Additional context variables.

{}

Returns:

Name Type Description
str str

The resolved expression or an error message if the expression cannot be resolved.

Source code in src/i18n/formatter.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def _resolve_expr(self, expr: str, **kwargs: Any) -> str:
    """Resolves an expression based on the context.

    Args:
        expr (str): The expression to resolve.
        **kwargs: Additional context variables.

    Returns:
        str: The resolved expression or an error message if the expression cannot be resolved.
    """
    ctx = ChainMap(kwargs, self.global_context)

    if expr.startswith("func:"):
        return self._call_function(expr[5:], **kwargs)
    if expr.startswith("obj:"):
        return self._get_object_attr(expr[4:], **kwargs)
    if expr.startswith("const:"):
        return ctx.get(expr[6:], f"[Error: const `{expr[6:]}` not defined]")
    return ctx.get(expr, f"[Error: `{expr}` is not defined]")

i18n.StringFormatter._call_function

_call_function(expr: str, **kwargs: Any) -> str

Calls a function based on the expression.

Parameters:

Name Type Description Default
expr str

The function expression to call.

required
**kwargs Any

Additional context variables.

{}

Returns:

Name Type Description
str str

The result of the function call or an error message if the function cannot be called.

Source code in src/i18n/formatter.py
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
def _call_function(self, expr: str, **kwargs: Any) -> str:
    """Calls a function based on the expression.

    Args:
        expr (str): The function expression to call.
        **kwargs: Additional context variables.

    Returns:
        str: The result of the function call or an error message if the function cannot be called.
    """
    ctx = ChainMap(kwargs, self.global_context)

    match = re.fullmatch(r"(\w+)\((.*)\)", expr)
    if not match:
        return f"[Error: Expression '{expr}' does not match the expected pattern]"

    func_name, func_args = match.groups()

    func: Optional[Callable[..., Any]] = ctx.get(func_name)

    if not func:
        return f"[Error: func `{func_name}` is not defined]"
    if not callable(func):
        return f"[Error: `{func_name}` is not callable]"
    return str(eval(f"{func_name}({func_args})", dict(ctx), {}))

i18n.StringFormatter._get_object_attr

_get_object_attr(expr: str, **kwargs: Any) -> str

Gets an object's attribute based on the expression.

Parameters:

Name Type Description Default
expr str

The object attribute expression to resolve.

required
**kwargs Any

Additional context variables.

{}

Returns:

Name Type Description
str str

The value of the object's attribute or an error message if the attribute cannot be resolved.

Source code in src/i18n/formatter.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def _get_object_attr(self, expr: str, **kwargs: Any) -> str:
    """Gets an object's attribute based on the expression.

    Args:
        expr (str): The object attribute expression to resolve.
        **kwargs: Additional context variables.

    Returns:
        str: The value of the object's attribute or an error message if the attribute cannot be resolved.
    """
    ctx = ChainMap(kwargs, self.global_context)
    parts = expr.split(".")
    obj_name = parts[0]

    obj = ctx.get(obj_name)

    if obj is None:
        return f"[Error: object `{obj_name}` is not defined]"

    return str(eval(expr, dict(ctx), {}))

i18n.StringFormatter.format

format(text: str, **kwargs: Any) -> str

Formats a string by replacing placeholders with context values.

Parameters:

Name Type Description Default
text str

The text containing placeholders to format.

required
**kwargs Any

Additional context variables.

{}

Returns:

Name Type Description
str str

The formatted string with placeholders replaced by context values.

Source code in src/i18n/formatter.py
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def format(self, text: str, **kwargs: Any) -> str:
    """Formats a string by replacing placeholders with context values.

    Args:
        text (str): The text containing placeholders to format.
        **kwargs: Additional context variables.

    Returns:
        str: The formatted string with placeholders replaced by context values.
    """

    def replace(match: re.Match[str]) -> str:
        return str(self._resolve_expr(match.group(1), **kwargs))

    return self.pattern.sub(replace, text)

i18n.I18N

Internationalization class for managing translations.

Source code in src/i18n/i18n.py
 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
103
104
105
106
107
108
109
110
111
class I18N:
    """Internationalization class for managing translations."""

    def __init__(
        self,
        default_locale: str,
        load_path: str = "locales/",
    ) -> None:
        """Initialize the I18N class.

        Args:
            default_locale (str): The default locale to use.
            load_path (str, optional): The path to the directory containing locale files. Defaults to "locales/".
        """
        self.load_path = load_path
        self.default_locale = default_locale
        self.loaded_translations: dict[str, dict[str, Any]] = {}
        self.context: dict[str, Any] = {}
        self.formatter = StringFormatter(self.context)

        self.load()

    def load(self) -> None:
        """Load translations from locale files."""
        if not Path(self.load_path).exists():
            self.loaded_translations = {}
            return

        for locale in Path(self.load_path).iterdir():
            if locale.is_file() and locale.suffix in (".yaml", ".yml"):
                with locale.open(encoding="utf-8") as f:
                    self.loaded_translations[locale.stem] = yaml.safe_load(f) or {}

    def _get_nested_translation(self, data: dict[str, Any], key: str) -> Optional[str]:
        """Retrieve a nested translation from the data dictionary.

        Args:
            data (dict): The dictionary containing translations.
            key (str): The key for the desired translation.

        Returns:
            Optional[str]: The nested translation or None if not found.
        """
        keys = key.split(".")
        for k in keys:
            if isinstance(data, dict) and k in data:
                data = data[k]
            else:
                return None
        return data if isinstance(data, str) else None

    def register_function(self, name: str, func: Callable[..., Any]) -> None:
        """Register a custom function for use in translations.

        Args:
            name (str): The name of the function.
            func (Callable): The function to register.
        """
        self.context[name] = func

    def register_constant(self, name: str, value: Any) -> None:
        """Register a constant for use in translations.

        Args:
            name (str): The name of the constant.
            value (Any): The value of the constant.
        """
        self.context[name] = value

    def t(self, locale: str, key: str, **kwargs: Any) -> str:
        """Translate a key for a given locale.

        Args:
            locale (str): The locale to use for translation.
            key (str): The key to translate.
            **kwargs: Additional keyword arguments for formatting the translation.

        Returns:
            str: The translated string or the key if no translation is found.
        """
        translation = (
            self._get_nested_translation(self.loaded_translations.get(locale, {}), key)
            or self._get_nested_translation(
                self.loaded_translations.get(self.default_locale, {}), key
            )
            or key
        )

        if isinstance(translation, str):
            return self.formatter.format(translation, **kwargs)

        return key

    @property
    def available_locales(self) -> set[str]:
        """Get the set of available locales.

        Returns:
            set[str]: The set of available locales.
        """
        return set(self.loaded_translations.keys())

i18n.I18N.available_locales property

available_locales: set[str]

Get the set of available locales.

Returns:

Type Description
set[str]

set[str]: The set of available locales.

i18n.I18N.__init__

__init__(
    default_locale: str, load_path: str = "locales/"
) -> None

Initialize the I18N class.

Parameters:

Name Type Description Default
default_locale str

The default locale to use.

required
load_path str

The path to the directory containing locale files. Defaults to "locales/".

'locales/'
Source code in src/i18n/i18n.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def __init__(
    self,
    default_locale: str,
    load_path: str = "locales/",
) -> None:
    """Initialize the I18N class.

    Args:
        default_locale (str): The default locale to use.
        load_path (str, optional): The path to the directory containing locale files. Defaults to "locales/".
    """
    self.load_path = load_path
    self.default_locale = default_locale
    self.loaded_translations: dict[str, dict[str, Any]] = {}
    self.context: dict[str, Any] = {}
    self.formatter = StringFormatter(self.context)

    self.load()

i18n.I18N.load

load() -> None

Load translations from locale files.

Source code in src/i18n/i18n.py
33
34
35
36
37
38
39
40
41
42
def load(self) -> None:
    """Load translations from locale files."""
    if not Path(self.load_path).exists():
        self.loaded_translations = {}
        return

    for locale in Path(self.load_path).iterdir():
        if locale.is_file() and locale.suffix in (".yaml", ".yml"):
            with locale.open(encoding="utf-8") as f:
                self.loaded_translations[locale.stem] = yaml.safe_load(f) or {}

i18n.I18N._get_nested_translation

_get_nested_translation(
    data: dict[str, Any], key: str
) -> Optional[str]

Retrieve a nested translation from the data dictionary.

Parameters:

Name Type Description Default
data dict

The dictionary containing translations.

required
key str

The key for the desired translation.

required

Returns:

Type Description
Optional[str]

Optional[str]: The nested translation or None if not found.

Source code in src/i18n/i18n.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def _get_nested_translation(self, data: dict[str, Any], key: str) -> Optional[str]:
    """Retrieve a nested translation from the data dictionary.

    Args:
        data (dict): The dictionary containing translations.
        key (str): The key for the desired translation.

    Returns:
        Optional[str]: The nested translation or None if not found.
    """
    keys = key.split(".")
    for k in keys:
        if isinstance(data, dict) and k in data:
            data = data[k]
        else:
            return None
    return data if isinstance(data, str) else None

i18n.I18N.register_function

register_function(
    name: str, func: Callable[..., Any]
) -> None

Register a custom function for use in translations.

Parameters:

Name Type Description Default
name str

The name of the function.

required
func Callable

The function to register.

required
Source code in src/i18n/i18n.py
62
63
64
65
66
67
68
69
def register_function(self, name: str, func: Callable[..., Any]) -> None:
    """Register a custom function for use in translations.

    Args:
        name (str): The name of the function.
        func (Callable): The function to register.
    """
    self.context[name] = func

i18n.I18N.register_constant

register_constant(name: str, value: Any) -> None

Register a constant for use in translations.

Parameters:

Name Type Description Default
name str

The name of the constant.

required
value Any

The value of the constant.

required
Source code in src/i18n/i18n.py
71
72
73
74
75
76
77
78
def register_constant(self, name: str, value: Any) -> None:
    """Register a constant for use in translations.

    Args:
        name (str): The name of the constant.
        value (Any): The value of the constant.
    """
    self.context[name] = value

i18n.I18N.t

t(locale: str, key: str, **kwargs: Any) -> str

Translate a key for a given locale.

Parameters:

Name Type Description Default
locale str

The locale to use for translation.

required
key str

The key to translate.

required
**kwargs Any

Additional keyword arguments for formatting the translation.

{}

Returns:

Name Type Description
str str

The translated string or the key if no translation is found.

Source code in src/i18n/i18n.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def t(self, locale: str, key: str, **kwargs: Any) -> str:
    """Translate a key for a given locale.

    Args:
        locale (str): The locale to use for translation.
        key (str): The key to translate.
        **kwargs: Additional keyword arguments for formatting the translation.

    Returns:
        str: The translated string or the key if no translation is found.
    """
    translation = (
        self._get_nested_translation(self.loaded_translations.get(locale, {}), key)
        or self._get_nested_translation(
            self.loaded_translations.get(self.default_locale, {}), key
        )
        or key
    )

    if isinstance(translation, str):
        return self.formatter.format(translation, **kwargs)

    return key