convertor/public/i18n.js
Your Name 569c572e62 fix(ci): resolve all lint errors for GitHub Actions
- Remove unused SupportedLocale import in results.tsx
- Remove unused webroot variable in public/i18n.js
- Configure knip.json to ignore i18n public API exports
- Add language-selector and language-option to ESLint ignore list
- Auto-fix Prettier formatting across all files
- Fix better-tailwindcss line wrapping issues
2026-01-20 12:16:13 +08:00

143 lines
4.2 KiB
JavaScript

// Client-side i18n helper
(function () {
const LOCALE_COOKIE_NAME = "locale";
const LOCALE_EXPIRY_DAYS = 365;
// Get current locale from meta tag or cookie
function getCurrentLocale() {
const metaLocale = document.querySelector("meta[name='locale']")?.content;
if (metaLocale) return metaLocale;
const cookieLocale = getCookie(LOCALE_COOKIE_NAME);
if (cookieLocale) return cookieLocale;
return "en";
}
// Get cookie value
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(";").shift();
return null;
}
// Set cookie value
function setCookie(name, value, days) {
const expires = new Date();
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=Strict`;
}
// Get translation from window.__TRANSLATIONS__
function t(category, key, params) {
const translations = window.__TRANSLATIONS__ || {};
let text = translations[category]?.[key];
if (!text) {
console.warn(`Missing translation: ${category}.${key}`);
return `${category}.${key}`;
}
// Interpolate params
if (params) {
text = text.replace(/\{(\w+)\}/g, (match, paramKey) => {
return params[paramKey] !== undefined ? String(params[paramKey]) : match;
});
}
return text;
}
// Expose t function globally
window.t = t;
window.getCurrentLocale = getCurrentLocale;
// Language selector functionality
document.addEventListener("DOMContentLoaded", () => {
const toggle = document.getElementById("language-toggle");
const dropdown = document.getElementById("language-dropdown");
if (!toggle || !dropdown) return;
// Toggle dropdown
toggle.addEventListener("click", (e) => {
e.stopPropagation();
const isOpen = !dropdown.classList.contains("hidden");
if (isOpen) {
dropdown.classList.add("hidden");
dropdown.classList.remove("flex");
toggle.setAttribute("aria-expanded", "false");
} else {
dropdown.classList.remove("hidden");
dropdown.classList.add("flex");
toggle.setAttribute("aria-expanded", "true");
}
});
// Close dropdown when clicking outside
document.addEventListener("click", (e) => {
if (!toggle.contains(e.target) && !dropdown.contains(e.target)) {
dropdown.classList.add("hidden");
dropdown.classList.remove("flex");
toggle.setAttribute("aria-expanded", "false");
}
});
// Handle language selection
const options = document.querySelectorAll(".language-option");
options.forEach((option) => {
option.addEventListener("click", () => {
const locale = option.dataset.locale;
// webroot is available in option.dataset.webroot if needed for future use
// Save to cookie
setCookie(LOCALE_COOKIE_NAME, locale, LOCALE_EXPIRY_DAYS);
// Reload the page to apply the new locale
window.location.reload();
});
});
// Keyboard navigation for dropdown
toggle.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggle.click();
}
});
dropdown.addEventListener("keydown", (e) => {
const items = [...dropdown.querySelectorAll(".language-option")];
const currentIndex = items.indexOf(document.activeElement);
switch (e.key) {
case "ArrowDown":
e.preventDefault();
if (currentIndex < items.length - 1) {
items[currentIndex + 1].focus();
}
break;
case "ArrowUp":
e.preventDefault();
if (currentIndex > 0) {
items[currentIndex - 1].focus();
}
break;
case "Escape":
dropdown.classList.add("hidden");
dropdown.classList.remove("flex");
toggle.setAttribute("aria-expanded", "false");
toggle.focus();
break;
case "Enter":
case " ":
if (document.activeElement.classList.contains("language-option")) {
document.activeElement.click();
}
break;
}
});
});
})();