SDK · Valutazione da parte dell'utente finale
Anteprima@sonenta/feedback
Lasciate che i vostri utenti finali valutino (5★) e propongano traduzioni dall'interno della vostra app in produzione. Un pacchetto, cinque punti d'ingresso — /react, /native, /vue, /svelte, /core — stessa rete, stessa sessione generata lato server, stesso back office di moderazione. React e React Native si agganciano al provider @sonenta/*-i18n che già usate (nessun secondo contesto, nessun re-render dell'host); Vue e Svelte sono adattatori autonomi idiomatici. Disponibile come add-on a pagamento da Pro in su.
Il pacchetto @sonenta/feedback viene fornito con l'add-on «Valutazione delle traduzioni da parte dell'utente finale» al lancio V1 di Sonenta. Non è ancora su npm — il contratto di rete è congelato (v3); i binding dei framework si stanno stabilizzando e potrebbero ancora cambiare prima del lancio.
1. Installare (al lancio)
Un solo pacchetto. Importate il punto d'ingresso del vostro framework: @sonenta/feedback/react, /native (RN/Expo), /vue, /svelte o /core per il resto. vue / svelte sono peer dependency opzionali — solo il punto d'ingresso corrispondente ne ha bisogno. Pubblicato con l'add-on al lancio V1.
terminal 1// ships with the End-user evaluation add-on at the V1 launch2npm i @sonenta/react-i18next @sonenta/feedback3// feedback peers with @sonenta/react-i18next 2.x4// optional peer deps only for the matching entry: vue · svelte 2. React (web) — plugin i18n
Aggiungete feedbackPlugin() allo slot plugins del vostro provider @sonenta/react-i18next (>= 0.7.0) esistente — nessun nuovo provider, nessun secondo contesto. Il provider chiama il setup() del plugin una sola volta e riutilizza i propri apiBase / projectId / defaultLocale. Il pannello viene montato come foglia sorella isolata con uno store privato di apertura/chiusura: aprirlo non causa mai un re-render del vostro albero host. Attivatelo dal vostro CTA tramite il controller fornito da controllerRef (oppure il callback onReady).
main.tsx 1// src/main.tsx — plugin of the i18n provider you already run2import { SonentaProvider } from "@sonenta/react-i18next";3import { feedbackPlugin } from "@sonenta/feedback/react";4import { useRef } from "react"; 6const feedback = useRef(null); 8<SonentaProvider9 projectUuid="proj_xxx"10 token={import.meta.env.VITE_SONENTA_TOKEN}11 plugins={[ feedbackPlugin({ controllerRef: feedback }) ]}12>13 <App />14</SonentaProvider> 16// own CTA — does NOT re-render the host tree17<button onClick={() => feedback.current?.open()}>Rate translations</button> Tutte le opzioni di feedbackPlugin() / createFeedback()
| Opzione | Tipo | Predefinito |
|---|---|---|
| controllerRef | Ref<Controller> | — |
| onReady | (c) => void | — |
| keys | string[] | auto-discovered |
| flushDebounceMs | number | 1500 |
| maxBatch | number | 20 |
| defaultButton | boolean | false |
3. React Native / Expo
Stesso schema dal punto d'ingresso /native: aggiungete feedbackPlugin() allo slot plugins dello stesso provider @sonenta/react-i18next nella vostra app Expo e attivatelo tramite il controller. Nessun modulo nativo aggiuntivo; la memorizzazione del token usa il secure store della piattaforma.
App.tsx 1// App.tsx (Expo / React Native) — same plugins slot2import { SonentaProvider } from "@sonenta/react-i18next";3import { feedbackPlugin } from "@sonenta/feedback/native"; 5<SonentaProvider6 projectUuid="proj_xxx"7 token={process.env.EXPO_PUBLIC_SONENTA_TOKEN}8 plugins={[ feedbackPlugin({ onReady: (c) => (ctrl = c) }) ]}9>{/* … */}</SonentaProvider> 11// wire ctrl.open() to your own button / FAB 4. Vue
@sonenta/feedback/vue è un adattatore autonomo — config esplicita, nessun provider i18n da cui ereditare. createFeedback(config) restituisce { client, isOpen, controller, FeedbackPanel }. Montate <FeedbackPanel /> una volta vicino alla radice (Teleport su body) e chiamate controller.open() dal vostro CTA. Stesso stato di apertura isolato — non causa mai un re-render della vostra app.
App.vue 1// main.ts — standalone adapter, explicit config2import { createFeedback } from "@sonenta/feedback/vue"; 4export const { controller, FeedbackPanel } = createFeedback({5 apiBase: "https://api.sonenta.com",6 projectId: "proj_xxx", language: "fr",7}); 9// App.vue — mount once near root (Teleports to body)10<FeedbackPanel />11<button @click="controller.open()">Rate translations</button> 5. Svelte
@sonenta/feedback/svelte è headless e idiomatico: createFeedback(config) restituisce store Svelte — isOpen (Writable), strings (Writable) — più open(), close(), loadStrings(), rate(), suggest(). Renderizzate il vostro pannello a partire dagli store; l'SDK gestisce trasporto, consenso e sessione server.
+page.svelte 1// feedback.ts — headless idiomatic stores2import { createFeedback } from "@sonenta/feedback/svelte"; 4export const fb = createFeedback({5 apiBase: "https://api.sonenta.com",6 projectId: "proj_xxx", language: "fr",7}); 9// component — render your own panel from the stores10{#if $fb.isOpen}{#each $fb.strings as s}…{/each}{/if}11<button on:click={fb.open}>Rate translations</button> 6. Il resto — /core
@sonenta/feedback/core espone il FeedbackClient congelato su cui sono costruiti tutti gli adattatori: acceptTos(), loadStrings(), rate(), suggest(), trasporto con debounce e batching, JWT a rotazione. Usatelo direttamente per qualsiasi framework privo di adattatore first-party.
feedback.ts 1// any framework — the frozen client all adapters wrap2import { FeedbackClient } from "@sonenta/feedback/core"; 4const client = new FeedbackClient({5 apiBase: "https://api.sonenta.com",6 projectId: "proj_xxx", language: "fr",7});8await client.acceptTos(); // server mints the session9await client.loadStrings(); client.rate(/* … */); client.suggest(/* … */); 7. Restrizione alle chiavi mostrate (automatica)
Il pannello si restringe automaticamente alle chiavi effettivamente mostrate nella vista corrente, tramite il registro globale delle chiavi prodotto dall'SDK @sonenta/*-i18n — nessuna configurazione. Passate un array keys esplicito solo come ripiego (es. stringhe non provenienti da @sonenta/*-i18n); non passate mai l'intero catalogo — esporrebbe tutte le stringhe dell'app, non quelle che l'utente sta guardando. Il registro è tracciato al montaggio e conteggiato per riferimento: le stringhe persistenti sempre a schermo (un'intestazione, un eyebrow) restano registrate finché il loro componente è montato — nessun reset per vista. (reset() esiste solo come via di fuga per casi limite di routing non-React; l'SDK non lo chiama mai automaticamente.)
scoping.ts 1// the panel auto-scopes to keys RENDERED on the current2// view, via the global key registry the @sonenta/*-i18n3// SDK produces — no config needed:4feedbackPlugin({ controllerRef: feedback }); // auto-scoped 6// explicit keys = FALLBACK only (e.g. strings not from7// @sonenta/*-i18n). NEVER pass your whole catalogue.8feedbackPlugin({ keys: ["common:checkout.cta"] }); 8. Filtro di namespace (opzionale)
Una schermata che renderizza più namespace può restringere il pannello a quello che interessa al cliente — passate un namespace opzionale (string | string[]) sul trigger/config (feedbackPlugin() per React/Native, createFeedback() per Vue/Svelte, resolveKeys() / filterByNamespace() per /core). Si compone dopo la restrizione alle chiavi mostrate — mostrato = mostrato ∩ namespace. Non impostato, "" o [] = nessun filtro (come prima). Non ripiega mai sull'intero progetto.
namespace.ts 1// §0d — OPTIONAL namespace filter (customer feature).2// Composes AFTER rendered-scoping: shown = rendered ∩ namespace.3feedbackPlugin({ controllerRef: feedback, namespace: "quiz" }); 5// Vue / Svelte — same option on createFeedback:6createFeedback({ apiBase, projectId, language, namespace: ["quiz"] }); 8// /core — resolveKeys / filterByNamespace:9resolveKeys(explicit, "quiz"); // or filterByNamespace(keys, "quiz") 11// unset / "" / [] ⇒ no filter (identical to v5). 9. Sessione generata lato server (tutti i framework)
La chiave di sessione / raggruppamento è generata lato server al momento del consenso. Il client non la invia né la genera mai — nessuna config groupingKey, nessun campo di richiesta. Su acceptTos() il backend la restituisce (legata al JWT con scope ristretto); ogni adattatore la espone in sola lettura tramite client.sessionId. Un endUserId di ritorno conserva il suo valore server stabile; un nuovo utente ottiene un sess_… nuovo.
consent.ts 1// session/grouping key is MINTED SERVER-SIDE at consent2await client.acceptTos(); // POST /v1/feedback/tos3client.sessionId; // read-only, e.g. "sess_018f…" 5// NO groupingKey config, NO request field —6// the client never sends or self-generates the session. 10. Consenso e sicurezza (automatici)
Prima della prima scrittura, l'utente finale accetta i Termini di servizio Sonenta per utente finale, versionati. L'SDK ottiene quindi un JWT a breve durata limitato allo scope feedback:write — crittograficamente separato dalla vostra auth cliente — e lo rinnova in modo trasparente. Gli utenti finali sono anonimi (id opaco, nessun dato personale). Non dovete scrivere nulla lato sicurezza.
Cosa ottenete gratuitamente
- Zero re-render dell'host. React/Native girano come foglia sorella isolata dal provider i18n; Vue/Svelte mantengono lo stato di apertura nel proprio store. Aggiungere o aprire il feedback non causa mai un re-render della vostra app.
- Consenso + sessione server gestiti. Accettazione dei Termini di servizio, sessione generata lato server, ottenimento del JWT, refresh a rotazione e un singolo retry trasparente su 401 — tutto dentro l'SDK. Non solleva mai eccezioni nel vostro rendering.
- Trasporto con debounce e batching. Valutazioni e suggerimenti vengono messi in coda e inviati su un debounce (1,5s per impostazione predefinita), a un batch massimo, o alla chiusura. Best-effort: un batch fallito viene rimesso in coda una volta, poi assorbito.
- Moderazione prima della pubblicazione. Niente di ciò che un utente finale invia va online automaticamente. I suggerimenti arrivano come
pendingnella coda di moderazione della vostra dashboard — voi approvate, rifiutate o applicate tramite il normale percorso di modifica auditato. Le valutazioni si aggregano in una dashboard in tempo reale per chiave / per lingua.