Ir al contenido
Sonenta

SDK · Evaluación por el usuario final

Vista previa

@sonenta/feedback

Deja que tus propios usuarios finales valoren (5★) y propongan traducciones desde dentro de tu app en producción. React y React Native se conectan al provider @sonenta/*-i18n que ya usas (sin segundo contexto, sin re-render del host); para todo lo demás, pasa por /core, el cliente congelado que envuelven todos los bindings. La misma red, la misma sesión generada en el servidor, el mismo back office de moderación. Disponible como add-on de pago desde Pro.

El paquete @sonenta/feedback se entrega con el add-on de evaluación de traducciones por el usuario final en el lanzamiento V1 de Sonenta. El contrato de red está congelado (v3); los bindings de framework aún se están estabilizando y pueden cambiar antes del lanzamiento.

1. Instalar (en el lanzamiento)

Un solo paquete. Importa el punto de entrada de tu framework: @sonenta/feedback/react, /native (RN/Expo), /vue, /svelte, o /core para el resto. vue / svelte son peer deps opcionales, solo el punto de entrada correspondiente las necesita. Publicado con el add-on en el lanzamiento 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

Añade feedbackPlugin() al slot plugins de tu provider @sonenta/react-i18next (>= 0.7.0) existente, sin nuevo provider, sin un segundo contexto. El provider llama al setup() del plugin una vez y reutiliza sus propios apiBase / projectId / defaultLocale. El panel se monta como una hoja hermana aislada con un store privado de apertura/cierre: abrirlo nunca vuelve a renderizar tu árbol host. Dispáralo desde tu propio CTA mediante el controlador proporcionado por controllerRef (o el 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>
Todas las opciones de feedbackPlugin() / createFeedback()
Opción Tipo Por defecto
controllerRefRef<Controller>,
onReady(c) => void,
keysstring[]auto-discovered
flushDebounceMsnumber1500
maxBatchnumber20
defaultButtonbooleanfalse

3. React Native / Expo

Mismo esquema desde el punto de entrada /native: añade feedbackPlugin() al slot plugins del mismo provider @sonenta/react-i18next en tu app Expo y dispáralo mediante el controlador. Sin módulos nativos adicionales; el almacenamiento del token usa el secure store de la plataforma.

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. El resto, /core

@sonenta/feedback/core expone el FeedbackClient congelado sobre el que se construyen todos los adaptadores: acceptTos(), loadStrings(), rate(), suggest(), transporte con debounce y batch, JWT rotativo. Úsalo directamente para cualquier framework sin adaptador 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. Acotación a las claves mostradas (automática)

El panel se acota automáticamente a las claves realmente mostradas en la vista actual, mediante el registro global de claves que produce el SDK @sonenta/*-i18n, sin configuración. Pasa un array keys explícito solo como respaldo (p. ej. cadenas que no provienen de @sonenta/*-i18n); nunca pases todo tu catálogo, eso expondría todas las cadenas de la app, no las que el usuario está mirando. El registro se rastrea al montar y se cuenta por referencia: las cadenas persistentes siempre en pantalla (un encabezado, un eyebrow) permanecen registradas mientras su componente esté montado, no hay reset por vista. (reset() existe únicamente como vía de escape para casos límite de enrutamiento no-React; el SDK nunca lo llama automáticamente.)

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 de namespace (opcional)

Una pantalla que renderiza varios namespaces puede acotar el panel solo al que le interesa al cliente, pasa un namespace opcional (string | string[]) en el trigger/config (feedbackPlugin() para React/Native, createFeedback() para Vue/Svelte, resolveKeys() / filterByNamespace() para /core). Se compone después de la acotación a las claves mostradas, mostrado = mostrado ∩ namespace. Sin definir, "" o [] = ningún filtro (como antes). Nunca recae en todo el proyecto.

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. Sesión generada en el servidor (todos los frameworks)

La clave de sesión / agrupación se genera en el servidor al dar el consentimiento. El cliente nunca la envía ni la genera por sí mismo, no hay config groupingKey ni campo de petición. En acceptTos() el backend la devuelve (vinculada al JWT restringido); cada adaptador la expone en solo lectura mediante client.sessionId. Un endUserId recurrente conserva su valor de servidor estable; un nuevo usuario final obtiene un sess_… nuevo.

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. Consentimiento y seguridad

Una versión concreta de los términos para el usuario final de Sonenta condiciona la primera escritura. En los paneles de React y React Native, el paso de términos se muestra antes de cualquier llamada autenticada, y es el toque del propio usuario final el que invoca acceptTos(): la carga de las cadenas está condicionada al consentimiento por construcción. Si usas /core, construye el cliente con autoAcceptTos: false y llama a acceptTos() solo cuando tu usuario haya aceptado; de lo contrario, la primera llamada autenticada acepta en su nombre. El cliente mantiene después un JWT de corta duración limitado al scope feedback:write, criptográficamente separado de la autenticación de tus clientes, y lo rota de forma transparente. Los usuarios finales son anónimos (id opaco, sin PII). En @sonenta/feedback 1.2.x y anteriores este es el comportamiento POR DEFECTO: el cliente acepta en la primera llamada autenticada salvo que pases autoAcceptTos: false. La versión 1.3.0 invierte ese valor por defecto y falla de forma segura, lanzando FeedbackError("not consented") en lugar de aceptar por tu usuario.

Lo que obtienes gratis

Siguiente