""" 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)