
Company News
Free Business Plan Upgrades for Open Source Maintainers
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.
@sonenta/feedback
Advanced tools
Sonenta End-User Translation Evaluation widget: let your end users rate and suggest translations. React (web) + React Native / Expo.
Embed a Sonenta End-User Translation Evaluation widget in your app. Your end users accept a short ToS, then rate (5-star) and suggest alternatives for the translations on screen. You moderate everything from the Sonenta dashboard — nothing auto-publishes.
@sonenta/feedback/react — React (web)@sonenta/feedback/native — React Native / Expo@sonenta/feedback/core — framework-agnostic client (advanced)MIT. Add-on available from the Pro plan upward.
npm i @sonenta/feedback
react (and react-native for the native entry) are peer deps.
@sonenta/feedback is a plugin of your existing @sonenta/*-i18n
provider — not a second context/provider. You add it to the i18n
provider's plugins slot; it reuses that provider's apiBase /
projectUuid / locale (no re-config) and never re-renders your app when
the panel opens. The sessionId is minted server-side — you don't supply
a cohort/session id.
import { SonentaProvider } from "@sonenta/react-i18next";
import { feedbackPlugin } from "@sonenta/feedback/react";
const feedbackCtl = { current: null };
<SonentaProvider
token="snt_live_…"
projectUuid="<project-uuid>"
defaultLocale="fr"
plugins={[
feedbackPlugin({
tosVersion: "2026-05-18",
keys: [{ namespace: "common", key: "home.title" }], // or omit → i18n registry
controllerRef: feedbackCtl, // imperative handle
}),
]}
>
<App />
</SonentaProvider>;
// trigger from your own CTA — no hook, no extra provider:
<button onClick={() => feedbackCtl.current?.open()}>Rate translations</button>;
feedbackPlugin also accepts onReady(controller), and optional
apiBase / projectId / language overrides (defaults come from the
i18n provider config).
import { feedbackPlugin } from "@sonenta/feedback/native";
// same plugin API; renders a bottom-sheet <Modal> instead of a side panel.
// add it to your @sonenta/react-i18next provider's `plugins` slot.
If @sonenta/*-i18n is on the page it exposes a tiny key registry; the widget
reads the on-screen keys from it automatically (scope: "current-view", the
default).
When our capture is wrong, you can always correct it — per open:
// The host knows what is on this screen. The host wins.
await controller.open({
keys: [{ namespace: "common", key: "home.title" }],
});
Precedence: open({ keys }) → the static keys plugin option → whatever the
current-view capture found.
1.3.1 — this README used to promise that fallback and the API did not allow it.
controller.open()took no argument, and the onlykeyswas the STATIC plugin option: the same list on every screen, which is the opposite of a per-view fallback. So on the night our capture broke, the workaround this page told you to use was unreachable.open({ keys })makes it true. If you read the old sentence and could not find the door, it was not there — that is on us.
tos: "skip") — call acceptTos() on EVERY app startWith tos: "modal" (the default) the SDK shows its own consent step and you do nothing.
With tos: "skip" you own consent, and you must call controller.startSession() once per app
start — not once per user, ever.
// on every launch, after your own consent screen has been satisfied at least once:
if (userHasConsentedBefore) await controller.startSession();
It used to be called
acceptTos(), and the name was the bug. "Accept the ToS" reads like a one-time act, so an integrator called it once, persisted their own flag, and their panel never unlocked again.acceptTos()still works as a deprecated alias — but nobody callsstartSession()once in the life of an app, which is the point.
Why: acceptTos() is the session bootstrap, not a consent record. It mints the scoped token
the client authenticates with, and that token lives in memory — this package persists nothing
client-side. A cold start begins with no session, so if you persist your own "already accepted" flag
and skip the call, the panel stays locked forever while your user's consent sits correctly
recorded on the server.
It is idempotent within a process, and the server records consent once, so calling it on every launch is correct and cheap.
This is a trap our own comments set: they claimed the SDK and an external ToS page "share the same persisted token bundle". There is no persisted bundle, and a customer built on that sentence. Making consent survive a restart without this call (a
restoreSession()/ storage adapter) is a real feature, and it is on the backlog — not something you have today.
Ratings/suggestions are debounced + batched and sent best-effort — the widget never blocks or breaks your app. See CONTRACT.md for the exact wire shape.
FAQs
Sonenta End-User Translation Evaluation widget: let your end users rate and suggest translations. React (web) + React Native / Expo.
We found that @sonenta/feedback demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.