feedback7 G-TOOLS: out-of-order language-pack responses no longer overwrite the newer choice (PR #304 by @siruimei07)
Applied as-is. initLang and switchLang record a request counter before the first await and apply a pack only when that request is still the latest. Choosing the language already on screen cancels an in-flight load. A failed latest choice still saves the preference and leaves the previous UI. Tests: node --test test/ui/lang.test.js test/ui/i18n.e2e.test.js test/i18n-packs.test.js test/i18n-server.test.js — 29 pass, 0 fail. The Chrome suite in test/ui/i18n.e2e.test.js skipped (SP_E2E unset).
This commit is contained in:
1 parent
7c307d8613
commit
5662b7ef65
3 files changed
+362
-5
No files matched your search
+19
-5
@@ -39,6 +39,8 @@ const uiLoads = new Map();
|
||||
let indexLoad = null;
|
||||
/** Whether the pack index arrived (without it the chain of a pack is taken from its own `_meta` as it loads). */
|
||||
let indexOk = false;
|
||||
/** Only the latest boot or explicit selection may apply its loaded language. */
|
||||
let langRequest = 0;
|
||||
const INDEX_URL = `${PACKS_URL}${PACK_INDEX_FILE}`;
|
||||
|
||||
const defaultFetch = (...a) => globalThis.fetch(...a);
|
||||
@@ -194,20 +196,23 @@ function applyLang(lang) {
|
||||
* @returns {Promise<string>} the language in effect
|
||||
*/
|
||||
export async function initLang() {
|
||||
const request = ++langRequest;
|
||||
wire();
|
||||
applyDocument(getLang());
|
||||
const TIMEOUT = Symbol('timeout');
|
||||
const boot = (async () => {
|
||||
const listed = await loadLangIndex();
|
||||
const { lang, fromUrl } = initialLang(undefined, undefined, { tentative: !listed });
|
||||
if (fromUrl) { savePref(PREF_KEY, lang); stripLangParam(); }
|
||||
if (fromUrl) stripLangParam();
|
||||
if (request !== langRequest) return null;
|
||||
if (fromUrl) savePref(PREF_KEY, lang);
|
||||
if (lang === DEFAULT_LANG) return null;
|
||||
return (await loadLangChain(lang)) ? lang : null;
|
||||
})();
|
||||
const got = await Promise.race([boot, new Promise((r) => setTimeout(() => r(TIMEOUT), BOOT_WAIT_MS))]);
|
||||
if (got === TIMEOUT) {
|
||||
boot.then((lang) => { if (lang && normalizeLang(loadPref(PREF_KEY, null)) === lang) applyLang(lang); });
|
||||
} else if (got) applyLang(got);
|
||||
boot.then((lang) => { if (request === langRequest && lang && normalizeLang(loadPref(PREF_KEY, null)) === lang) applyLang(lang); });
|
||||
} else if (request === langRequest && got) applyLang(got);
|
||||
return getLang();
|
||||
}
|
||||
|
||||
@@ -218,10 +223,12 @@ export async function initLang() {
|
||||
* @returns {Promise<string>} the language in effect
|
||||
*/
|
||||
export async function switchLang(lang) {
|
||||
const request = ++langRequest;
|
||||
wire();
|
||||
const want = normalizeLang(lang) || DEFAULT_LANG;
|
||||
savePref(PREF_KEY, want);
|
||||
if (want !== DEFAULT_LANG && !(await loadLangChain(want))) return getLang();
|
||||
if (request !== langRequest) return getLang();
|
||||
applyLang(want);
|
||||
return getLang();
|
||||
}
|
||||
@@ -290,10 +297,17 @@ export function machineTranslationNote() {
|
||||
export function LangToggle({ class: cls }) {
|
||||
const lang = useLang();
|
||||
const menu = langMenuModel(useLangs(), lang);
|
||||
const pick = (code) => { if (code && code !== getLang()) switchLang(code); };
|
||||
const pick = async (code, select = null) => {
|
||||
if (!code) return;
|
||||
const switched = switchLang(code);
|
||||
const request = langRequest;
|
||||
await switched;
|
||||
// A failed current request keeps the displayed language; stale requests must not reset a newer pending pick.
|
||||
if (select && request === langRequest) select.value = getLang();
|
||||
};
|
||||
if (menu.kind === 'select') {
|
||||
return html`<label class=${`set-seg lang-toggle lang-select${cls ? ` ${cls}` : ''}`} data-testid="lang-toggle">
|
||||
<select aria-label=${SWITCH_LABEL} value=${lang} onChange=${(e) => pick(e.currentTarget.value)}>
|
||||
<select aria-label=${SWITCH_LABEL} value=${lang} onChange=${(e) => pick(e.currentTarget.value, e.currentTarget)}>
|
||||
${menu.items.map((it) => html`<option key=${it.code} value=${it.code} lang=${it.htmlLang} title=${it.title || undefined} selected=${it.on}>${it.label}</option>`)}
|
||||
</select>
|
||||
</label>`;
|
||||
|
||||
Reference in new issue
Block a user