Add internationalization support and enhance language handling
All checks were successful
CI / lint-and-test (push) Successful in 14s

- Introduced a new i18n module for managing translations and language normalization, supporting both Russian and English.
- Updated various handlers and services to utilize the new translation functions for user-facing messages, improving user experience based on language preferences.
- Enhanced error handling and response messages to be language-aware, ensuring appropriate feedback is provided to users in their preferred language.
- Added tests for the i18n module to validate language detection and translation functionality.
- Updated the example environment file to include a default language configuration.
This commit is contained in:
2026-02-18 13:56:49 +03:00
parent be57555d4f
commit 263c2fefbd
21 changed files with 594 additions and 92 deletions

View File

@@ -13,12 +13,13 @@ from duty_teller.db.repository import (
delete_group_duty_pin,
get_all_group_duty_pin_chat_ids,
)
from duty_teller.i18n import t
def format_duty_message(duty, user, tz_name: str) -> str:
def format_duty_message(duty, user, tz_name: str, lang: str = "en") -> str:
"""Build the text for the pinned message. duty, user may be None."""
if duty is None or user is None:
return "Сейчас дежурства нет."
return t(lang, "duty.no_duty")
try:
tz = ZoneInfo(tz_name)
except Exception:
@@ -39,8 +40,9 @@ def format_duty_message(duty, user, tz_name: str) -> str:
f"{start_local.strftime('%d.%m.%Y %H:%M')}"
f"{end_local.strftime('%d.%m.%Y %H:%M')} ({tz_hint})"
)
label = t(lang, "duty.label")
lines = [
f"🕐 Дежурство: {time_range}",
f"🕐 {label} {time_range}",
f"👤 {user.full_name}",
]
if user.phone:
@@ -50,14 +52,14 @@ def format_duty_message(duty, user, tz_name: str) -> str:
return "\n".join(lines)
def get_duty_message_text(session: Session, tz_name: str) -> str:
def get_duty_message_text(session: Session, tz_name: str, lang: str = "en") -> str:
"""Get current duty from DB and return formatted message."""
now = datetime.now(timezone.utc)
result = get_current_duty(session, now)
if result is None:
return "Сейчас дежурства нет."
return t(lang, "duty.no_duty")
duty, user = result
return format_duty_message(duty, user, tz_name)
return format_duty_message(duty, user, tz_name, lang)
def get_next_shift_end_utc(session: Session) -> datetime | None: