Split auth UI module and load FAQ from markdown assets
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user