import { en } from "./locales/en"; type NestedStrings = { [key: string]: string | NestedStrings }; export type Translations = NestedStrings; function getByPath(obj: Record, path: string): string | undefined { const parts = path.split("."); let current: unknown = obj; for (const part of parts) { if (current == null || typeof current !== "object") return undefined; current = (current as Record)[part]; } return typeof current === "string" ? current : undefined; } function interpolate(template: string, params: Record): string { return template.replace(/\{(\w+)\}/g, (_, key: string) => { return key in params ? String(params[key]) : `{${key}}`; }); } const cache = new Map(); export async function loadTranslations( lang: string, kv?: { get(key: string, type: "json"): Promise }, ): Promise { if (lang === "en") return en; const cached = cache.get(lang); if (cached) return cached; if (!kv) return en; try { const stored = await kv.get>(`i18n:${lang}`, "json"); if (stored) { const merged = { ...en, ...stored } as Translations; cache.set(lang, merged); return merged; } } catch { // KV read failed, fall back to EN } return en; } export function t( key: string, params?: Record, lang?: string | null, translations?: Translations, ): string { const dict = translations ?? en; const raw = getByPath(dict as Record, key) ?? getByPath(en as Record, key) ?? key; return params ? interpolate(raw, params) : raw; }