spark-server/website/assets/legal.js
DESKTOP-L8OUL9F\gfl0034 f941bf4454 feat(web): 补齐官网协议页,并按包名控制会员价格下发
商店审核需要独立的隐私、用户协议和注销页;运营后台也要按应用包名启用或停用价格。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 21:32:53 +08:00

167 lines
7.0 KiB
JavaScript

(() => {
const LANGS = [
"zh-Hans", "zh-Hant", "en-US", "ja", "ko", "es-MX", "fr-FR",
"de-DE", "pt-BR", "ru", "ar-SA", "it", "th", "vi", "id",
];
const UI = {
"zh-Hans": { home: "首页", privacy: "隐私政策", terms: "用户协议", toDark: "暗黑", toLight: "日间" },
"zh-Hant": { home: "首頁", privacy: "隱私政策", terms: "使用者協議", toDark: "深色", toLight: "日間" },
"en-US": { home: "Home", privacy: "Privacy Policy", terms: "Terms of Use", toDark: "Dark", toLight: "Light" },
ja: { home: "ホーム", privacy: "プライバシー", terms: "利用規約", toDark: "ダーク", toLight: "ライト" },
ko: { home: "홈", privacy: "개인정보", terms: "이용약관", toDark: "어둡게", toLight: "밝게" },
"es-MX": { home: "Inicio", privacy: "Privacidad", terms: "Condiciones", toDark: "Oscuro", toLight: "Claro" },
"fr-FR": { home: "Accueil", privacy: "Confidentialité", terms: "Conditions", toDark: "Sombre", toLight: "Clair" },
"de-DE": { home: "Start", privacy: "Datenschutz", terms: "Nutzung", toDark: "Dunkel", toLight: "Hell" },
"pt-BR": { home: "Início", privacy: "Privacidade", terms: "Termos", toDark: "Escuro", toLight: "Claro" },
ru: { home: "Главная", privacy: "Конфиденциальность", terms: "Условия", toDark: "Тёмная", toLight: "Светлая" },
"ar-SA": { home: "الرئيسية", privacy: "الخصوصية", terms: "الشروط", toDark: "داكن", toLight: "فاتح" },
it: { home: "Home", privacy: "Privacy", terms: "Condizioni", toDark: "Scuro", toLight: "Chiaro" },
th: { home: "หน้าแรก", privacy: "ความเป็นส่วนตัว", terms: "ข้อกำหนด", toDark: "มืด", toLight: "สว่าง" },
vi: { home: "Trang chủ", privacy: "Quyền riêng tư", terms: "Điều khoản", toDark: "Tối", toLight: "Sáng" },
id: { home: "Beranda", privacy: "Privasi", terms: "Ketentuan", toDark: "Gelap", toLight: "Terang" },
};
const HTML_LANG = {
"zh-Hans": "zh-CN",
"zh-Hant": "zh-TW",
"en-US": "en",
ja: "ja",
ko: "ko",
"es-MX": "es-MX",
"fr-FR": "fr",
"de-DE": "de",
"pt-BR": "pt-BR",
ru: "ru",
"ar-SA": "ar",
it: "it",
th: "th",
vi: "vi",
id: "id",
};
function norm(tag) {
if (!tag) return "";
const raw = String(tag).trim().replace(/_/g, "-");
if (LANGS.includes(raw)) return raw;
const lower = raw.toLowerCase();
if (lower === "zh" || lower === "zh-cn" || lower === "zh-sg" || lower === "zh-hans") return "zh-Hans";
if (lower === "zh-tw" || lower === "zh-hk" || lower === "zh-mo" || lower === "zh-hant") return "zh-Hant";
if (lower.startsWith("en")) return "en-US";
if (lower.startsWith("ja")) return "ja";
if (lower.startsWith("ko")) return "ko";
if (lower.startsWith("es")) return "es-MX";
if (lower.startsWith("fr")) return "fr-FR";
if (lower.startsWith("de")) return "de-DE";
if (lower.startsWith("pt")) return "pt-BR";
if (lower.startsWith("ru")) return "ru";
if (lower.startsWith("ar")) return "ar-SA";
if (lower.startsWith("it")) return "it";
if (lower.startsWith("th")) return "th";
if (lower.startsWith("vi")) return "vi";
if (lower === "id" || lower.startsWith("id-") || lower.startsWith("in")) return "id";
return "";
}
function detectLang() {
const query = new URLSearchParams(location.search).get("lang");
if (query) return norm(query) || "zh-Hans";
try {
const saved = norm(localStorage.getItem("spark-legal-lang"));
if (saved) return saved;
} catch (_) { /* ignore */ }
const list = navigator.languages && navigator.languages.length
? navigator.languages
: [navigator.language || ""];
for (const item of list) {
const code = norm(item);
if (code) return code;
}
return "zh-Hans";
}
function effectiveTheme() {
const chosen = document.documentElement.getAttribute("data-theme");
if (chosen === "dark" || chosen === "light") return chosen;
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function applyTheme(mode) {
if (mode === "light" || mode === "dark") {
document.documentElement.setAttribute("data-theme", mode);
} else {
document.documentElement.removeAttribute("data-theme");
}
paintThemeButton();
}
function paintThemeButton() {
const button = document.getElementById("theme");
const lang = document.documentElement.getAttribute("data-lang") || "zh-Hans";
const ui = UI[lang] || UI["zh-Hans"];
if (!button) return;
const dark = effectiveTheme() === "dark";
button.textContent = dark ? ui.toLight : ui.toDark;
button.setAttribute("aria-pressed", dark ? "true" : "false");
}
function applyLang(code) {
const safe = LANGS.includes(code) ? code : "zh-Hans";
document.querySelectorAll("article[data-lang]").forEach((node) => {
node.hidden = node.getAttribute("data-lang") !== safe;
});
document.documentElement.lang = HTML_LANG[safe] || "zh-CN";
document.documentElement.dir = safe === "ar-SA" ? "rtl" : "ltr";
document.documentElement.setAttribute("data-lang", safe);
const heading = document.querySelector(`article[data-lang="${safe}"] h1`);
if (heading && heading.textContent) {
document.title = `${heading.textContent.trim()} — Spark Messenger`;
}
const ui = UI[safe];
document.querySelectorAll("[data-i18n]").forEach((node) => {
const key = node.getAttribute("data-i18n");
if (ui[key]) node.textContent = ui[key];
});
const select = document.getElementById("lang");
if (select) select.value = safe;
paintThemeButton();
}
applyLang(detectLang());
try {
const savedTheme = localStorage.getItem("spark-legal-theme");
if (savedTheme === "light" || savedTheme === "dark") applyTheme(savedTheme);
else paintThemeButton();
} catch (_) {
paintThemeButton();
}
const select = document.getElementById("lang");
if (select) {
select.addEventListener("change", () => {
applyLang(select.value);
try { localStorage.setItem("spark-legal-lang", select.value); } catch (_) { /* ignore */ }
const url = new URL(location.href);
url.searchParams.set("lang", select.value);
history.replaceState(null, "", url);
});
}
const themeButton = document.getElementById("theme");
if (themeButton) {
themeButton.addEventListener("click", () => {
const next = effectiveTheme() === "dark" ? "light" : "dark";
applyTheme(next);
try { localStorage.setItem("spark-legal-theme", next); } catch (_) { /* ignore */ }
});
}
const media = window.matchMedia("(prefers-color-scheme: dark)");
const onScheme = () => {
if (!document.documentElement.getAttribute("data-theme")) paintThemeButton();
};
if (typeof media.addEventListener === "function") media.addEventListener("change", onScheme);
else if (typeof media.addListener === "function") media.addListener(onScheme);
})();