تخطَّ إلى المحتوى
Sonenta

SDK · تقييم المستخدم النهائي

معاينة

@sonenta/feedback

دع مستخدميك النهائيين أنفسهم يقيّمون (5★) ويقترحون ترجمات من داخل تطبيقك المُنتَج. حزمة واحدة، وخمس نقاط دخول — /react و/native و/vue و/svelte و/core — بالشبكة نفسها، والجلسة نفسها المُولَّدة من جانب الخادم، ومكتب الإشراف الخلفي نفسه. يندمج React وReact Native في موفّر @sonenta/*-i18n الذي تشغّله بالفعل (لا سياق ثانٍ، ولا إعادة رسم للمضيف)؛ أما Vue وSvelte فهما مُحوّلان مستقلان اصطلاحيان. متاح كـ إضافة مدفوعة ابتداءً من Pro.

تُشحَن حزمة @sonenta/feedback مع إضافة «تقييم المستخدم النهائي للترجمات» عند إطلاق Sonenta V1. وهي ليست على npm بعد — عقد الاتصال مُجمَّد (v3)؛ أما روابط أطر العمل فتتثبّت وقد تتغيّر بعد قبل الإطلاق.

1. التثبيت (عند الإطلاق)

حزمة واحدة. استورد نقطة الدخول الخاصة بإطار عملك: @sonenta/feedback/react أو /native (RN/Expo) أو /vue أو /svelte أو /core لكل ما عدا ذلك. vue / svelte اعتماديات نظيرة اختيارية — تحتاجها نقطة الدخول المطابقة فقط. تُنشَر مع الإضافة عند إطلاق 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 (الويب) — مكوّن 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()
الخيار النوع الافتراضي
controllerRefRef<Controller>
onReady(c) => void
keysstring[]auto-discovered
flushDebounceMsnumber1500
maxBatchnumber20
defaultButtonbooleanfalse

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. Vue

@sonenta/feedback/vue مُحوّل مستقل — إعداد صريح، ولا موفّر i18n يُورَث منه. تُرجِع createFeedback(config) القيمة { client, isOpen, controller, FeedbackPanel }. ركّب <FeedbackPanel /> مرة واحدة قرب الجذر (تُنقَل عبر Teleport إلى body) واستدعِ 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 دوّار. استخدمه مباشرةً لأي إطار عمل دون مُحوّل من الطرف الأول.

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. تحديد نطاق المفاتيح المعروضة (تلقائي)

تحدّد اللوحة نطاقها تلقائيًا على المفاتيح المعروضة فعليًا على العرض الحالي، عبر السجل العام للمفاتيح الذي يُنتجه SDK @sonenta/*-i18n — دون أي إعداد. مرّر مصفوفة keys صريحة كاحتياط فقط (مثلًا سلاسل غير مصدرها @sonenta/*-i18n)؛ ولا تمرّر أبدًا كامل كتالوجك — فهذا سيكشف كل سلسلة في التطبيق، لا ما ينظر إليه المستخدم. السجل مُتتبَّع عند التركيب ومعدود بالمرجع: تبقى السلاسل الدائمة المعروضة دومًا (ترويسة، أيقونة علوية) مسجّلة طالما بقي مكوّنها مُركّبًا — لا إعادة تعيين لكل عرض. (لا يوجد 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 اختياريًا (string | string[]) على المُطلِق/الإعداد (feedbackPlugin() لـ React/Native، وcreateFeedback() لـ Vue/Svelte، وresolveKeys() / filterByNamespace() لـ /core). يتركّب بعد تحديد نطاق المفاتيح المعروضة — المعروض = المعروض ∩ نطاق الأسماء. القيمة غير المعرّفة أو "" أو [] تعني بلا مرشّح (مطابق لما سبق). ولا يرتدّ أبدًا إلى المشروع بأكمله.

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() يُرجعه الـ backend (مربوطًا داخل 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 على JWT قصير العمر مقيّد بنطاق feedback:write فقط — منفصل تشفيريًا عن مصادقة عميلك — ويجدّده بشفافية. المستخدمون النهائيون مجهولون (مُعرّف معتم، بلا أي معلومات تعريف شخصية). ولا تكتب أي شيء من جانب الأمان.

ما تحصل عليه مجانًا

بعد ذلك