Setup
createI18n options
import { createI18n } from "@domphy/i18n"
const en = {
greeting: "Hello, {{name}}!",
items_one: "{{count}} item",
items_other: "{{count}} items",
} as const
const i18n = createI18n<"en" | "fr" | "vi", typeof en>({
/** Unique key on globalThis — must differ per app to avoid cross-app collision. */
globalKey: "__myapp_i18n__",
/** i18next resource namespace. */
namespace: "app",
/** Translation objects keyed by locale code. */
locales: {
en,
fr: { greeting: "Bonjour, {{name}}!", items_one: "{{count}} article", items_other: "{{count}} articles" },
vi: { greeting: "Xin chào, {{name}}!", items_one: "{{count}} mục", items_other: "{{count}} mục" },
},
/** Locale used before initI18n() is called. */
defaultLocale: "en",
})
await i18n.initI18n()Reactive translation
Pass a listener to re-render when the locale changes:
const { t } = i18n
const Header = {
h1: (l) => t(l, "greeting", { name: "World" }),
}When no listener is needed (outside reactive context):
const label = t("greeting", { name: "World" })Locale switching
const { setLocale, getLocale } = i18n
// Switch locale — all reactive t(l, ...) re-render automatically
await setLocale("vi")
// Read current locale
console.log(getLocale()) // "vi"Locale detection
detectLocale reads from the URL path segment or localStorage and returns the best matching locale:
const { detectLocale, initI18n } = i18n
await initI18n(detectLocale({ pathSegment: true, storageKey: "locale" }))globalThis dedup
createI18n registers the i18next instance on globalThis[globalKey]. This ensures a single instance across Vite code-split chunks — multiple calls with the same globalKey return the same instance.
On the server, resources stay on that shared store, but the locale is request-scoped (AsyncLocalStorage). Two concurrent initI18n("en") and initI18n("vi") calls do not clobber each other. Use runWithI18n() to wrap a request when the runtime does not already isolate async context (Node HTTP does). See Lazy Loading & Backend — SSR.
i18next plugin integration
The underlying i18next instance is created internally. To add plugins (HTTP backend, language detector, etc.) use i18next directly alongside the wrapper, or access the exported i18n type for integration:
import { createInstance } from "i18next"
import HttpBackend from "i18next-http-backend"
// For advanced use cases, create a pre-configured instance and pass locale messages:
const resources = {
en: { app: { greeting: "Hello!" } },
vi: { app: { greeting: "Xin chào!" } },
}
// @domphy/i18n bundles the messages at createI18n time — for HTTP-loaded messages,
// fetch them before calling createI18n and pass the resolved objects in locales.