feat: unify language handling across the application

- Updated the language configuration to use a single source of truth from `DEFAULT_LANGUAGE` for the bot, API, and Mini App, eliminating auto-detection from user settings.
- Refactored the `get_lang` function to always return `DEFAULT_LANGUAGE`, ensuring consistent language usage throughout the application.
- Modified the handling of language in various components, including API responses and UI elements, to reflect the new language management approach.
- Enhanced documentation and comments to clarify the changes in language handling.
- Added unit tests to verify the new language handling behavior and ensure coverage for the updated functionality.
This commit is contained in:
2026-03-02 23:05:28 +03:00
parent 54446d7b0f
commit 67ba9826c7
21 changed files with 446 additions and 205 deletions

View File

@@ -37,23 +37,35 @@ import {
initTheme();
state.lang = getLang();
document.documentElement.lang = state.lang;
document.title = t(state.lang, "app.title");
const loadingEl = getLoadingEl();
const loadingTextEl = loadingEl ? loadingEl.querySelector(".loading__text") : null;
if (loadingTextEl) loadingTextEl.textContent = t(state.lang, "loading");
const dayLabels = weekdayLabels(state.lang);
const weekdaysEl = getWeekdaysEl();
if (weekdaysEl) {
const spans = weekdaysEl.querySelectorAll("span");
spans.forEach((span, i) => {
if (dayLabels[i]) span.textContent = dayLabels[i];
});
/**
* Apply current state.lang to document and locale-dependent UI elements (title, loading, weekdays, nav).
* Call at startup and after re-evaluating lang when initData becomes available.
* Exported for tests.
*/
export function applyLangToUi() {
document.documentElement.lang = state.lang;
document.title = t(state.lang, "app.title");
const loadingEl = getLoadingEl();
const loadingTextEl = loadingEl ? loadingEl.querySelector(".loading__text") : null;
if (loadingTextEl) loadingTextEl.textContent = t(state.lang, "loading");
const dayLabels = weekdayLabels(state.lang);
const weekdaysEl = getWeekdaysEl();
if (weekdaysEl) {
const spans = weekdaysEl.querySelectorAll("span");
spans.forEach((span, i) => {
if (dayLabels[i]) span.textContent = dayLabels[i];
});
}
const prevBtn = getPrevBtn();
const nextBtn = getNextBtn();
if (prevBtn) prevBtn.setAttribute("aria-label", t(state.lang, "nav.prev_month"));
if (nextBtn) nextBtn.setAttribute("aria-label", t(state.lang, "nav.next_month"));
}
const prevBtn = getPrevBtn();
const nextBtn = getNextBtn();
if (prevBtn) prevBtn.setAttribute("aria-label", t(state.lang, "nav.prev_month"));
if (nextBtn) nextBtn.setAttribute("aria-label", t(state.lang, "nav.next_month"));
applyLangToUi();
window.__dtReady = true;
/**
* Run callback when Telegram WebApp is ready (or immediately outside Telegram).
@@ -62,9 +74,6 @@ if (nextBtn) nextBtn.setAttribute("aria-label", t(state.lang, "nav.next_month"))
*/
function runWhenReady(cb) {
if (window.Telegram && window.Telegram.WebApp) {
if (window.Telegram.WebApp.ready) {
window.Telegram.WebApp.ready();
}
if (window.Telegram.WebApp.expand) {
window.Telegram.WebApp.expand();
}
@@ -193,6 +202,8 @@ async function loadMonth() {
setNavEnabled(true);
return;
}
const errorEl2 = getErrorEl();
if (errorEl2) errorEl2.hidden = true;
const loading = getLoadingEl();
if (loading) loading.classList.add("hidden");
setNavEnabled(true);
@@ -276,6 +287,11 @@ function bindStickyScrollShadow() {
runWhenReady(() => {
requireTelegramOrLocalhost(() => {
const newLang = getLang();
if (newLang !== state.lang) {
state.lang = newLang;
applyLangToUi();
}
bindStickyScrollShadow();
initDayDetail();
initHints();
@@ -284,7 +300,6 @@ runWhenReady(() => {
window.Telegram.WebApp.initDataUnsafe.start_param) ||
"";
if (startParam === "duty") {
state.lang = getLang();
showCurrentDutyView(() => {
hideCurrentDutyView();
loadMonth();