import zh from "./zh.json";
|
import en from "./en.json";
|
import fr from "./fr.json";
|
import de from "./de.json";
|
import es from "./es.json";
|
import it from "./it.json";
|
import pt from "./pt.json";
|
import ja from "./ja.json";
|
import ko from "./ko.json";
|
import { writable } from "svelte/store";
|
|
export type Locale = "zh" | "en" | "fr" | "de" | "es" | "it" | "pt" | "ja" | "ko";
|
|
const translations: Record<Locale, Record<string, any>> = { zh, en, fr, de, es, it, pt, ja, ko };
|
export const localeRevision = writable(0);
|
|
// Detect system language, default to Chinese
|
function detectLocale(): Locale {
|
const lang = navigator.language.toLowerCase();
|
if (lang.startsWith("zh")) return "zh";
|
if (lang.startsWith("fr")) return "fr";
|
if (lang.startsWith("de")) return "de";
|
if (lang.startsWith("es")) return "es";
|
if (lang.startsWith("it")) return "it";
|
if (lang.startsWith("pt")) return "pt";
|
if (lang.startsWith("ja")) return "ja";
|
if (lang.startsWith("ko")) return "ko";
|
return "en";
|
}
|
|
let currentLocale: Locale = detectLocale();
|
|
function normalizeLocale(locale: string): Locale {
|
const normalized = locale.toLowerCase();
|
if (normalized.startsWith("zh")) return "zh";
|
for (const candidate of ["fr", "de", "es", "it", "pt", "ja", "ko"] as Locale[]) {
|
if (normalized.startsWith(candidate)) return candidate;
|
}
|
return "en";
|
}
|
|
export function setLocale(locale: Locale | string) {
|
const next = normalizeLocale(locale);
|
if (currentLocale !== next) {
|
currentLocale = next;
|
localeRevision.update((value) => value + 1);
|
}
|
}
|
|
export function getLocale(): Locale {
|
return currentLocale;
|
}
|
|
/**
|
* Translate a key path like "status.ready" with optional interpolation.
|
* Example: t("indicator.holdToSpeak", { key: "Ctrl" }) => "按住Ctrl说话"
|
*/
|
export function t(key: string, params?: Record<string, string>): string {
|
const parts = key.split(".");
|
let value: any = readPath(translations[currentLocale], parts);
|
|
if (typeof value !== "string" && currentLocale !== "en") {
|
value = readPath(translations.en, parts);
|
}
|
|
if (typeof value !== "string") return key;
|
|
if (params) {
|
for (const [k, v] of Object.entries(params)) {
|
value = value.replace(`{${k}}`, v);
|
}
|
}
|
|
return value;
|
}
|
|
function readPath(source: Record<string, any>, parts: string[]): any {
|
let value: any = source;
|
for (const part of parts) {
|
if (value == null) return undefined;
|
value = value[part];
|
}
|
return value;
|
}
|