Ir para o conteúdo
Sonenta

SDK · Avaliação pelo utilizador final

Pré-visualização

@sonenta/feedback

Deixe os seus próprios utilizadores finais avaliarem (5★) e sugerirem traduções a partir da sua app em produção. React e React Native ligam-se ao provider @sonenta/*-i18n que já utiliza (sem segundo contexto, sem re-render do host); tudo o resto passa por /core, o cliente congelado que todos os bindings envolvem. A mesma rede, a mesma sessão gerada no servidor, o mesmo back office de moderação. Disponível como add-on pago a partir do Pro.

O pacote @sonenta/feedback é fornecido com o add-on de avaliação de traduções pelo utilizador final no lançamento V1 da Sonenta. O contrato de rede está congelado (v3); os bindings de framework ainda estão a estabilizar e podem mudar antes do lançamento.

1. Instalar (no lançamento)

Um único pacote. Importe o ponto de entrada do seu framework: @sonenta/feedback/react, /native (RN/Expo), /vue, /svelte, ou /core para o resto. vue / svelte são peer deps opcionais, apenas o ponto de entrada correspondente precisa delas. Publicado com o add-on no lançamento 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

Adicione feedbackPlugin() ao slot plugins do seu provider @sonenta/react-i18next (>= 0.7.0) existente, sem novo provider, sem segundo contexto. O provider chama o setup() do plugin uma vez e reutiliza os seus próprios apiBase / projectId / defaultLocale. O painel é montado como uma folha irmã isolada com um store privado de abrir/fechar, pelo que abri-lo nunca faz re-render da sua árvore host. Despolete a partir do seu próprio CTA através do controlador fornecido por controllerRef (ou pelo 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 as opções de feedbackPlugin() / createFeedback()
Opção Tipo Por omissão
controllerRefRef<Controller>,
onReady(c) => void,
keysstring[]auto-discovered
flushDebounceMsnumber1500
maxBatchnumber20
defaultButtonbooleanfalse

3. React Native / Expo

Mesmo padrão a partir do ponto de entrada /native: adicione feedbackPlugin() ao slot plugins do mesmo provider @sonenta/react-i18next na sua app Expo e despolete através do controlador. Sem módulos nativos adicionais; o armazenamento do token usa o secure store da 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. O resto, /core

@sonenta/feedback/core expõe o FeedbackClient congelado sobre o qual todos os adaptadores são construídos: acceptTos(), loadStrings(), rate(), suggest(), transporte com debounce e em lote, JWT rotativo. Use-o diretamente para qualquer framework sem 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. Delimitação às chaves apresentadas (automática)

O painel delimita-se automaticamente às chaves efetivamente apresentadas na vista atual, através do registo global de chaves produzido pelo SDK @sonenta/*-i18n, sem configuração. Passe um array keys explícito apenas como recurso de reserva (p. ex. strings que não provêm de @sonenta/*-i18n); nunca passe todo o seu catálogo, isso exporia todas as strings da app, e não as que o utilizador está a ver. O registo é rastreado no montar e contado por referência: strings persistentes sempre no ecrã (um cabeçalho, um eyebrow) permanecem registadas enquanto o seu componente estiver montado, não há reset por vista. (reset() existe apenas como válvula de escape para casos limite de routing não-React; o SDK nunca o chama 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 de namespace (opcional)

Um ecrã que apresenta vários namespaces pode delimitar o painel apenas àquele que interessa ao cliente, passe um namespace opcional (string | string[]) no trigger/config (feedbackPlugin() para React/Native, createFeedback() para Vue/Svelte, resolveKeys() / filterByNamespace() para /core). Compõe-se depois da delimitação às chaves apresentadas, apresentado = apresentado ∩ namespace. Por definir, "" ou [] = sem filtro (como antes). Nunca recorre a todo o projeto.

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. Sessão gerada no servidor (todos os frameworks)

A chave de sessão / agrupamento é gerada no servidor no momento do consentimento. O cliente nunca a envia nem a gera, não há config groupingKey nem campo de pedido. No acceptTos() o backend devolve-a (ligada ao JWT restrito); cada adaptador expõe-na em apenas leitura através de client.sessionId. Um endUserId recorrente mantém o seu valor estável de servidor; um novo utilizador final obtém um sess_… novo.

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. Consentimento e segurança

Uma versão datada dos termos de utilizador final da Sonenta condiciona a primeira escrita. Nos painéis React e React Native, o passo dos termos é apresentado antes de qualquer chamada autenticada, e é o toque do próprio utilizador final que invoca acceptTos(): o carregamento das strings está condicionado ao consentimento por construção. Se optar por /core, construa o cliente com autoAcceptTos: false e chame acceptTos() apenas depois de o seu utilizador concordar, caso contrário a primeira chamada autenticada aceita em nome dele. O cliente passa então a deter um JWT de curta duração limitado ao scope feedback:write, criptograficamente separado da autenticação dos seus clientes, e renova-o de forma transparente. Os utilizadores finais são anónimos (id opaco, sem PII). No @sonenta/feedback 1.2.x e anteriores este é o comportamento POR OMISSÃO: o cliente aceita na primeira chamada autenticada, a menos que passe autoAcceptTos: false. A versão 1.3.0 inverte esse valor por omissão e falha de forma segura, lançando FeedbackError("not consented") em vez de aceitar em nome do seu utilizador.

O que obtém gratuitamente

A seguir