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
"""
byceps.util.l10n
~~~~~~~~~~~~~~~~

Localization.

:Copyright: 2014-2025 Jochen Kupperschmidt
:License: Revised BSD (see `LICENSE` file for details)
"""

from collections.abc import Iterator
from contextlib import contextmanager

from babel import Locale
from flask import current_app, g, request
from flask_babel import force_locale, format_currency, get_locale
from moneyed import Money
from wtforms import Form

from byceps.services.user.models.user import User


def get_current_user_locale() -> str | None:
    """Return the locale for the current user, if available."""
    # Look for a locale on the current user object.
    user = g.user
    if (user is not None) and (user.locale is not None):
        return user.locale

    if request:
        # Try to match user agent's accepted languages.
        languages = [locale.language for locale in g.locales]
        return request.accept_languages.best_match(languages)

    return None


@contextmanager
def force_user_locale(user: User) -> Iterator[None]:
    """Execute code with the user's preferred locale."""
    locale = get_user_locale(user)
    with force_locale(locale):
        yield


def get_default_locale() -> str:
    """Return the application's default locale."""
    return current_app.config['LOCALE']


def get_user_locale(user: User) -> str:
    """Return the user's preferred locale.

    If no preference is set for the user, return the app's default
    locale.
    """
    return user.locale or get_default_locale()


BASE_LOCALE = Locale('en')


def get_locales() -> list[Locale]:
    """List available locales."""
    return [BASE_LOCALE] + current_app.babel_instance.list_translations()


def get_locale_str() -> str | None:
    """Return the current locale as a string.

    Return `None` outside of a request.
    """
    locale = get_locale()
    if locale is None:
        return None

    locale_str = locale.language
    if locale.territory:
        locale_str += '_' + locale.territory

    return locale_str


class LocalizedForm(Form):
    def __init__(self, *args, **kwargs):
        locale = current_app.config['LOCALE']
        kwargs['meta'] = {'locales': [locale]}
        super().__init__(*args, **kwargs)


def format_money(money: Money) -> str:
    """Format monetary value with amount and currency."""
    return format_currency(money.amount, money.currency.code)