@better-translate/core
v1.4.0
Published
Framework-agnostic translation configuration runtime for TypeScript projects.
Maintainers
Readme
Core
@better-translate/core works in any TypeScript project. No framework dependency, no runtime requirement. The same config and the same API apply whether you're in Next.js, Astro, React, Bun, Node.js, a script, or any other TypeScript environment.
Adapters are extensions of core that add framework-specific helpers: locale-aware routing, React context, per-request helpers, etc. But everything is built on top of the same core.
When to use core directly
Use core without an adapter when you don't need framework-specific helpers. This includes servers, APIs, scripts, shared libraries, or any TypeScript environment where no adapter exists for your setup.
1. Install the package
npm install @better-translate/core2. Create one translator file
Create src/i18n.ts:
import { configureTranslations } from "@better-translate/core";
const en = {
home: {
title: "Hello",
description: "This is the English version.",
},
} as const;
const es = {
home: {
title: "Hola",
description: "Esta es la version en espanol.",
},
} as const;
export const translator = await configureTranslations({
availableLocales: ["en", "es"] as const,
defaultLocale: "en",
fallbackLocale: "en",
messages: { en, es },
});3. Use the translator anywhere
import { translator } from "./i18n";
translator.t("home.title"); // "Hello"
translator.t("home.title", { locale: "es" }); // "Hola"
translator.t("home.description", { locale: "es" });4. Add another locale later
const fr = {
home: {
title: "Bonjour",
description: "Ceci est la version francaise.",
},
} as const;
export const translator = await configureTranslations({
availableLocales: ["en", "es", "fr"] as const,
defaultLocale: "en",
fallbackLocale: "en",
messages: { en, es, fr },
});Fallback behavior
If the active locale doesn't have a key, better-translate tries the fallback locale. If the fallback doesn't have it either, it returns the key string itself.
- Missing locale value -> fallback locale value
- Missing fallback value -> key string
Optional locales
By default, every locale in availableLocales must either have messages or an
async loader. Preloaded locale messages are type-checked against the default
locale so missing keys fail during TypeScript checks.
If a locale is intentionally incomplete, mark it optional:
export const translator = await configureTranslations({
availableLocales: ["en", "es", "fr"] as const,
defaultLocale: "en",
fallbackLocale: "en",
optionalLocales: ["fr"],
messages: {
en,
es,
fr: {
home: {
title: "Bonjour",
},
},
},
});Optional locales may be omitted from messages and loaders, or may provide a
partial message tree. Missing keys use the configured fallback locale at
runtime.
You can also mark a locale optional in its language metadata when the locale has no preloaded messages or loader yet:
export const translator = await configureTranslations({
availableLocales: ["en", "es"] as const,
defaultLocale: "en",
fallbackLocale: "en",
languages: [
{
locale: "es",
nativeLabel: "Español",
optional: true,
shortLabel: "ES",
},
],
messages: { en },
});Use optionalLocales when you provide a partial message object and want
TypeScript to allow missing nested keys. Do not mark the default locale
optional. The default locale remains the source for translation key and
placeholder inference.
Async loaders
Register locale loaders for languages you don't want to preload. Loaded locales are cached after the first successful load.
const translator = await configureTranslations({
availableLocales: ["en", "fr"] as const,
defaultLocale: "en",
messages: { en },
loaders: {
fr: async () => import("./messages/fr").then((m) => m.default),
},
});
// loads fr on demand and caches it
await translator.loadLocale("fr");5. Auto-extract strings with the CLI
Instead of naming keys by hand, mark strings with { bt: true } and let the CLI extract and key them automatically:
import { t } from "@better-translate/core";
// Write the source string directly — CLI converts it to a proper key
t("Hello world", { bt: true });Run npx bt extract to sync the strings into your source locale file and rewrite calls to proper keys. See the CLI docs for the full setup.
Locale route param names
If you use a routing adapter, Better Translate exports the canonical locale param-name list from core:
import { SUPPORTED_LOCALE_ROUTE_SYNTAXES } from "@better-translate/core";That list is:
["locale", "lang", "language", "intl", "i18n", "l10n", "localization"];Framework adapters format those names into their own route syntax, like "$lang" in TanStack Router or "[lang]" in Next.js and Astro.
Continue with
Examples
- core-elysia-example — plain TypeScript/Node.js setup
