gonets/SRV/utils/i18n.py
2025-10-21 09:20:50 +05:00

74 lines
No EOL
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Утилиты локализации и зависимости переводов для FastAPI.
Содержимое модуля:
- `get_translations()`: фабрика зависимости FastAPI, считывающая локаль из
query-параметра `locale`, cookie `locale` или заголовка `Accept-Language`
(по приоритету), с дефолтом `ru`. Возвращает функцию-зависимость, которая
подгружает `babel.support.Translations` из каталога `SRV/locales`.
- `_()`: удобная обёртка для получения перевода строки с использованием
текущей локали (или дефолтного `ru`, если контекст запроса не передан).
Особенности:
- При отсутствии словаря перевода для выбранной локали выполняется fallback на
русскую локаль, что гарантирует наличие строк интерфейса.
- Локаль определяется только по первому языку из `Accept-Language` (без
региональных суффиксов) для упрощения.
- Модуль ведёт отладочное логирование выбора и загрузки локали.
"""
from fastapi import Request, Depends
from babel.support import Translations
import logging
logger = logging.getLogger(__name__)
def get_translations() -> callable:
"""
Создаёт зависимость для загрузки переводов на основе заголовка Accept-Language
или параметра запроса 'locale'. По умолчанию используется русский язык.
"""
def translations_dependency(request: Request) -> Translations:
# Проверяем параметр запроса 'locale', если он есть
locale = request.query_params.get("locale")
if not locale:
# Если параметр отсутствует, проверяем куку 'locale'
locale = request.cookies.get("locale")
if not locale:
# Если куки нет, используем заголовок Accept-Language
locale = request.headers.get("Accept-Language", "ru").split(",")[0].split("-")[0]
logger.debug(f"Determined locale: {locale}")
try:
translations = Translations.load("locales", [locale], "messages")
if not translations._catalog:
logger.warning(f"Translations for locale {locale} not found, falling back to ru")
translations = Translations.load("locales", ["ru"], "messages")
logger.debug(f"Loaded translations for locale {locale}, catalog size: {len(translations._catalog)}")
return translations
except Exception as e:
logger.error(f"Failed to load translations for locale {locale}: {str(e)}")
# Fallback на русский
logger.debug("Falling back to ru translations")
return Translations.load("locales", ["ru"], "messages")
return translations_dependency
def _(msgid: str, request: Request = None) -> str:
"""
Переводит строку msgid для текущей локали.
Args:
msgid (str): Строка для перевода.
request (Request, optional): Объект запроса FastAPI для получения локали.
Returns:
str: Переведенная строка.
"""
if request is None:
# Язык, который будет отображаться по умолчанию
translations = Translations.load("locales", ["ru"], "messages")
logger.debug(f"Using default ru translations for msgid: {msgid}")
else:
translations = get_translations()(request)
logger.debug(f"Using translations for locale {request.cookies.get('locale', 'ru')} for msgid: {msgid}")
return translations.gettext(msgid)