From a8eefafbe9f2f91e8adf44ee5b983926ecad3c63 Mon Sep 17 00:00:00 2001 From: ray2c <160003873+ray2c@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:34:53 +0800 Subject: [PATCH 1/3] `banner.js`: Workaround formatting bug in localized countdown timer for `ar` --- public/banner.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/banner.js b/public/banner.js index 2216db4..6c595a9 100644 --- a/public/banner.js +++ b/public/banner.js @@ -295,10 +295,10 @@ } } - cacheFormattingInfo(1, "day"); - cacheFormattingInfo(2, "hour"); - cacheFormattingInfo(3, "minute"); - cacheFormattingInfo(4, "second"); + cacheFormattingInfo(11, "day"); + cacheFormattingInfo(22, "hour"); + cacheFormattingInfo(33, "minute"); + cacheFormattingInfo(44, "second"); function getLocalizedUnit(value, unit, trimConjunction, trimSuffix) { var offset = getOffset(unit); From 6cbda0d4dab500cf1410e2afa9bd6e60989218e7 Mon Sep 17 00:00:00 2001 From: ray2c <160003873+ray2c@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:15:43 +0800 Subject: [PATCH 2/3] `banner.js`: Factor in language preference list from browser and more robust locales selection --- public/banner.js | 95 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/public/banner.js b/public/banner.js index 6c595a9..cef7bc2 100644 --- a/public/banner.js +++ b/public/banner.js @@ -66,27 +66,79 @@ var params = getScriptParams(); // ── Determine locale ────────────────────────────────────────────────── - function resolveLocale(tag) { - if (!tag) return "en"; - if (messages[tag]) return tag; - var lower = tag.toLowerCase(); - for (var key in messages) { - if (key.toLowerCase() === lower) return key; + function getBestLocale(desired, available) { + var dLen = 0; + if (!desired || !(dLen = desired.length) || !available) return null; + + var d = new Array(dLen); + var dS = new Array(dLen); + var dC = new Array(dLen); + + function matchLang(k, d) { + var kLen = k.length; + var dLen = d.length; + var kChr; + var dChr; + var kBal; + var dBal; + for (var i = 0; (kBal = i < kLen && (kChr = k[i]) !== '-') & (dBal = i < dLen && (dChr = d[i]) !== '-'); i++) { + if (kChr !== dChr && kChr.toLowerCase() !== dChr.toLowerCase()) return ~i; + } + return kBal || dBal ? ~i : i; } - var base = tag.split("-")[0].toLowerCase(); - if (messages[base]) return base; - for (var key2 in messages) { - if (key2.toLowerCase().split("-")[0] === base) return key2; + + var b = null; + var bW = null; + for (var key in available) { + var a = null; + var s = null; + var prio = -1; + var l = null; + var script = null; + for (var i = 0; i < dLen; i++) { + var sep = matchLang(key, desired[i]); + if (sep < 0) continue; + if (!a) try { + a = new Intl.Locale(key); + s = a.script || a.maximize().script; + } catch (e) {} + l = d[i]; + try { + script = l ? l.script || dS[i] : (l = d[i] = new Intl.Locale(desired[i])).script || (dS[i] ||= l.maximize().script); + } catch (e) {} + if (script === s) { + prio = i; + break; + } + } + if (prio < 0) continue; + s = a.script; + var c = a.region; + var w = prio << 5 | (sep + (s ? s.length + 1 : 0) + (c ? c.length + 1 : 0) != key.length) << 4; + if ((s && l.script || c && l.region) && c === l.region) { + w |= !s | !c << 1 | !(s === l.script) << 2; + } else { + w |= 8 | !!c << 2 | !!s << 1; + if (c) try { + w |= c !== (dC[prio] ||= new Intl.Locale(d[prio].language, { script: script }).maximize().region); + } catch (e) {} + } + if (bW === null || w < bW) { + b = key; + bW = w; + } } - return "en"; + return b; } - var locale = resolveLocale( - params.lang || + var locales = navigator.languages; + var preferred = params.lang || document.documentElement.lang || navigator.language || - navigator.userLanguage - ); + navigator.userLanguage; + if (!locales || !locales.length || preferred !== locales[0]) locales = [...preferred.split(',').map(p => p.trim()), ...locales]; + + var locale = messages[locales[0]] ? locales[0] : getBestLocale(locales, messages) || 'en'; // ── Size variant ────────────────────────────────────────────────────── var size = params.size === "mini" ? "mini" : "normal"; @@ -306,8 +358,8 @@ var p = pfx[offset]; var s = sfx[offset]; return string.slice( - trimConjunction && p || (p == 1 && string[0] === "+") ? pfx[offset] : 0, - trimSuffix && s ? -sfx[offset] : string.length + trimConjunction && p || (p == 1 && string[0] === "+") ? p : 0, + trimSuffix && s ? -s : string.length ); } @@ -319,6 +371,11 @@ var now = new Date().getTime(); var distance = countDownDate - now; + if (distance < 0) { + clearInterval(timer); + return; + } + var days = Math.floor(distance / (1000 * 60 * 60 * 24)); var hours = Math.floor( (distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60) @@ -342,10 +399,6 @@ remaining[6] = getLocalizedUnit(seconds, "second", parts++, false); countdownSpan.textContent = remaining.join(""); - - if (distance < 0) { - clearInterval(timer); - } } timer = setInterval(updateBanner, 1000); From c803c6aa27e9472269646ec8d6b9619bc1ec172d Mon Sep 17 00:00:00 2001 From: ray2c <160003873+ray2c@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:04:03 +0800 Subject: [PATCH 3/3] Take 2: simplified --- public/banner.js | 87 ++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 58 deletions(-) diff --git a/public/banner.js b/public/banner.js index cef7bc2..4192c42 100644 --- a/public/banner.js +++ b/public/banner.js @@ -66,79 +66,50 @@ var params = getScriptParams(); // ── Determine locale ────────────────────────────────────────────────── - function getBestLocale(desired, available) { - var dLen = 0; - if (!desired || !(dLen = desired.length) || !available) return null; - - var d = new Array(dLen); - var dS = new Array(dLen); - var dC = new Array(dLen); - - function matchLang(k, d) { - var kLen = k.length; - var dLen = d.length; - var kChr; - var dChr; - var kBal; - var dBal; - for (var i = 0; (kBal = i < kLen && (kChr = k[i]) !== '-') & (dBal = i < dLen && (dChr = d[i]) !== '-'); i++) { - if (kChr !== dChr && kChr.toLowerCase() !== dChr.toLowerCase()) return ~i; - } - return kBal || dBal ? ~i : i; - } + function resolveLocale(tags) { + var preferences = tags.map(tag => { + try { return new Intl.Locale(tag); } catch (e) {} + }); - var b = null; - var bW = null; - for (var key in available) { - var a = null; - var s = null; - var prio = -1; - var l = null; - var script = null; - for (var i = 0; i < dLen; i++) { - var sep = matchLang(key, desired[i]); - if (sep < 0) continue; - if (!a) try { - a = new Intl.Locale(key); - s = a.script || a.maximize().script; - } catch (e) {} - l = d[i]; - try { - script = l ? l.script || dS[i] : (l = d[i] = new Intl.Locale(desired[i])).script || (dS[i] ||= l.maximize().script); - } catch (e) {} - if (script === s) { - prio = i; + var best = null; + var bestWeight = null; + for (var k in messages) { + var key = new Intl.Locale(k); + var script = key.script || key.maximize().script; + var preferred = -1; + var preference = null; + for (var p = 0; p < preferences.length; p++) { + tag = preferences[p]; + if (tag && key.language === tag.language && script === (tag.script || tag.maximize().script)) { + preferred = p; + preference = tag; break; } } - if (prio < 0) continue; - s = a.script; - var c = a.region; - var w = prio << 5 | (sep + (s ? s.length + 1 : 0) + (c ? c.length + 1 : 0) != key.length) << 4; - if ((s && l.script || c && l.region) && c === l.region) { - w |= !s | !c << 1 | !(s === l.script) << 2; + if (preferred < 0) continue; + var country = key.region; + var weight = preferred << 4; + if ((script && preference.script || country && preference.region) && country === preference.region) { + weight |= !script | !country << 1 | !(script === preference.script) << 2; } else { - w |= 8 | !!c << 2 | !!s << 1; - if (c) try { - w |= c !== (dC[prio] ||= new Intl.Locale(d[prio].language, { script: script }).maximize().region); - } catch (e) {} + weight |= 8 | !!country << 2 | !!script << 1 | (country && country !== new Intl.Locale(preference.language, { script: script }).maximize().region); } - if (bW === null || w < bW) { - b = key; - bW = w; + if (bestWeight === null || weight < bestWeight) { + best = key; + bestWeight = weight; } } - return b; + return best; } var locales = navigator.languages; - var preferred = params.lang || + var prefer = params.lang || document.documentElement.lang || navigator.language || navigator.userLanguage; - if (!locales || !locales.length || preferred !== locales[0]) locales = [...preferred.split(',').map(p => p.trim()), ...locales]; + if (!locales || !locales.length || prefer !== locales[0]) locales = [...prefer.split(',').map(p => p.trim()), ...locales]; - var locale = messages[locales[0]] ? locales[0] : getBestLocale(locales, messages) || 'en'; + var locale = messages[locales[0]] ? locales[0] : resolveLocale(locales) || 'en'; // ── Size variant ────────────────────────────────────────────────────── var size = params.size === "mini" ? "mini" : "normal";