refactor: improve language normalization and date handling utilities
All checks were successful
CI / lint-and-test (push) Successful in 21s

- Introduced a new `normalize_lang` function to standardize language codes across the application, ensuring consistent handling of user language preferences.
- Refactored date handling utilities by adding `parse_utc_iso` and `parse_utc_iso_naive` functions for better parsing of ISO 8601 date strings, enhancing timezone awareness.
- Updated various modules to utilize the new language normalization and date parsing functions, improving code clarity and maintainability.
- Enhanced error handling in date validation to raise specific `DateRangeValidationError` exceptions, providing clearer feedback on validation issues.
- Improved test coverage for date range validation and language normalization functionalities, ensuring robustness and reliability.
This commit is contained in:
2026-02-20 22:42:54 +03:00
parent f53ef81306
commit d02d0a1835
19 changed files with 216 additions and 158 deletions

View File

@@ -1,6 +1,7 @@
"""Internationalization: RU/EN by Telegram language_code. Normalize to 'ru' or 'en'."""
from duty_teller.i18n.messages import MESSAGES
from duty_teller.i18n.core import get_lang, t
from duty_teller.i18n.lang import normalize_lang
from duty_teller.i18n.messages import MESSAGES
__all__ = ["MESSAGES", "get_lang", "t"]
__all__ = ["MESSAGES", "get_lang", "normalize_lang", "t"]

View File

@@ -3,6 +3,7 @@
from typing import TYPE_CHECKING
import duty_teller.config as config
from duty_teller.i18n.lang import normalize_lang
from duty_teller.i18n.messages import MESSAGES
if TYPE_CHECKING:
@@ -12,13 +13,12 @@ if TYPE_CHECKING:
def get_lang(user: "User | None") -> str:
"""
Normalize Telegram user language to 'ru' or 'en'.
If user has language_code starting with 'ru' (e.g. ru, ru-RU) return 'ru', else 'en'.
When user is None or has no language_code, return config.DEFAULT_LANGUAGE.
Uses normalize_lang for user.language_code; when user is None or has no
language_code, returns config.DEFAULT_LANGUAGE.
"""
if user is None or not getattr(user, "language_code", None):
return config.DEFAULT_LANGUAGE
code = (user.language_code or "").strip().lower()
return "ru" if code.startswith("ru") else "en"
return normalize_lang(user.language_code)
def t(lang: str, key: str, **kwargs: str) -> str:

26
duty_teller/i18n/lang.py Normal file
View File

@@ -0,0 +1,26 @@
"""Single source of truth for normalizing language codes to 'ru' or 'en'.
Use for: env DEFAULT_LANGUAGE, Accept-Language header, Telegram user.language_code,
and initData user object in Miniapp auth.
"""
from typing import Literal
def normalize_lang(code: str | None) -> Literal["ru", "en"]:
"""Normalize a language code to 'ru' or 'en'.
Suitable for: env DEFAULT_LANGUAGE, Accept-Language header,
Telegram user.language_code, and initData user in Miniapp auth.
Args:
code: Raw language code (e.g. 'ru', 'ru-RU', 'en', 'en-US') or None.
Returns:
'ru' if code starts with 'ru' (after strip/lower), else 'en'.
Returns 'en' when code is empty or not a string.
"""
if not code or not isinstance(code, str):
return "en"
code = code.strip().lower()
return "ru" if code.startswith("ru") else "en"