feat(i18n): add Khmer language support

Register Khmer (km) locale, add complete 1,394-entry dashboard translation,
and show the Cambodia flag in the language selector. Place Khmer immediately
before Thai in the selector and keep the header locale in sync after switching.
This commit is contained in:
seakleang.nhak 2026-07-19 16:30:13 +07:00 committed by decolua
parent 55628eea02
commit 9ba8f37486
4 changed files with 1406 additions and 3 deletions

1396
public/i18n/literals/km.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@ export const LOCALES = [
"uk", "uk",
"tl", "tl",
"id", "id",
"km",
"th", "th",
"hi", "hi",
"bn", "bn",
@ -60,6 +61,7 @@ export const LOCALE_NAMES = {
tl: "Tagalog", tl: "Tagalog",
id: "Indonesia", id: "Indonesia",
th: "ไทย", th: "ไทย",
km: "ខ្មែរ",
hi: "हिन्दी", hi: "हिन्दी",
bn: "বাংলা", bn: "বাংলা",
ur: "اردو", ur: "اردو",
@ -141,6 +143,9 @@ export function normalizeLocale(locale) {
if (locale === "th") { if (locale === "th") {
return "th"; return "th";
} }
if (locale === "km") {
return "km";
}
if (locale === "hi") { if (locale === "hi") {
return "hi"; return "hi";
} }

View file

@ -39,6 +39,7 @@ const getLocaleInfo = (locale) => {
"tl": { name: "Tagalog", flag: "🇵🇭" }, "tl": { name: "Tagalog", flag: "🇵🇭" },
"id": { name: "Indonesia", flag: "🇮🇩" }, "id": { name: "Indonesia", flag: "🇮🇩" },
"th": { name: "ไทย", flag: "🇹🇭" }, "th": { name: "ไทย", flag: "🇹🇭" },
"km": { name: "ខ្មែរ", flag: "🇰🇭" },
"hi": { name: "हिन्दी", flag: "🇮🇳" }, "hi": { name: "हिन्दी", flag: "🇮🇳" },
"bn": { name: "বাংলা", flag: "🇧🇩" }, "bn": { name: "বাংলা", flag: "🇧🇩" },
"ur": { name: "اردو", flag: "🇵🇰" }, "ur": { name: "اردو", flag: "🇵🇰" },
@ -63,9 +64,9 @@ export default function LanguageSwitcher({ className = "", isOpen: controlledOpe
const isControlled = typeof controlledOpen === "boolean"; const isControlled = typeof controlledOpen === "boolean";
const isOpen = isControlled ? controlledOpen : internalOpen; const isOpen = isControlled ? controlledOpen : internalOpen;
const setIsOpen = (value) => { const setIsOpen = (value, nextLocale = locale) => {
if (isControlled) { if (isControlled) {
if (!value && onClose) onClose(locale); if (!value && onClose) onClose(nextLocale);
} else { } else {
setInternalOpen(value); setInternalOpen(value);
} }
@ -92,7 +93,6 @@ export default function LanguageSwitcher({ className = "", isOpen: controlledOpe
if (nextLocale === locale || isPending) return; if (nextLocale === locale || isPending) return;
setIsPending(true); setIsPending(true);
setIsOpen(false);
try { try {
await fetch("/api/locale", { await fetch("/api/locale", {
method: "POST", method: "POST",
@ -103,6 +103,7 @@ export default function LanguageSwitcher({ className = "", isOpen: controlledOpe
// Reload translations without full page reload // Reload translations without full page reload
await reloadTranslations(); await reloadTranslations();
setLocale(nextLocale); setLocale(nextLocale);
setIsOpen(false, nextLocale);
} catch (err) { } catch (err) {
console.error("Failed to set locale:", err); console.error("Failed to set locale:", err);
} finally { } finally {

View file

@ -22,6 +22,7 @@ export const LOCALE_FLAGS = {
"tl": "🇵🇭", "tl": "🇵🇭",
"id": "🇮🇩", "id": "🇮🇩",
"th": "🇹🇭", "th": "🇹🇭",
"km": "🇰🇭",
"hi": "🇮🇳", "hi": "🇮🇳",
"bn": "🇧🇩", "bn": "🇧🇩",
"ur": "🇵🇰", "ur": "🇵🇰",