From 74272039c57f07aa76e8c0ac1a34519c3f3b1aa9 Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Mon, 1 Jun 2026 19:06:27 +0300 Subject: [PATCH] feat: add login language selector --- frontend/src/App.svelte | 21 ++- frontend/src/styles/webapp.css | 44 +++++++ frontend/src/webapp/auth/AuthScreen.svelte | 144 ++++++++++++++++----- 3 files changed, 173 insertions(+), 36 deletions(-) diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index c71616c..6c0eb53 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -183,6 +183,7 @@ let languageClickGuardArmed = false; let languageClickGuardTimer = null; let languageClickGuardArmTimer = null; + let guestLanguage = ""; let emailAvatarUrl = ""; let avatarHashToken = ""; let token = MOCK ? "local-preview" : ""; @@ -213,12 +214,13 @@ const i18n = createI18n({ messages: I18N, defaultLang: "ru", - getLang: () => user?.language_code || CFG.language || "ru", + getLang: () => user?.language_code || guestLanguage || CFG.language || "ru", }); const normalizeLangCode = i18n.normalizeLangCode; const t = i18n.t; const termUnitLabel = i18n.termUnitLabel; const languageName = i18n.languageName; + guestLanguage = normalizeLangCode(CFG.language || "ru"); const apiClient = createApiClient({ apiBase: CFG.apiBase, csrfCookieName: CSRF_COOKIE_NAME, @@ -460,7 +462,7 @@ activeTab = "settings"; } $: referral = data?.referral || MOCK_SOURCE.data.referral; - $: currentLang = normalizeLangCode(user?.language_code || CFG.language || "ru"); + $: currentLang = normalizeLangCode(user?.language_code || guestLanguage || CFG.language || "ru"); $: languageCodes = uniqueLanguageCodes( WEBAPP_LANGUAGE_ORDER, CFG.languages, @@ -939,6 +941,13 @@ }, 260); } + function updateGuestLanguage(nextValue) { + const language = normalizeLangCode(nextValue); + setLanguageMenuOpen(false); + if (!language || language === currentLang) return; + guestLanguage = language; + } + function readTelegramMiniAppInitDataFromLocation() { return telegramSdk.readInitDataFromLocation(); } @@ -2212,7 +2221,15 @@ {telegramLoginUnavailableMessage} {privacyPolicyUrl} {userAgreementUrl} + {currentLang} + {currentLanguageOption} + {languageOptions} + {languageMenuOpen} + {languageClickGuard} + {languageClickGuardArmed} {t} + {setLanguageMenuOpen} + updateLoginLanguage={updateGuestLanguage} requestEmailCode={() => authStore.requestEmailCode((s) => (screen = s))} loginWithEmailPassword={authStore.loginWithEmailPassword} verifyEmailCode={authStore.verifyEmailCode} diff --git a/frontend/src/styles/webapp.css b/frontend/src/styles/webapp.css index 43707fa..ec2bae6 100644 --- a/frontend/src/styles/webapp.css +++ b/frontend/src/styles/webapp.css @@ -2378,6 +2378,50 @@ a { color: var(--accent); } +.auth-language-trigger { + appearance: none; + -webkit-tap-highlight-color: transparent; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5px; + min-height: 26px; + border: 0; + border-radius: 7px; + background: transparent; + color: var(--muted); + font: inherit; + font-size: 12px; + line-height: 1; + cursor: pointer; +} + +.auth-language-trigger:hover, +.auth-language-trigger[data-state="open"] { + color: var(--accent); +} + +.auth-language-trigger:focus-visible { + outline: 0; + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 42%, transparent); +} + +.auth-language-trigger > span:last-of-type { + text-decoration: underline; + text-underline-offset: 2px; +} + +.auth-language-trigger > svg, +.auth-language-trigger > .emoji-flag { + flex: 0 0 auto; +} + +.auth-language-content { + width: min(188px, calc(100vw - 40px)); + min-width: 154px; + transform-origin: top center; +} + .auth-bottom strong { color: var(--accent); } diff --git a/frontend/src/webapp/auth/AuthScreen.svelte b/frontend/src/webapp/auth/AuthScreen.svelte index a90981a..1c711b3 100644 --- a/frontend/src/webapp/auth/AuthScreen.svelte +++ b/frontend/src/webapp/auth/AuthScreen.svelte @@ -1,6 +1,14 @@ {#if screen === "code"} @@ -236,40 +259,93 @@ {/key} - {#if userAgreementUrl || privacyPolicyUrl} + {#if userAgreementUrl || privacyPolicyUrl || showLanguageSelect}