fix: use localStorage instead of URL path for language persistence

Telegram WebView caches URL and strips #tgWebAppData hash on reopens,
causing empty initData when URL path contained language prefix like /en.
This commit is contained in:
2026-02-22 21:04:29 +03:00
parent f2b809cb7c
commit c72782793a
4 changed files with 16 additions and 33 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "pezkuwi-telegram-miniapp",
"version": "1.0.218",
"version": "1.0.220",
"type": "module",
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
"author": "Pezkuwichain Team",
+2 -2
View File
@@ -12,8 +12,8 @@ import type { Translations, LanguageCode } from '@/i18n/types';
const translations: Record<LanguageCode, Translations> = { krd, en, tr, ckb, fa, ar };
function detectLang(): LanguageCode {
const seg = window.location.pathname.split('/').filter(Boolean)[0];
if (seg && seg in translations) return seg as LanguageCode;
const stored = localStorage.getItem('app_language');
if (stored && stored in translations) return stored as LanguageCode;
return 'krd';
}
+10 -27
View File
@@ -68,14 +68,15 @@ const TG_LANG_MAP: Record<string, LanguageCode> = {
};
/**
* Detect language: URL path first, then Telegram user language, then default.
* Detect language: localStorage first, then Telegram user language, then default.
* CRITICAL: Never use URL path for language in Telegram MiniApps!
* Telegram WebView caches URL and strips #tgWebAppData hash on next open.
*/
function detectLanguage(): LanguageCode {
// 1. Check URL path
const path = window.location.pathname;
const firstSegment = path.split('/').filter(Boolean)[0];
if (firstSegment && VALID_LANGS.includes(firstSegment as LanguageCode)) {
return firstSegment as LanguageCode;
// 1. Check localStorage (persisted user preference)
const stored = localStorage.getItem('app_language');
if (stored && VALID_LANGS.includes(stored as LanguageCode)) {
return stored as LanguageCode;
}
// 2. Check Telegram WebApp user language
@@ -134,34 +135,16 @@ export function LanguageProvider({ children }: LanguageProviderProps) {
currentLanguage = lang;
}, [lang]);
// Update document direction, lang attribute, and URL when language changes
// Update document direction, lang attribute, and persist to localStorage
useEffect(() => {
document.documentElement.lang = lang === 'krd' ? 'ku' : lang;
document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
// Ensure URL has language prefix (preserve query params and special paths)
const pathSegments = window.location.pathname.split('/').filter(Boolean);
const firstSegment = pathSegments[0];
// Don't rewrite URL for standalone pages like /citizens
const STANDALONE_PATHS = ['citizens', 'explorer'];
if (firstSegment && STANDALONE_PATHS.includes(firstSegment)) {
// Keep standalone path as-is
} else if (!firstSegment || !VALID_LANGS.includes(firstSegment as LanguageCode)) {
window.history.replaceState(null, '', `/${lang}${window.location.search}`);
}
localStorage.setItem('app_language', lang);
}, [lang, isRTL]);
const setLang = useCallback((newLang: LanguageCode) => {
setLangState(newLang);
// Update URL without reload
const currentPath = window.location.pathname;
const segments = currentPath.split('/').filter(Boolean);
// Remove old lang prefix if present
if (segments.length > 0 && VALID_LANGS.includes(segments[0] as LanguageCode)) {
segments.shift();
}
const newPath = `/${newLang}${segments.length > 0 ? '/' + segments.join('/') : ''}`;
window.history.replaceState(null, '', `${newPath}${window.location.search}`);
localStorage.setItem('app_language', newLang);
}, []);
const t = useCallback(
+3 -3
View File
@@ -1,5 +1,5 @@
{
"version": "1.0.218",
"buildTime": "2026-02-21T15:35:05.624Z",
"buildNumber": 1771688105624
"version": "1.0.220",
"buildTime": "2026-02-22T18:04:29.930Z",
"buildNumber": 1771783469930
}