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:
File diff suppressed because one or more lines are too long
@@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const LOCALE_FLAGS = {
|
|||||||
"tl": "🇵🇭",
|
"tl": "🇵🇭",
|
||||||
"id": "🇮🇩",
|
"id": "🇮🇩",
|
||||||
"th": "🇹🇭",
|
"th": "🇹🇭",
|
||||||
|
"km": "🇰🇭",
|
||||||
"hi": "🇮🇳",
|
"hi": "🇮🇳",
|
||||||
"bn": "🇧🇩",
|
"bn": "🇧🇩",
|
||||||
"ur": "🇵🇰",
|
"ur": "🇵🇰",
|
||||||
|
|||||||
Reference in New Issue
Block a user