@sonenta/in-context
v1.3.2
Published
In-context (on-device) translation editing for Sonenta — pair a live app to the editor and see edits apply in place. React (web) + React Native / Expo.
Maintainers
Readme
@sonenta/in-context
In-context (on-device) translation editing for Sonenta.
A plugin of your existing @sonenta/react-i18next provider: pair your
running app to the Sonenta 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.
@sonenta/in-context/react— theSonentaProviderplugin (web / React).@sonenta/in-context/native— the same plugin for React Native / Expo.@sonenta/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 @sonenta/in-context
# peer: @sonenta/react-i18next >= 1.0.6 (needed so live edits repaint the
# public useTranslation hooks in place — see "Apply edits" below)
# @sonenta/realtime (the Centrifugo transport) is pulled in automaticallyUsage (web / React)
import { SonentaProvider } from "@sonenta/react-i18next";
import { inContextPlugin, type InContextController } from "@sonenta/in-context/react";
let controller: InContextController | null = null;
<SonentaProvider
token="vrb_live_…"
projectUuid="…"
defaultLocale="fr"
plugins={[
inContextPlugin({
device: "Marketing site · Chrome",
onReady: (c) => (controller = c),
onStatus: (s) => console.log("in-context:", s),
}),
]}
>
<App />
</SonentaProvider>;
// When the user pastes the short code shown in the editor:
await controller?.pair(codeFromInput);
// Call this on route / screen change so the editor tracks what's visible:
router.afterEach(() => controller?.reportKeys());Usage (React Native / Expo)
Identical, importing from @sonenta/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 "@sonenta/in-context/native";
inContextPlugin({
device: "iPhone 15 · Expo",
onReady: (c) => setController(c),
});
// from your QR scanner's onScan handler:
await controller.pair(decodedPairingToken);How it works
- Pair —
controller.pair(pairingToken)callsPOST /v1/in-context/pairwith the single-use token and gets back a scoped, subscribe-only Centrifugo sub-token, the session channel, and the realtimertUrl(server-provided — never hardcoded). - Subscribe — opens that channel via the
@sonenta/realtimeLiveClienttransport and listens forpaired/edit/session_ended. - Apply edits — each
editbecomes an in-memory i18next override (addResource) followed byi18n.refresh()(react-i18next ≥1.0.6), which repaints both the SDK'suseTranslationhooks 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__) arePOSTed to/v1/in-context/sessions/{id}/keysso the editor can highlight them. Each item is the canonical shape{ namespace, key, values? }, wherevaluescarries the value the device rendered on screen (the active locale's raw template). The SDK omitssourceValue. The backend's/keysendpoint is pass-through (it relays your items verbatim); the editor dashboard resolves the authoritativesourceValueand 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_endedor when the sub-token expires (the pairing token is single-use and can't be re-authed).
Controller API
| Member | Description |
| --- | --- |
| 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. |
