SDK · Évaluation par l'utilisateur final
Préversion@sonenta/feedback
Laissez vos propres utilisateurs finaux noter (5★) et proposer des traductions depuis votre app en production. Un paquet, cinq points d'entrée — /react, /native, /vue, /svelte, /core — même réseau, même session générée côté serveur, même back-office de modération. React et React Native se branchent sur le provider @sonenta/*-i18n que vous utilisez déjà (pas de second contexte, aucun re-render hôte) ; Vue et Svelte sont des adaptateurs autonomes idiomatiques. Disponible en add-on payant à partir de Pro.
Le paquet @sonenta/feedback est livré avec l'add-on « Évaluation des traductions par l'utilisateur final » au lancement V1 de Sonenta. Il n'est pas encore sur npm — le contrat réseau est figé (v3) ; les bindings frameworks se stabilisent et peuvent encore évoluer avant le lancement.
1. Installer (au lancement)
Un seul paquet. Importez le point d'entrée de votre framework : @sonenta/feedback/react, /native (RN/Expo), /vue, /svelte, ou /core pour le reste. vue / svelte sont des peer deps optionnelles — seul le point d'entrée concerné en a besoin. Publié avec l'add-on au lancement 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
Ajoutez feedbackPlugin() au slot plugins de votre provider @sonenta/react-i18next (>= 0.7.0) existant — aucun nouveau provider, aucun second contexte. Le provider appelle le setup() du plugin une fois et réutilise ses apiBase / projectId / defaultLocale. Le panneau se monte en feuille sœur isolée avec un store privé d'ouverture/fermeture : l'ouvrir ne re-render jamais votre arbre hôte. Déclenchez depuis votre propre CTA via le contrôleur fourni par controllerRef (ou le 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> Toutes les options de feedbackPlugin() / createFeedback()
| Option | Type | Défaut |
|---|---|---|
| controllerRef | Ref<Controller> | — |
| onReady | (c) => void | — |
| keys | string[] | auto-discovered |
| flushDebounceMs | number | 1500 |
| maxBatch | number | 20 |
| defaultButton | boolean | false |
3. React Native / Expo
Même schéma depuis le point d'entrée /native : ajoutez feedbackPlugin() au slot plugins du même provider @sonenta/react-i18next dans votre app Expo et déclenchez via le contrôleur. Aucun module natif supplémentaire ; le stockage du token utilise le secure store de la plateforme.
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 est un adaptateur autonome — config explicite, aucun provider i18n dont hériter. createFeedback(config) retourne { client, isOpen, controller, FeedbackPanel }. Montez <FeedbackPanel /> une fois près de la racine (Teleport vers body) et appelez controller.open() depuis votre CTA. Même état d'ouverture isolé — aucun re-render de votre 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 est headless et idiomatique : createFeedback(config) retourne des stores Svelte — isOpen (Writable), strings (Writable) — plus open(), close(), loadStrings(), rate(), suggest(). Vous rendez votre propre panneau depuis les stores ; le SDK gère le transport, le consentement et la session serveur.
+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. Le reste — /core
@sonenta/feedback/core expose le FeedbackClient figé sur lequel tous les adaptateurs sont bâtis : acceptTos(), loadStrings(), rate(), suggest(), transport debouncé/batché, JWT rotatif. Utilisez-le directement pour tout framework sans adaptateur 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. Cadrage sur les clés affichées (automatique)
Le panneau se cadre automatiquement sur les clés réellement affichées sur la vue courante, via le registre global de clés produit par le SDK @sonenta/*-i18n — aucune configuration. Ne passez un tableau keys explicite qu'en repli (ex. chaînes non issues de @sonenta/*-i18n) ; ne passez jamais tout votre catalogue — cela exposerait toutes les chaînes de l'app, pas celles que l'utilisateur regarde. Le registre est suivi au montage et compté par référence : les chaînes persistantes toujours à l'écran (en-tête, eyebrow) restent enregistrées tant que leur composant est monté — aucun reset par vue. (reset() n'existe que comme échappatoire pour des cas de routage non-React ; le SDK ne l'appelle jamais automatiquement.)
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. Filtre de namespace (optionnel)
Un écran qui rend plusieurs namespaces peut cadrer le panneau sur celui qui intéresse le client — passez un namespace optionnel (string | string[]) sur le trigger/config (feedbackPlugin() pour React/Native, createFeedback() pour Vue/Svelte, resolveKeys() / filterByNamespace() pour /core). Il se compose après le cadrage sur les clés affichées — affiché = affiché ∩ namespace. Non défini, "" ou [] = aucun filtre (comme avant). Ne retombe jamais sur tout le projet.
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. Session générée côté serveur (tous frameworks)
La clé de session / regroupement est générée côté serveur au consentement. Le client ne l'envoie ni ne la génère jamais — aucune config groupingKey, aucun champ de requête. À acceptTos() le backend la renvoie (liée au JWT restreint) ; chaque adaptateur l'expose en lecture seule via client.sessionId. Un endUserId de retour conserve sa valeur serveur stable ; un nouvel utilisateur obtient un sess_… neuf.
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. Consentement et sécurité (automatique)
Avant la première écriture, l'utilisateur final accepte les CGU utilisateur final Sonenta versionnées. Le SDK obtient ensuite un JWT court limité au scope feedback:write — cryptographiquement séparé de votre auth client — et le renouvelle de façon transparente. Les utilisateurs finaux sont anonymes (id opaque, aucune PII). Vous n'avez rien à coder côté sécurité.
Ce que vous obtenez gratuitement
- Zéro re-render hôte. React/Native tournent en feuille sœur isolée du provider i18n ; Vue/Svelte gardent l'état d'ouverture dans leur propre store. Ajouter ou ouvrir le feedback ne re-render jamais votre app.
- Consentement + session serveur gérés. Acceptation des CGU, session générée côté serveur, obtention du JWT, refresh rotatif et un retry transparent unique sur 401 — tout dans le SDK. Il ne lève jamais d'exception dans votre rendu.
- Transport debouncé et batché. Notes et suggestions sont mises en file et envoyées sur un debounce (1,5s par défaut), à un batch max, ou à la fermeture. Best-effort : un batch en échec est re-mis en file une fois, puis absorbé.
- Modération avant publication. Rien de soumis par un utilisateur final ne part en ligne automatiquement. Les suggestions arrivent en
pendingdans la file de modération de votre dashboard — vous approuvez, rejetez ou appliquez via le chemin d'édition audité normal. Les notes s'agrègent dans un tableau de bord temps réel par clé / par langue.