@domphy/i18n
v0.19.5
Published
Domphy i18n — generic i18next wrapper with reactive Domphy integration (globalThis dedup, typed keys, t(listener, key) overload)
Maintainers
Readme
@domphy/i18n
domphy.com · Docs · npm
Reactive i18next wrapper for Domphy. When the locale changes, any UI element that called t(listener, key) re-renders automatically — no manual subscriptions.
Install
npm install @domphy/i18n i18next@domphy/core is a peer dependency.
Quick start
import { createI18n } from "@domphy/i18n"
const en = { hello: "Hello, {{name}}!", save: "Save" } as const
const i18n = createI18n<"en" | "vi", typeof en>({
globalKey: "__myapp_i18n__", // unique per app; deduplicates across Vite chunks + SSR
namespace: "app",
locales: {
en,
vi: { hello: "Xin chào, {{name}}!", save: "Lưu" },
},
defaultLocale: "en",
})
await i18n.initI18n()Reactive usage
const { t, setLocale, getLocale } = i18n
// Reactive — re-renders when setLocale() is called
const Greeting = {
p: (l) => t(l, "hello", { name: "World" }),
}
// Non-reactive (outside element tree)
const label = t("save")Locale switching
await setLocale("vi")
console.log(getLocale()) // "vi"All elements using t(listener, key) re-render automatically.
Locale detection
const detected = i18n.detectLocale({ pathSegment: true })
await i18n.initI18n(detected)Priority: URL path prefix (/vi/...) → localStorage key → defaultLocale.
Type-safe keys
Pass your translation object as a generic to get full key inference:
const { t } = createI18n<"en" | "fr", typeof en>({ ... })
t("hello") // ✓
t("nav.missing") // ✗ TypeScript errorAPI
| Member | Signature | Description |
|---|---|---|
| t | (key, opts?) → string | Static translation |
| t | (listener, key, opts?) → string | Reactive translation |
| locale | State<TLocale> | Reactive locale state |
| currentLocale | (listener) → TLocale | Reactive locale code (sugar for locale.get(listener)) |
| exists | (key) → boolean | Check key presence in active locale |
| initI18n | (locale?) → Promise<void> | Initialize i18next |
| setLocale | (locale) → Promise<void> | Switch locale, trigger re-renders |
| getLocale | () → TLocale | Current locale (non-reactive) |
| detectLocale | (opts?) → TLocale | Detect locale from URL/localStorage |
Module export runWithI18n(fn) — fresh request-locale scope (SSR). No-op on the client.
createI18n also accepts an optional interpolation: { escapeValue?: boolean } — defaults to true (i18next's own safe default, HTML-escapes interpolated values); pass false to disable escaping globally.
On the server, initI18n / setLocale do not mutate the shared globalThis store's language. The request locale is stored in AsyncLocalStorage, so two concurrent initI18n("en") and initI18n("vi") calls do not clobber each other. The client still dedups via globalThis[globalKey]. Node HTTP already isolates requests; wrap other SSR entry points with runWithI18n().
See the full API reference for details.
