translations and pickers
This commit is contained in:
+47
-11
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
const STORAGE_KEY = "rustpad:language";
|
||||
const GUEST_STORAGE_KEY = "rustpad:guest-language";
|
||||
const AUTH_STATE_KEY = "rustpad:auth-state";
|
||||
const FALLBACK_LANGUAGE = "en";
|
||||
const TRANSLATABLE_ATTRIBUTES = ["placeholder", "title", "aria-label", "aria-description"];
|
||||
const SKIP_SELECTOR = [
|
||||
@@ -285,21 +287,50 @@ async function loadBundle(code) {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
function hasAccountSession() {
|
||||
try { return localStorage.getItem(AUTH_STATE_KEY) === "1"; } catch { return false; }
|
||||
}
|
||||
|
||||
function preferredCode() {
|
||||
// English is always the default. A cached language is only used while an
|
||||
// authenticated profile is expected; the server session remains the source
|
||||
// of truth and will re-apply its saved preference after validation.
|
||||
let hasAccountSession = false;
|
||||
let saved = "";
|
||||
try {
|
||||
hasAccountSession = localStorage.getItem("rustpad:auth-state") === "1";
|
||||
if (hasAccountSession) saved = localStorage.getItem(STORAGE_KEY) || "";
|
||||
saved = hasAccountSession()
|
||||
? (localStorage.getItem(STORAGE_KEY) || "")
|
||||
: (localStorage.getItem(GUEST_STORAGE_KEY) || localStorage.getItem(STORAGE_KEY) || "");
|
||||
} catch { /* storage may be blocked */ }
|
||||
if (!hasAccountSession) return FALLBACK_LANGUAGE;
|
||||
const available = new Set(catalog.map(item => item.code));
|
||||
return available.has(saved) ? saved : FALLBACK_LANGUAGE;
|
||||
}
|
||||
|
||||
function persistLanguage(code, scope = "auto") {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, code);
|
||||
const guestScope = scope === "guest" || (scope === "auto" && !hasAccountSession());
|
||||
if (guestScope) localStorage.setItem(GUEST_STORAGE_KEY, code);
|
||||
} catch { /* storage may be blocked */ }
|
||||
}
|
||||
|
||||
export function rememberGuestLanguage(code = activeCode) {
|
||||
try {
|
||||
const available = new Set(catalog.map(item => item.code));
|
||||
const saved = localStorage.getItem(GUEST_STORAGE_KEY);
|
||||
if (available.has(saved)) return;
|
||||
const value = available.has(code) ? code : FALLBACK_LANGUAGE;
|
||||
localStorage.setItem(GUEST_STORAGE_KEY, value);
|
||||
} catch { /* storage may be blocked */ }
|
||||
}
|
||||
|
||||
export async function restoreGuestLanguage() {
|
||||
let code = FALLBACK_LANGUAGE;
|
||||
try { code = localStorage.getItem(GUEST_STORAGE_KEY) || FALLBACK_LANGUAGE; } catch { /* storage may be blocked */ }
|
||||
const applied = await setLanguage(code, { persist: false });
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, applied);
|
||||
localStorage.setItem(GUEST_STORAGE_KEY, applied);
|
||||
} catch { /* storage may be blocked */ }
|
||||
return applied;
|
||||
}
|
||||
|
||||
function applyDocumentLanguage() {
|
||||
const meta = activeBundle?.meta || fallbackBundle?.meta;
|
||||
if (!meta) return;
|
||||
@@ -307,16 +338,14 @@ function applyDocumentLanguage() {
|
||||
document.documentElement.dataset.locale = meta.locale;
|
||||
}
|
||||
|
||||
export async function setLanguage(code, { persist = true } = {}) {
|
||||
export async function setLanguage(code, { persist = true, scope = "auto" } = {}) {
|
||||
await initI18n();
|
||||
const available = catalog.find(item => item.code === code);
|
||||
const target = available ? available.code : FALLBACK_LANGUAGE;
|
||||
activeBundle = await loadBundle(target);
|
||||
activeCode = activeBundle.meta.code;
|
||||
rebuildSourceIndex();
|
||||
if (persist) {
|
||||
try { localStorage.setItem(STORAGE_KEY, activeCode); } catch { /* storage may be blocked */ }
|
||||
}
|
||||
if (persist) persistLanguage(activeCode, scope);
|
||||
applyDocumentLanguage();
|
||||
translateTree(document, true);
|
||||
document.dispatchEvent(new CustomEvent("rustpad:languagechange", {
|
||||
@@ -473,6 +502,13 @@ export function populateLanguageSelect(input, selectedCode = getLanguage()) {
|
||||
}));
|
||||
}
|
||||
|
||||
window.addEventListener("storage", event => {
|
||||
if (event.key !== STORAGE_KEY && event.key !== GUEST_STORAGE_KEY && event.key !== AUTH_STATE_KEY) return;
|
||||
if (event.key === GUEST_STORAGE_KEY && hasAccountSession()) return;
|
||||
const next = preferredCode();
|
||||
if (next !== activeCode) void setLanguage(next, { persist: false });
|
||||
});
|
||||
|
||||
export function formatDateTime(value, options) {
|
||||
const date = value instanceof Date ? value : new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return String(value ?? "");
|
||||
|
||||
Reference in New Issue
Block a user