SDK · エンドユーザーによる評価
プレビュー@sonenta/feedback
あなた自身のエンドユーザーが、本番環境のアプリ内から翻訳を評価(5★)し、提案できるようにします。1 つのパッケージに 5 つのエントリーポイント(/react、/native、/vue、/svelte、/core)があり、同じワイヤー、サーバー側で発行される同じセッション、同じモデレーション用バックオフィスを共有します。React と React Native は、すでにお使いの @sonenta/*-i18n プロバイダーに接続します(第 2 のコンテキストもホストの再レンダリングもありません)。Vue と Svelte は、それぞれのイディオムに沿った独立したアダプターです。Pro 以上の有料アドオンとしてご利用いただけます。
@sonenta/feedback パッケージは、Sonenta V1 ローンチ時に「エンドユーザーによる翻訳評価」アドオンと共に提供されます。まだ npm では公開されていません。ワイヤープロトコルは確定済み(v3)ですが、フレームワークバインディングは安定化の途上にあり、ローンチ前に変更される可能性があります。
1. インストール(ローンチ時)
パッケージは 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 プラグイン
既存の @sonenta/react-i18next(>= 0.7.0)プロバイダーの plugins スロットに feedbackPlugin() を追加してください。新しいプロバイダーも第 2 のコンテキストも不要です。プロバイダーはプラグインの setup() を一度呼び出し、自身の apiBase / projectId / defaultLocale を再利用します。パネルは独立した兄弟リーフとしてマウントされ、開閉状態を保持する専用のストアを持つため、パネルを開いてもホストツリーが再レンダリングされることは決してありません。controllerRef 経由で提供されるコントローラー(または 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() のすべてのオプション
| オプション | 型 | デフォルト |
|---|---|---|
| controllerRef | Ref<Controller> | — |
| onReady | (c) => void | — |
| keys | string[] | auto-discovered |
| flushDebounceMs | number | 1500 |
| maxBatch | number | 20 |
| defaultButton | boolean | false |
3. React Native / Expo
/native エントリーから同じパターンを使います。Expo アプリ内の同じ @sonenta/react-i18next プロバイダーの plugins スロットに feedbackPlugin() を追加し、コントローラー経由でトリガーします。追加のネイティブモジュールは不要で、トークンの保存にはプラットフォームのセキュアストアを使用します。
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 は独立したアダプターです。設定は明示的で、継承する i18n プロバイダーはありません。createFeedback(config) は { client, isOpen, controller, FeedbackPanel } を返します。<FeedbackPanel /> をルート付近に一度マウントし(body へ Teleport します)、ご自身の CTA から controller.open() を呼び出してください。開閉状態も同様に独立しているため、アプリが再レンダリングされることはありません。
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 はヘッドレスでイディオムに沿っています。createFeedback(config) は Svelte ストア(isOpen(Writable)、strings(Writable))に加え、open()、close()、loadStrings()、rate()、suggest() を返します。ストアから独自のパネルをレンダリングしてください。SDK はトランスポート、同意、サーバーセッションを担います。
+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 は、すべてのアダプターの基盤となる確定済みの 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.com",6 projectId: "proj_xxx", language: "fr",7});8await client.acceptTos(); // server mints the session9await client.loadStrings(); client.rate(/* … */); client.suggest(/* … */); 7. 表示中のキーへのスコープ設定(自動)
パネルは、@sonenta/*-i18n SDK が生成するグローバルなキーレジストリを通じて、現在のビューに実際に表示されているキーへ自動的にスコープを設定します。設定は不要です。明示的な 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"] }); 8. namespace フィルター(オプション)
複数の namespace をレンダリングする画面では、顧客が関心を持つものだけにパネルのスコープを絞れます。トリガー/設定にオプションの namespace(string | string[])を渡してください(React/Native では feedbackPlugin()、Vue/Svelte では createFeedback()、/core では resolveKeys() / 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. サーバー側で発行されるセッション(全フレームワーク)
セッション/グルーピングキーは、同意時にサーバー側で発行されます。クライアントがこれを送信したり自ら生成したりすることは決してありません。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. 10. 同意とセキュリティ(自動)
最初の書き込みの前に、エンドユーザーはバージョン管理された Sonenta エンドユーザー利用規約に同意します。SDK はその後、feedback:write スコープのみに限定された短命の JWT を取得し(あなたの顧客認証とは暗号的に分離されています)、透過的にローテーションします。エンドユーザーは匿名です(不透明な id で、PII はありません)。セキュリティ面で実装する必要はありません。
無料で得られるもの
- ホストの再レンダリングはゼロ。 React/Native は i18n プロバイダーから独立した兄弟リーフとして動作し、Vue/Svelte は開閉状態を独自のストアで保持します。フィードバックの追加や表示でアプリが再レンダリングされることは決してありません。
- 同意とサーバーセッションを処理済み。 利用規約への同意、サーバー側で発行されるセッション、JWT の取得、ローテーションする更新、401 時の透過的な 1 回のリトライ、これらすべてが SDK 内で行われます。レンダリングパスに例外を投げることは決してありません。
- デバウンス&バッチ化されたトランスポート。 評価と提案はキューに入れられ、デバウンス(デフォルト 1.5 秒)、最大バッチ到達時、またはクローズ時にフラッシュされます。ベストエフォートで、失敗したバッチは一度再キューされ、その後は黙って破棄されます。
- 公開前のモデレーション。 エンドユーザーが送信したものが自動的に公開されることはありません。提案はダッシュボードのモデレーションキューに
pendingとして届きます。通常の監査済み編集パスを通じて、承認、却下、または適用できます。評価はキーごと/言語ごとのリアルタイムダッシュボードに集計されます。