Feature chore bug 007 #21
@@ -0,0 +1,56 @@
|
||||
// Minimal hand-rolled i18n — no dependency. A reactive `locale` (persisted to
|
||||
// localStorage, UI ephemera like the layout store) plus a `t(key)` lookup over
|
||||
// per-language JSON dictionaries. English is the fallback for any missing key,
|
||||
// so a partial translation degrades to English rather than showing raw keys.
|
||||
//
|
||||
// Adding a language: create `<code>.json` next to en.json, import it, and add
|
||||
// it to DICTS + LOCALES below. Translating that one file is the whole job.
|
||||
|
||||
import en from "./en.json";
|
||||
|
||||
type Dict = Record<string, string>;
|
||||
|
||||
// Register languages here. en is the source-of-truth key set + fallback.
|
||||
const DICTS: Record<string, Dict> = { en };
|
||||
export const LOCALES: { code: string; label: string }[] = [{ code: "en", label: "English" }];
|
||||
|
||||
const KEY = "wa-locale-v1";
|
||||
|
||||
function load(): string {
|
||||
try {
|
||||
const saved = localStorage.getItem(KEY);
|
||||
if (saved && saved in DICTS) return saved;
|
||||
} catch {
|
||||
/* localStorage unavailable — fall through to default */
|
||||
}
|
||||
return "en";
|
||||
}
|
||||
|
||||
class I18n {
|
||||
locale = $state(load());
|
||||
|
||||
setLocale(code: string) {
|
||||
if (!(code in DICTS)) return;
|
||||
this.locale = code;
|
||||
try {
|
||||
localStorage.setItem(KEY, code);
|
||||
} catch {
|
||||
/* non-fatal: preference just won't persist */
|
||||
}
|
||||
}
|
||||
|
||||
/** Look up `key` in the active locale, falling back to English then the key
|
||||
* itself. `vars` fills `{name}` placeholders. Reads `locale` so components
|
||||
* that call `t()` in markup re-render when the language changes. */
|
||||
t = (key: string, vars?: Record<string, string | number>): string => {
|
||||
const dict = DICTS[this.locale] ?? en;
|
||||
let s = dict[key] ?? (en as Dict)[key] ?? key;
|
||||
if (vars) {
|
||||
for (const [k, v] of Object.entries(vars)) s = s.replaceAll(`{${k}}`, String(v));
|
||||
}
|
||||
return s;
|
||||
};
|
||||
}
|
||||
|
||||
export const i18n = new I18n();
|
||||
export const t = i18n.t;
|
||||
Reference in new issue
Block a user