SDK · Valutazione da parte dell'utente finale
Anteprima@sonenta/feedback
Lascia che siano i tuoi utenti finali a valutare (5★) e proporre traduzioni dall’interno della tua app in produzione. React e React Native si innestano nel provider @sonenta/*-i18n che già usi (nessun secondo contesto, nessun re-render dell’host); per tutto il resto si passa da /core, il client congelato che ogni binding avvolge. Stessa rete, stessa sessione generata dal server, stesso back office di moderazione. Disponibile come add-on a pagamento da Pro.
Il pacchetto @sonenta/feedback viene fornito con l’add-on di valutazione delle traduzioni da parte degli utenti finali al lancio V1 di Sonenta. Il contratto di rete è congelato (v3); i binding per i framework si stanno ancora stabilizzando e potrebbero 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. 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.dev",6 projectId: "proj_xxx", language: "fr",7 // REQUIRED. Without it, the first authenticated call8 // accepts the end-user ToS on your user's behalf.9 autoAcceptTos: false,10}); 12// show YOUR ToS step, and only once the user agrees:13await client.acceptTos(); // server mints the session14await client.loadStrings(); client.rate(/* … */); client.suggest(/* … */); 16// with autoAcceptTos:false, an unconsented authed call throws17// FeedbackError("not consented") instead of fabricating a record. 5. 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"] }); 6. 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). 7. 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. 8. Consenso e sicurezza
Una versione specifica dei termini per l’utente finale di Sonenta precede la prima scrittura. Nei pannelli React e React Native il passaggio sui termini viene mostrato prima di qualsiasi chiamata autenticata, ed è il tocco dell’utente finale a invocare acceptTos(): il caricamento delle stringhe è vincolato al consenso per costruzione. Se invece passi da /core, costruisci il client con autoAcceptTos: false e chiama acceptTos() solo quando il tuo utente ha acconsentito, altrimenti la prima chiamata autenticata accetta al posto suo. Il client detiene poi un JWT di breve durata limitato al solo scope feedback:write, crittograficamente separato dall’auth dei tuoi clienti, e lo ruota in modo trasparente. Gli utenti finali sono anonimi (id opaco, nessun dato personale). Su @sonenta/feedback 1.2.x e precedenti questo è il comportamento PREDEFINITO: il client accetta alla prima chiamata autenticata a meno che tu non passi autoAcceptTos: false. La versione 1.3.0 inverte il valore predefinito e fallisce in sicurezza, sollevando FeedbackError("not consented") invece di accettare al posto del tuo utente.
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.