跳到内容
Sonenta

SDK · 终端用户评估

预览版

@sonenta/feedback

让你自己的终端用户在已上线的 app 内为翻译评分(5★)并提出建议。一个包,五个入口——/react/native/vue/svelte/core——相同的 wire、相同的服务端签发 session、相同的审核后台。React 与 React Native 接入你已经在用的 @sonenta/*-i18n provider(无需第二个 context,不会触发宿主重新渲染);Vue 与 Svelte 是符合各自惯用法的独立 adapter。作为 Pro 及以上的付费 add-on 提供。

@sonenta/feedback 包随「终端用户翻译评估」add-on 在 Sonenta V1 发布时一同推出。它尚未发布到 npm——wire 契约已冻结(v3);框架 binding 仍在稳定中,在发布前可能还会变化。

1. 安装(发布时)

只有一个包。导入你所用框架的入口:@sonenta/feedback/react/native(RN/Expo)、/vue/svelte,或为其余情况导入 /corevue / svelte 是可选的 peer deps——只有对应的入口才需要它们。随 add-on 在 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)——i18n plugin

feedbackPlugin() 添加到你现有 @sonenta/react-i18next(>= 0.7.0)provider 的 plugins 槽位——无需新 provider,无需第二个 context。provider 会调用 plugin 的 setup() 一次,并复用它自身的 apiBase / projectId / defaultLocale。面板会作为一个隔离的兄弟叶子节点挂载,并拥有私有的开/关 store,因此打开它永远不会重新渲染你的宿主树。通过 controllerRef 提供的 controller(或 onReady 回调)从你自己的 CTA 触发。

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>
全部 feedbackPlugin() / createFeedback() 选项
选项 类型 默认值
controllerRefRef<Controller>
onReady(c) => void
keysstring[]auto-discovered
flushDebounceMsnumber1500
maxBatchnumber20
defaultButtonbooleanfalse

3. React Native / Expo

/native 入口采用相同模式:在你的 Expo app 中,将 feedbackPlugin() 添加到同一个 @sonenta/react-i18next provider 的 plugins 槽位,并通过 controller 触发。无需额外的原生模块;token 存储使用平台的 secure store。

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 是一个独立 adapter——显式配置,没有可继承的 i18n provider。createFeedback(config) 返回 { client, isOpen, controller, FeedbackPanel }。在靠近根部处挂载一次 <FeedbackPanel />(它会 Teleport 到 body),并从你自己的 CTA 调用 controller.open()。同样的隔离开启状态——它永远不会重新渲染你的 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 且符合惯用法的:createFeedback(config) 返回 Svelte store——isOpenWritable)、stringsWritable)——以及 open()close()loadStrings()rate()suggest()。你从这些 store 渲染你自己的面板;SDK 负责传输、consent 和服务端 session。

+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. 其余情况——/core

@sonenta/feedback/core 暴露了所有 adapter 都构建于其上的、已冻结的 FeedbackClientacceptTos()loadStrings()rate()suggest()、debounce 加批处理的传输、轮换的 JWT。对于任何没有 first-party adapter 的框架,可直接使用它。

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. 已渲染 key 的范围限定(自动)

面板会通过 @sonenta/*-i18n SDK 生成的全局 key 注册表,自动将范围限定到当前视图上实际渲染的 key——无需配置。仅在回退场景下才传入显式的 keys 数组(例如并非来自 @sonenta/*-i18n 的字符串);切勿传入你的整个 catalogue——那会暴露 app 中的每一个字符串,而非用户正在查看的那些。该注册表是按挂载追踪并按引用计数的:始终显示在屏幕上的持久字符串(页眉、eyebrow)只要其组件保持挂载就会一直被注册——不存在按视图的重置。(reset() 仅作为非 React 路由边缘场景的应急出口存在;SDK 永远不会自动调用它。)

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. namespace 过滤器(可选)

渲染多个 namespace 的屏幕可以将面板范围限定到客户关心的那一个——在 trigger/config 上传入一个可选的 namespacestring | string[])(React/Native 用 feedbackPlugin(),Vue/Svelte 用 createFeedback()/coreresolveKeys() / filterByNamespace())。它在已渲染范围限定之后组合——显示项 = 已渲染 ∩ namespace。未设置、""[] 表示无过滤(与此前一致)。它永远不会回退到整个项目。

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(所有框架)

session / 分组 key 是在 consent 时由服务端签发的。客户端永远不会发送或自行生成它——既没有 groupingKey 配置,也没有请求字段。在 acceptTos() 时后端会返回它(绑定到受限的 JWT 中);每个 adapter 都通过 client.sessionId只读方式暴露它。回访的 endUserId 会保持其稳定的服务端值;新的终端用户会获得一个全新的 sess_…

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. consent 与安全(自动)

在首次写入之前,终端用户会接受带版本号的 Sonenta 终端用户 ToS。随后 SDK 会获取一个短期的、仅限 feedback:write scope 的 JWT——与你的客户 auth 在密码学上相互分离——并透明地轮换它。终端用户是匿名的(不透明 id,无 PII)。安全方面你无需编写任何代码。

你免费获得的能力

下一步