@verbumia/in-context
In-context (on-device) translation editing for Verbumia.
A plugin of your existing @verbumia/react-i18next provider: pair your
running app to the Verbumia editor, and translations the editor changes apply
live, in place — no redeploy, no page reload. The plugin also reports which
strings are on the current screen so the editor can highlight them.
@verbumia/in-context/react — the VerbumiaProvider plugin (web / React).
@verbumia/in-context/native — the same plugin for React Native / Expo.
@verbumia/in-context/core — the framework-agnostic client
(InContextClient, parseEvent, applyEdit) for advanced / other-framework use.
In-context editing is headless: the SDK owns pairing, the live channel,
applying edits, and reporting on-screen keys — your app owns the pairing UI
(a short-code / paste box on web, a QR scan with your own camera on native).
MIT.
Install
npm i @verbumia/in-context
Usage (web / React)
import { VerbumiaProvider } from "@verbumia/react-i18next";
import { inContextPlugin, type InContextController } from "@verbumia/in-context/react";
let controller: InContextController | null = null;
<VerbumiaProvider
token="vrb_live_…"
projectUuid="…"
defaultLocale="fr"
plugins={[
inContextPlugin({
device: "Marketing site · Chrome",
onReady: (c) => (controller = c),
onStatus: (s) => console.log("in-context:", s),
}),
]}
>
<App />
</VerbumiaProvider>;
await controller?.pair(codeFromInput);
router.afterEach(() => controller?.reportKeys());
Usage (React Native / Expo)
Identical, importing from @verbumia/in-context/native. Scan the editor's QR
with your own camera component (e.g. expo-camera / react-native-vision-camera)
and hand the decoded pairing_token to controller.pair(token) — the SDK
bundles no camera. The client uses only JSON-body POSTs (no URLSearchParams),
so it is Hermes-safe.
import { inContextPlugin } from "@verbumia/in-context/native";
inContextPlugin({
device: "iPhone 15 · Expo",
onReady: (c) => setController(c),
});
await controller.pair(decodedPairingToken);
How it works
- Pair —
controller.pair(pairingToken) calls POST /v1/in-context/pair
with the single-use token and gets back a scoped, subscribe-only
Centrifugo sub-token, the session channel, and the realtime rtUrl
(server-provided — never hardcoded).
- Subscribe — opens that channel via the
@verbumia/realtime LiveClient
transport and listens for paired / edit / session_ended.
- Apply edits — each
edit becomes an in-memory i18next override
(addResource) followed by i18n.refresh() (react-i18next ≥1.0.6), which
repaints both the SDK's useTranslation hooks and react-i18next-native
consumers in place, without a CDN re-fetch (so the override isn't
clobbered). On a host pinned to react-i18next <1.0.6 the public hooks won't
repaint until the next render — bump to ≥1.0.6. Edits to a background
language are stored silently and surface when you switch to it.
- Report on-screen keys — on pair, on language change, and whenever you
call
controller.reportKeys(), the keys rendered on the current view (read
from the i18n SDK's __verbumia_key_registry__) are POSTed to
/v1/in-context/sessions/{id}/keys so the editor can highlight them. Each
item is the canonical shape { namespace, key, values? }, where values
carries the value the device rendered on screen (the active locale's raw
template). The SDK omits sourceValue. The backend's /keys endpoint is
pass-through (it relays your items verbatim); the editor dashboard
resolves the authoritative sourceValue and the full per-locale values from
its own project data, so the device never has to know every locale. Reports
are capped at 500 items.
- Teardown — the session ends gracefully on
session_ended or when the
sub-token expires (the pairing token is single-use and can't be re-authed).
Controller API
pair(pairingToken, device?) | Start a session from a pairing token. Re-pairing ends the previous session first. |
reportKeys() | Re-report the keys on screen now (call on navigation). Best-effort. |
end() | Locally tear down the session. |
status | idle | connecting | connected | disconnected | ended. |
sessionId | The server-minted session id once paired. |