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

@verbumia/react-i18next

v0.9.0

Published

React SDK for Verbumia — translations + realtime missing-key handler.

Downloads

1,810

Readme

@verbumia/react-i18next

MIT licensed

The React SDK for Verbumia. Resolve translations from the Verbumia CDN, fall back gracefully when a key is missing, and stream those missing keys back to your dashboard in real time so the team can fill them without redeploying.

npm install @verbumia/react-i18next
  • ✦ Zero-config CDN fetch (Bunny.net edge)
  • ✦ Built-in missing-key handler with first-paint anti-spam gate
  • ✦ Pluggable transport for Storybook / inspectors
  • ✦ < 10 KB ESM (gzipped much smaller), tree-shakeable
  • ✦ Plain t() + <Trans> semantics — drop-in for most i18next codebases

Quickstart

import { VerbumiaProvider, useTranslation } from "@verbumia/react-i18next";

export function App() {
  return (
    <VerbumiaProvider
      token={import.meta.env.VITE_VERBUMIA_TOKEN}
      projectUuid={import.meta.env.VITE_VERBUMIA_PROJECT}
      defaultLocale="fr"
      fallbackLng="en"
      namespaces={["common"]}
    >
      <Hello />
    </VerbumiaProvider>
  );
}

function Hello() {
  const { t, i18n } = useTranslation("common");
  if (!i18n.ready) return <span>Loading…</span>;
  return <h1>{t("hello.title", { name: "Marc", defaultValue: "Hello {{name}}" })}</h1>;
}

The token is the API key minted in Org Settings → API Keys. For the browser SDK use a project-scoped key with the missing:write scope and nothing else — that key only sees missing-key writes for one project, which is the safest exposure profile.


API surface

VerbumiaProvider

interface VerbumiaConfig {
  token: string;                  // vrb_live_<prefix>.<secret>
  projectUuid: string;
  defaultLocale: string;          // BCP-47 (e.g. "fr", "fr-CA")
  fallbackLng?: string;           // resolved before reporting a key as missing
  namespaces?: string[];          // default ['common']
  defaultNS?: string;             // alias: default namespace for single-ns apps
  apiBase?: string;               // default 'https://api.verbumia.dev'
  cdnBase?: string;               // default 'https://cdn.verbumia.ca'
  version?: string;               // version slug, default 'main' (in cache keys)
  versionSlug?: string;           // @deprecated alias of `version`
  env?: 'prod' | 'dev';           // default 'prod' (drives fetch source)
  plugins?: VerbumiaPlugin[];     // e.g. @verbumia/feedback, @verbumia/realtime
  transport?: (batch: MissingKeyEvent[]) => void | Promise<void>;
  missingHandler?: 'send' | 'log' | 'off';   // default 'send'
  flushIntervalMs?: number;       // default 5000
  flushBatchSize?: number;        // default 50
  missingEventsBufferSize?: number; // default 200
}

version selects which published version's bundles to load (/p/<project>/<version>/latest/...); it defaults to 'main' and is part of the SDK's bundle cache keys, so two providers with different version values never share cached bundles. versionSlug is a deprecated alias of version (if both are set, version wins).

Removed in 0.9.0: the liveUpdates / centrifugoWsUrl / centrifugoTokenEndpoint config keys. Realtime updates now live in the separate @verbumia/realtime plugin (see Realtime updates). Passing any of those keys throws a clear migration error.


### `useTranslation(defaultNamespace?)`

Returns `{ t, i18n }`.

