SDK · Оценка конечным пользователем
Превью@sonenta/feedback
Позвольте вашим конечным пользователям оценивать (5★) и предлагать переводы прямо из вашего приложения. React и React Native подключаются к провайдеру @sonenta/*-i18n, который у вас уже работает (без второго контекста, без ре-рендера хоста); всё остальное идёт через /core, замороженный клиент, который оборачивает каждый биндинг. Тот же протокол, та же сессия, созданная на сервере, тот же бэк-офис модерации. Доступно как платное дополнение начиная с Pro.
Пакет @sonenta/feedback поставляется с дополнением для оценки переводов конечными пользователями на запуске Sonenta V1. Сетевой контракт заморожен (v3); биндинги для фреймворков ещё стабилизируются и могут измениться до запуска.
1. Установка (при запуске)
Один пакет. Импортируйте точку входа вашего фреймворка: @sonenta/feedback/react, /native (RN/Expo), /vue, /svelte или /core для всего остального. vue / svelte, это опциональные peer-зависимости; они нужны только соответствующей точке входа. Публикуется вместе с дополнением при запуске 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
Добавьте feedbackPlugin() в слот plugins вашего существующего провайдера @sonenta/react-i18next (>= 0.7.0), никакого нового провайдера, никакого второго контекста. Провайдер вызывает setup() плагина один раз и переиспользует свои apiBase / projectId / defaultLocale. Панель монтируется как изолированный соседний лист с приватным хранилищем открытия/закрытия: её открытие никогда не вызывает ре-рендер вашего хост-дерева. Запускайте из своего собственного CTA через контроллер, предоставленный через controllerRef (или коллбэк 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> Все опции feedbackPlugin() / createFeedback()
| Опция | Тип | По умолчанию |
|---|---|---|
| controllerRef | Ref<Controller> | , |
| onReady | (c) => void | , |
| keys | string[] | auto-discovered |
| flushDebounceMs | number | 1500 |
| maxBatch | number | 20 |
| defaultButton | boolean | false |
3. React Native / Expo
Та же схема из точки входа /native: добавьте feedbackPlugin() в слот plugins того же провайдера @sonenta/react-i18next в вашем приложении Expo и запускайте через контроллер. Никаких дополнительных нативных модулей; хранение токена использует защищённое хранилище платформы.
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. Всё остальное, /core
@sonenta/feedback/core предоставляет зафиксированный FeedbackClient, на котором построены все адаптеры: acceptTos(), loadStrings(), rate(), suggest(), транспорт с дебаунсом и батчингом, ротируемый JWT. Используйте его напрямую для любого фреймворка без 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. Привязка к отображаемым ключам (автоматически)
Панель автоматически ограничивается ключами, фактически отображёнными в текущем представлении, через глобальный реестр ключей, который формирует SDK @sonenta/*-i18n, без настройки. Передавайте явный массив keys только как запасной вариант (например, строки не из @sonenta/*-i18n); никогда не передавайте весь ваш каталог, это раскрыло бы все строки приложения, а не те, на которые смотрит пользователь. Реестр отслеживается по монтированию и считается по ссылкам: постоянно присутствующие на экране строки (заголовок, 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"] }); 6. Фильтр по namespace (опционально)
Экран, который рендерит несколько namespace, может ограничить панель именно тем, который интересует клиента, передайте опциональный namespace (string | string[]) в триггер/конфигурацию (feedbackPlugin() для React/Native, createFeedback() для Vue/Svelte, resolveKeys() / filterByNamespace() для /core). Он применяется после привязки к отображаемым ключам, показано = отображённое ∩ 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). 7. Сессия, выпущенная на сервере (все фреймворки)
Ключ сессии / группировки выпускается на стороне сервера при согласии. Клиент никогда не отправляет и не генерирует его сам, нет конфигурации groupingKey и нет поля запроса. При acceptTos() бэкенд возвращает его (привязанным к ограниченному JWT); каждый адаптер предоставляет его только для чтения через 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. 8. Согласие и безопасность
Версионированные пользовательские условия Sonenta предшествуют первой записи. В панелях React и React Native шаг с условиями отображается до любого аутентифицированного вызова, и именно нажатие самого конечного пользователя вызывает acceptTos(): загрузка строк по построению привязана к согласию. Если вы используете /core, создавайте клиент с autoAcceptTos: false и вызывайте acceptTos() только после согласия вашего пользователя, иначе первый аутентифицированный вызов примет условия за него. Затем клиент удерживает краткоживущий JWT, ограниченный только областью feedback:write, криптографически отделённый от аутентификации ваших клиентов, и прозрачно его обновляет. Конечные пользователи анонимны (непрозрачный id, без персональных данных). В @sonenta/feedback 1.2.x и ранее это поведение ПО УМОЛЧАНИЮ: клиент принимает условия при первом аутентифицированном вызове, если вы не передали autoAcceptTos: false. Версия 1.3.0 меняет умолчание и завершается безопасно, выбрасывая FeedbackError("not consented") вместо принятия за вашего пользователя.
Что вы получаете бесплатно
- Ноль ре-рендеров хоста. React/Native работают как изолированный соседний лист провайдера i18n; Vue/Svelte держат состояние открытия в собственном store. Добавление или открытие обратной связи никогда не вызывает ре-рендер вашего приложения.
- Согласие + серверная сессия обрабатываются. Принятие условий, выпущенная на сервере сессия, получение JWT, ротирующее обновление и единичный прозрачный повтор при 401, всё внутри SDK. Он никогда не выбрасывает исключение в ваш путь рендеринга.
- Транспорт с дебаунсом и батчингом. Оценки и предложения ставятся в очередь и отправляются по дебаунсу (по умолчанию 1,5 с), при достижении максимального батча или при закрытии. Best-effort: неудавшийся батч повторно ставится в очередь один раз, затем поглощается.
- Модерация перед публикацией. Ничто, отправленное конечным пользователем, не публикуется автоматически. Предложения попадают в статусе
pendingв очередь модерации вашего дашборда, вы одобряете, отклоняете или применяете их обычным аудируемым путём редактирования. Оценки агрегируются в реальном времени по ключу / по языку.