npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@better-translate/core

v1.4.0

Published

Framework-agnostic translation configuration runtime for TypeScript projects.

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/core

2. 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