Split auth UI module and load FAQ from markdown assets

This commit is contained in:
2026-02-07 02:28:21 +01:00
parent 5f31455651
commit 536e6392f0
6 changed files with 514 additions and 94 deletions

View File

@@ -4,7 +4,7 @@ const translationsAssetUrl = new URL(
"../data/i18n/translations.json",
import.meta.url,
);
const faqAssetUrl = new URL("../data/i18n/faq.json", import.meta.url);
const faqBaseUrl = new URL("../data/i18n/faq/", import.meta.url);
let currentLang = defaultLang;
const listeners = [];
@@ -62,20 +62,6 @@ function validateTranslations(raw) {
return raw;
}
function validateFaq(raw) {
if (!isRecord(raw) || typeof raw[defaultLang] !== "string") {
throw new Error(`Missing default FAQ language "${defaultLang}".`);
}
Object.entries(raw).forEach(([lang, value]) => {
if (typeof value !== "string") {
throw new Error(`Invalid FAQ value for "${lang}".`);
}
});
return raw;
}
async function loadJson(url, label) {
const response = await fetch(url, { cache: "no-store" });
if (!response.ok) {
@@ -85,18 +71,61 @@ async function loadJson(url, label) {
return response.json();
}
async function loadText(url, label) {
const response = await fetch(url, { cache: "no-store" });
if (!response.ok) {
throw new Error(`Failed to load ${label}: ${response.status}`);
}
return response.text();
}
async function loadFaqMarkdownForLanguages(languages) {
if (!Array.isArray(languages) || languages.length === 0) {
throw new Error("No i18n languages available for FAQ loading.");
}
const faqByLanguage = {};
for (const lang of languages) {
const faqUrl = new URL(`${lang}.md`, faqBaseUrl);
try {
faqByLanguage[lang] = await loadText(faqUrl, `faq.${lang}`);
} catch (err) {
if (lang === defaultLang) {
throw err;
}
}
}
const fallback = faqByLanguage[defaultLang];
if (typeof fallback !== "string" || fallback.trim().length === 0) {
throw new Error(`Missing default FAQ language "${defaultLang}".`);
}
languages.forEach((lang) => {
const value = faqByLanguage[lang];
faqByLanguage[lang] =
typeof value === "string" && value.trim().length > 0
? value
: fallback;
});
return faqByLanguage;
}
async function ensureAssetsLoaded() {
if (assetsLoaded) return;
if (assetsLoadingPromise) return assetsLoadingPromise;
assetsLoadingPromise = (async () => {
const [translationsRaw, faqRaw] = await Promise.all([
loadJson(translationsAssetUrl, "translations"),
loadJson(faqAssetUrl, "faq"),
]);
const translationsRaw = await loadJson(
translationsAssetUrl,
"translations",
);
translations = validateTranslations(translationsRaw);
faqMarkdown = validateFaq(faqRaw);
faqMarkdown = await loadFaqMarkdownForLanguages(
Object.keys(translations),
);
assetsLoaded = true;
})().finally(() => {
assetsLoadingPromise = null;