@sonenta/realtime
v2.1.0
Published
Realtime translation updates plugin for @sonenta/react-i18next (Centrifugo).
Maintainers
Readme
@sonenta/realtime
Realtime translation updates for Sonenta. A plugin
of your existing @sonenta/react-i18next provider: for dev versions it
subscribes to the version's Centrifugo channel and, when a new release is
published, bust-refetches the affected bundle and re-renders — no page
reload, no redeploy.
This was previously baked into @sonenta/react-i18next core; it was
extracted into this package in react-i18next 0.9.0 so realtime is opt-in
and the core stays lean.
@sonenta/realtime/react— the provider plugin (web/React).@sonenta/realtime/vue— the same plugin, for@sonenta/vue-i18n.@sonenta/realtime/svelte— the same plugin, for@sonenta/svelte-i18n.@sonenta/realtime/core— the framework-agnostic Centrifugo client (LiveClient,fetchCentrifugoToken) for advanced/other-framework use.
React Native: TESTED — it resolves and runs. It is dev-only, not web-only.
Unlike @sonenta/feedback and @sonenta/in-context, this package ships no
native entry point — it ships no UI at all, so it has no native view layer
to provide. That absence says what this package ships, not what it
runs on: nothing here is DOM-coupled, and LiveClient talks to Centrifugo
over a plain new WebSocket(url), which is a React Native global.
What has actually been tested (demo-app-expo, Expo 52 / RN 0.76.5 / Hermes, 2026-07-13):
@sonenta/realtime/reactresolves under Metro with zero app-side config, via the package root shim (Expo shipsunstable_enablePackageExportsoff, so theexportsmap is bypassed — which is exactly what the 0.1.1 shims were for).- It runs under Hermes — no polyfill, no patch; the plugin's
setup()executes on-device. - The WebSocket was not attempted — and that is correct, not a failure. With a live key the client fetched version metadata (200), evaluated the gate, and logged: "realtime is only available on dev versions; main is published — no subscription, serving static CDN bundles." The project's only version was published, so the SDK declined to subscribe by design. That gate is platform-agnostic: a published web project behaves identically.
So on React Native the SDK resolves, runs, and executes its full startup path, including the conditional it is supposed to evaluate. The socket is unexercised because the product rule closed it, not because the platform blocked it.
Do not call this package "web only." That would assert a platform limitation that has now been positively disproven on a device. It is dev-only — which is a product decision, not a platform one, and true on every framework.
MIT.
Install
npm i @sonenta/realtimereact and @sonenta/react-i18next (>= 0.9.0) are peer deps.
Usage (React)
Add sonentaRealtime(...) to your i18n provider's plugins slot — no
second context, no extra config (it reuses the provider's apiBase /
projectUuid / token):
import { SonentaProvider } from "@sonenta/react-i18next";
import { sonentaRealtime } from "@sonenta/realtime/react";
<SonentaProvider
token="snt_live_…"
projectUuid="<project-uuid>"
defaultLocale="fr"
version="main"
plugins={[
sonentaRealtime({
wsUrl: "wss://rt.sonenta.dev/connection/websocket",
}),
]}
>
<App />
</SonentaProvider>;sonentaRealtime accepts:
wsUrl(required) — the Centrifugo WebSocket URL.tokenEndpoint(optional) — defaults to${apiBase}/v1/auth/centrifugo-token.
Behaviour
Realtime is gated per version (Sonenta product model): on mount the
plugin reads the configured version's state from the backend
(GET /v1/projects/{id}/versions/{version} → is_dev + realtime_channel):
- dev version (
is_dev: true) — mints a version-scoped translations token (POST /v1/auth/centrifugo-token { kind: "translations", version_uuid }) and subscribes to the version'srealtime_channel. - published version (
realtime_channel === null) — no subscription; aconsole.warnexplains realtime is dev-only, and bundles are served statically from the CDN.
This is independent of the provider's env flag (which only controls where
bundles are fetched). On each translations_published push for a
(language_code, namespace_slug) the app already loaded, the plugin calls
i18n.reload({ locale, namespace }) — a cache-bypassing refetch + re-render.
Connection + token minting are best-effort: a failure logs a console.warn
and the SDK keeps serving the last bundle. Teardown cancels any in-flight
setup and disposes the client.
