fix(i18n): reconcile preferred_language with the device's saved language
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m18s

The UI language follows the device (the local choice / system guess) and is
deliberately not overridden from the account, but the advertising banner and
out-of-app push routing are resolved server-side from preferred_language. A
saved device choice the account had not recorded — picked while a guest, or
differing from the Telegram system-language seed — left the banner (and pushes)
in the wrong language until a Settings change rewrote preferred_language.

On profile load (adoptSession and the in-place link path) push the saved local
choice to the account when it differs (new pure helper languageNeedsServerSync;
no-op for guests and when already equal), so the banner and pushes match the
visible UI from the first open. Unit-tested.
This commit is contained in:
Ilia Denisov
2026-06-22 16:16:33 +02:00
parent be1627936f
commit 90f0424de2
3 changed files with 61 additions and 1 deletions
+12 -1
View File
@@ -8,6 +8,7 @@ import { gateway } from './gateway';
import { GatewayError } from './client';
import { navigate, router } from './router.svelte';
import { errorKey, localeFrom, setLocale, t, type Locale } from './i18n/index.svelte';
import { languageNeedsServerSync } from './language';
import { applyReduceMotion, applyTelegramTheme, applyTheme, type ThemePref } from './theme';
import {
insideTelegram,
@@ -455,6 +456,13 @@ async function adoptSession(s: Session): Promise<void> {
// account here. preferred_language stays the user's saved choice (written from Settings,
// and used for out-of-app push routing), but the Telegram bot a user signs in through must
// not dictate the UI: a ru-bot launch on an English system stays English.
//
// But the banner and out-of-app push routing ARE resolved from preferred_language, so an
// explicit device choice the account has not recorded yet (picked while a guest, or
// differing from the Telegram system-language seed) would otherwise leave them in the wrong
// language until the next Settings change. Reconcile the account to the saved local choice
// here; persistLanguageToServer no-ops for guests and when already equal.
if (app.localeLocked) void persistLanguageToServer(app.locale);
} catch (err) {
handleError(err);
}
@@ -475,6 +483,9 @@ export async function applyLinkResult(r: LinkResult): Promise<void> {
return;
}
app.profile = await gateway.profileGet();
// A guest who chose a language and then linked in place now has a durable account: push the
// saved choice so the banner + push routing follow it (see adoptSession).
if (app.localeLocked) void persistLanguageToServer(app.locale);
}
/**
@@ -756,7 +767,7 @@ export function setLocalePref(locale: Locale): void {
*/
async function persistLanguageToServer(locale: Locale): Promise<void> {
const p = app.profile;
if (!p || p.isGuest || p.preferredLanguage === locale) return;
if (!p || !languageNeedsServerSync(p, locale)) return;
try {
app.profile = await gateway.profileUpdate({
displayName: p.displayName,