```ts
// Two call shapes — the native object form AND the react-i18next-style
// positional fallback (a string 2nd arg is the default value):
type TranslationFunction = {
  (key: string, defaultValue: string, options?: Record<string, unknown>): string;
  (key: string, options?: Record<string, unknown> & { defaultValue?: string }): string;
};

interface I18nInstance {
  ready: boolean;
  locale: string;
  language: string;                             // alias of `locale`
  setLocale(next: string): Promise<void>;
  changeLanguage(next: string): Promise<void>;  // alias of `setLocale`
  t: TranslationFunction;                       // for out-of-React use via getI18n()
  missingEvents: MissingKeyEvent[];             // newest first, capped buffer
  flushMissing(): Promise<void>;                // force-flush the pending batch
  reload(opts?: { locale?: string; namespace?: string }): Promise<void>;
}

i18n.reload(opts?)

Bust-refetches already-loaded bundles (bypassing the browser HTTP cache) and re-renders. Without opts it refreshes every loaded (locale, ns) bundle; pass { locale } and/or { namespace } to narrow. Returns once all refetches settle. Useful for a manual "refresh translations" button, and it's what @verbumia/realtime calls on a translations_published push.

await getI18n().reload();                                  // refresh all
await getI18n().reload({ locale: "fr", namespace: "common" });

<Trans>

Inline translation with JSX slots:

<Trans
  i18nKey="cta.terms"
  defaults="I accept the <0>terms</0> and <1>privacy policy</1>"
  components={[<a href="/terms" />, <a href="/privacy" />]}
/>

The <0>...</0> slots are 0-indexed into components. The bundle string should follow the same shape so that translators see I accept the <0>terms</0>... and the SDK swaps the elements at render time.


Migrating from react-i18next

@verbumia/react-i18next is built to be a near drop-in for react-i18next, so existing codebases migrate with minimal changes:

  • Positional default valuet('key', 'Default text') works (so does t('key', 'Hi {{name}}', { name })), alongside the native t('key', { defaultValue }). No codemod needed for inline fallbacks.

  • changeLanguage / languagei18n.changeLanguage('en') (alias of setLocale) and the i18n.language getter (alias of locale) are available.

  • Out-of-React accessgetI18n() returns the active instance for use in plain modules, stores, or helpers (the react-i18next standalone-singleton pattern):

    import { getI18n } from "@verbumia/react-i18next";
    // anywhere after <VerbumiaProvider> has mounted:
    const label = getI18n().t("nav.home", "Home");
    await getI18n().changeLanguage("en");

    getI18n() throws a clear error if no provider is mounted yet, and assumes a single app-wide provider.

  • Default namespace — the default is ['common'] (not react-i18next's 'translation'). Migrants pass namespaces={['translation']}, or the defaultNS="translation" alias for single-namespace apps.

Not yet supported (planned for V1.1)

Plurals and context are not resolved yet: t('key', { count }) performs interpolation only — it does not select plural keys (key_one / key_other) or context keys (key_male). Handle these manually until V1.1.


Missing-key flow

  1. The user navigates a page that calls t("hello.title").
  2. The bundle for (locale, namespace) was already fetched but doesn't contain hello.title. (i18n.ready === true and the bundle for that tuple is in the "attempted" set — this is the gate.)
  3. The SDK enqueues a MissingKeyEvent, dedups it within the instance, and pushes it into the missingEvents ring buffer.
  4. Every flushIntervalMs (default 5s) — or sooner if the batch hits flushBatchSize (default 50) — the SDK flushes the pending batch via the transport.
interface MissingKeyEvent {
  key: string;
  namespace: string;
  language_code: string;
  source_value?: string;
  sdk_meta?: Record<string, unknown>;  // SDK adds {lib, ver, url} automatically
}

Why the gate matters

Without the gate, every t("…") call between mount and bundle resolution would report a "missing" key — which is a lie (the bundle just hadn't arrived yet). The first-paint flood would poison your dashboard. The SDK holds reports until both:

  • i18n.ready === true (initial bundles loaded), AND
  • the specific (locale, namespace) bundle was actually fetched.

You can see the gate in action with i18n.missingEvents — it stays empty until the network round-trip completes.


Custom transport

Replace the default POST with anything — Storybook mock, in-app inspector, Cypress capture:

<VerbumiaProvider
  {...config}
  transport={(batch) => {
    window.parent.postMessage({ type: "verbumia:missing", batch }, "*");
  }}
>
  ...
</VerbumiaProvider>

The default delivery path is also exported if you need to wrap it:

import { defaultTransport, logTransport } from "@verbumia/react-i18next";

Realtime updates

Zero-deploy translation updates (subscribe to the project's Centrifugo translations: channel and bust-refetch on publish) live in the separate @verbumia/realtime package — added as a plugin of this provider, not configured here:

import { VerbumiaProvider } from "@verbumia/react-i18next";
import { verbumiaRealtime } from "@verbumia/realtime/react";

<VerbumiaProvider
  {...config}
  env="dev"
  plugins={[
    verbumiaRealtime({ wsUrl: "wss://centrifugo.verbumia.ca/connection/websocket" }),
  ]}
>
  <App />
</VerbumiaProvider>;

Under the hood the plugin calls i18n.reload(...) on each translations_published push. Realtime is a dev-version-only feature (it only subscribes when env: "dev").

The liveUpdates / centrifugoWsUrl / centrifugoTokenEndpoint config keys were removed in 0.9.0. Install @verbumia/realtime and use the plugin instead.


Recipes

Next.js (App Router)

Wrap the SDK in a Client Component and feed it env vars from .env.local:

// app/(verbumia)/i18n-client.tsx
"use client";
import { VerbumiaProvider } from "@verbumia/react-i18next";

export function I18nClient({ children }: { children: React.ReactNode }) {
  return (
    <VerbumiaProvider
      token={process.env.NEXT_PUBLIC_VERBUMIA_TOKEN!}
      projectUuid={process.env.NEXT_PUBLIC_VERBUMIA_PROJECT!}
      defaultLocale="fr"
      fallbackLng="en"
    >
      {children}
    </VerbumiaProvider>
  );
}

The provider reads the bundle via the public CDN — no server-side state to hydrate. SSR pre-renders the defaultValue and the client smoothly upgrades after i18n.ready flips.

Storybook

// .storybook/preview.tsx
import { VerbumiaProvider } from "@verbumia/react-i18next";

export const decorators = [
  (Story) => (
    <VerbumiaProvider
      token="vrb_live_storybook.fake"
      projectUuid="storybook"
      defaultLocale="fr"
      missingHandler="log"
      transport={(batch) => action("missing-keys")(batch)}
    >
      <Story />
    </VerbumiaProvider>
  ),
];

Cypress

cy.intercept("POST", "**/v1/missing", (req) => {
  cy.task("captureMissing", req.body);
  req.reply({ accepted: req.body.events.length, rejected: 0, items: [] });
});

Versioning

Semver. V1.x will keep the public API stable. Internal changes (bundle fetcher, dedup heuristics) may shift in patch releases.

Breaking changes pre-V1 are flagged in CONTRACT.md.

License

MIT — see LICENSE.