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
+27
View File
@@ -0,0 +1,27 @@
import { describe, it, expect } from 'vitest';
import { languageNeedsServerSync } from './language';
import type { Profile } from './model';
// The reconciler only reads isGuest + preferredLanguage; a partial cast keeps the fixture small.
const profile = (over: Partial<Profile>): Profile => ({ isGuest: false, preferredLanguage: 'en', ...over }) as Profile;
describe('languageNeedsServerSync', () => {
it('is false without a profile', () => {
expect(languageNeedsServerSync(null, 'ru')).toBe(false);
expect(languageNeedsServerSync(undefined, 'ru')).toBe(false);
});
it('is false for a guest — guests keep only the client preference', () => {
expect(languageNeedsServerSync(profile({ isGuest: true, preferredLanguage: 'en' }), 'ru')).toBe(false);
});
it('is false when the account already matches the locale', () => {
expect(languageNeedsServerSync(profile({ preferredLanguage: 'ru' }), 'ru')).toBe(false);
});
it('is true for a real account whose stored language differs (banner + push follow it)', () => {
expect(languageNeedsServerSync(profile({ preferredLanguage: 'en' }), 'ru')).toBe(true);
expect(languageNeedsServerSync(profile({ preferredLanguage: 'ru' }), 'en')).toBe(true);
});
});