Optimize images and convert PNG to WebP

This commit is contained in:
tistopandita
2026-06-05 08:34:24 +07:00
parent 00cc837fa1
commit 693e41af0a
14 changed files with 2090 additions and 2662 deletions
+99
View File
@@ -1,3 +1,29 @@
/**
* localization.js — HarusSelesaiKP
*
* ANTI-FLICKER STRATEGY:
* 1. IIFE `initLanguage()` runs SYNCHRONOUSLY (no defer/async) to set
* `window.__currentLang` before the browser paints any content.
* 2. HTML body carries `lang-pending` class; body is hidden via CSS until
* `applyTranslations()` fires on DOMContentLoaded and removes the class.
* 3. `languageChanged` CustomEvent signals dynamic sections (Facilities,
* Campus, Culinary, LivingCost) to re-render in the new language.
*/
// ─── 1. Determine language SYNCHRONOUSLY (before first paint) ────────────────
(function initLanguage() {
try {
const saved = localStorage.getItem("lang");
const browserLang = navigator.language?.startsWith("id") ? "id" : "en";
window.__currentLang = saved || browserLang;
} catch (e) {
window.__currentLang = "id";
}
// Stamp <html lang> immediately so screen readers get the right language
document.documentElement.lang = window.__currentLang;
})();
// ─── 2. Translation data ─────────────────────────────────────────────────────
const translations = {
id: {
// Kelompok HarusSelesaiKP (skp)
@@ -330,3 +356,76 @@ const translations = {
skp_cost_type_transport: "Transport",
},
};
// ─── 3. Public translation helper ────────────────────────────────────────────
/**
* t(key) — returns translated string for the active language.
* Falls back to Indonesian if key is missing in the active language.
*/
function t(key) {
const lang = window.__currentLang || "id";
return translations[lang]?.[key] ?? translations["id"]?.[key] ?? key;
}
// ─── 4. Apply translations to all [data-i18n] elements ───────────────────────
function applyTranslations(lang) {
window.__currentLang = lang;
document.documentElement.lang = lang;
try {
localStorage.setItem("lang", lang);
} catch (e) {
// localStorage may be blocked (private mode, etc.) — fail silently
}
document.querySelectorAll("[data-i18n]").forEach((el) => {
const key = el.dataset.i18n;
const text = t(key);
if (text && text !== key) {
el.textContent = text;
}
});
// Signal dynamic components to re-render
document.dispatchEvent(new CustomEvent("languageChanged"));
}
// ─── 5. Toggle language (called by UI buttons) ───────────────────────────────
function toggleLanguage() {
const next = window.__currentLang === "id" ? "en" : "id";
applyTranslations(next);
updateLanguageButtonUI(next);
}
function updateLanguageButtonUI(lang) {
document.querySelectorAll(".language-toggle-btn").forEach((btn) => {
const idSpan = btn.querySelector(".lang-id");
const enSpan = btn.querySelector(".lang-en");
if (idSpan && enSpan) {
if (lang === "id") {
idSpan.className = "lang-id font-bold text-white";
enSpan.className = "lang-en font-normal opacity-50";
} else {
idSpan.className = "lang-id font-normal opacity-50";
enSpan.className = "lang-en font-bold text-white";
}
}
});
}
// ─── 6. Boot on DOMContentLoaded ─────────────────────────────────────────────
document.addEventListener("DOMContentLoaded", () => {
// Apply translations to all static [data-i18n] nodes
applyTranslations(window.__currentLang);
// Sync language button label
updateLanguageButtonUI(window.__currentLang);
// Wire up toggle buttons
document.querySelectorAll(".language-toggle-btn").forEach((btn) => {
btn.addEventListener("click", toggleLanguage);
});
// ── Reveal body: remove anti-flicker class after translations applied ──
document.body.classList.remove("lang-pending");
});