hesabixArc/hesabixAPI/app/core/i18n.py

90 lines
3.1 KiB
Python

from __future__ import annotations
from typing import Any, Callable
from fastapi import Request
from .i18n_catalog import get_gettext_translation
SUPPORTED_LOCALES: tuple[str, ...] = ("fa", "en")
DEFAULT_LOCALE: str = "en"
def negotiate_locale(accept_language: str | None) -> str:
if not accept_language:
return DEFAULT_LOCALE
parts = [p.strip() for p in accept_language.split(",") if p.strip()]
for part in parts:
lang = part.split(";")[0].strip().lower()
base = lang.split("-")[0]
if lang in SUPPORTED_LOCALES:
return lang
if base in SUPPORTED_LOCALES:
return base
return DEFAULT_LOCALE
class Translator:
def __init__(self, locale: str) -> None:
self.locale = locale if locale in SUPPORTED_LOCALES else DEFAULT_LOCALE
self._gt = get_gettext_translation(self.locale)
_catalog: dict[str, dict[str, str]] = {
"en": {
"OK": "OK",
"INVALID_CAPTCHA": "Invalid captcha code.",
"INVALID_CREDENTIALS": "Invalid credentials.",
"IDENTIFIER_REQUIRED": "Identifier is required.",
"INVALID_IDENTIFIER": "Identifier must be a valid email or mobile number.",
"EMAIL_IN_USE": "Email is already in use.",
"MOBILE_IN_USE": "Mobile number is already in use.",
"ACCOUNT_DISABLED": "Your account is disabled.",
"RESET_TOKEN_INVALID_OR_EXPIRED": "Reset token is invalid or expired.",
"VALIDATION_ERROR": "Validation error",
"STRING_TOO_SHORT": "String is too short",
"STRING_TOO_LONG": "String is too long",
"FIELD_REQUIRED": "Field is required",
"INVALID_EMAIL": "Invalid email address",
"HTTP_ERROR": "Request failed",
},
"fa": {
"OK": "باشه",
"INVALID_CAPTCHA": "کد امنیتی نامعتبر است.",
"INVALID_CREDENTIALS": "ایمیل/موبایل یا رمز عبور نادرست است.",
"IDENTIFIER_REQUIRED": "شناسه ورود الزامی است.",
"INVALID_IDENTIFIER": "شناسه باید ایمیل یا شماره موبایل معتبر باشد.",
"EMAIL_IN_USE": "این ایمیل قبلاً استفاده شده است.",
"MOBILE_IN_USE": "این شماره موبایل قبلاً استفاده شده است.",
"ACCOUNT_DISABLED": "حساب کاربری شما غیرفعال است.",
"RESET_TOKEN_INVALID_OR_EXPIRED": "توکن بازنشانی نامعتبر یا منقضی شده است.",
"VALIDATION_ERROR": "خطای اعتبارسنجی",
"STRING_TOO_SHORT": "رشته خیلی کوتاه است",
"STRING_TOO_LONG": "رشته خیلی بلند است",
"FIELD_REQUIRED": "فیلد الزامی است",
"INVALID_EMAIL": "ایمیل نامعتبر است",
"HTTP_ERROR": "درخواست ناموفق بود",
},
}
def t(self, key: str, default: str | None = None) -> str:
# 1) gettext domain (if present)
try:
if self._gt is not None:
msg = self._gt.gettext(key)
if msg and msg != key:
return msg
except Exception:
pass
# 2) in-memory catalog fallback
catalog = self._catalog.get(self.locale) or {}
if key in catalog:
return catalog[key]
return default or key
async def locale_dependency(request: Request) -> Translator:
lang = negotiate_locale(request.headers.get("Accept-Language"))
return Translator(lang)