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 اعتماديات نظيرة اختيارية, تحتاجها نقطة الدخول المطابقة فقط. تُنشَر مع الإضافة عند إطلاق 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()
| الخيار | النوع | الافتراضي |
|---|---|---|
| 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 دوّار. استخدمه مباشرةً لأي إطار عمل دون مُحوّل من الطرف الأول.
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)؛ ولا تمرّر أبدًا كامل كتالوجك, فهذا سيكشف كل سلسلة في التطبيق، لا ما ينظر إليه المستخدم. السجل مُتتبَّع عند التركيب ومعدود بالمرجع: تبقى السلاسل الدائمة المعروضة دومًا (ترويسة، أيقونة علوية) مسجّلة طالما بقي مكوّنها مُركّبًا, لا إعادة تعيين لكل عرض. (لا يوجد 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 اختياريًا (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). 7. جلسة مُولَّدة من جانب الخادم (جميع أطر العمل)
مفتاح الجلسة / التجميع مُولَّد من جانب الخادم عند الموافقة. لا يرسله العميل ولا يولّده ذاتيًا أبدًا, لا إعداد 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. 8. الموافقة والأمان
تسبق شروطُ المستخدم النهائي المُصدَّرة بإصدار أولَ عملية كتابة. في لوحتَي React و React Native، تظهر خطوة الشروط قبل أي استدعاء موثَّق، والنقرة التي يقوم بها المستخدم النهائي نفسه هي ما يستدعي acceptTos(): فجلب النصوص مقيَّد بالموافقة بحكم البنية. أما إذا استخدمت /core، فأنشئ العميل بالخيار autoAcceptTos: false ولا تستدعِ acceptTos() إلا بعد موافقة مستخدمك، وإلا فإن أول استدعاء موثَّق سيوافق نيابةً عنه. يحتفظ العميل بعدها برمز JWT قصير العمر مقصور على نطاق feedback:write فقط، ومفصول تشفيريًا عن مصادقة عملائك، ويُجدَّد بشفافية. المستخدمون النهائيون مجهولون (معرّف غير شفاف، دون بيانات شخصية). في @sonenta/feedback الإصدار 1.2.x وما قبله، هذا هو السلوك الافتراضي: يوافق العميل عند أول استدعاء موثَّق ما لم تمرّر autoAcceptTos: false. أما الإصدار 1.3.0 فيعكس هذا الافتراضي ويفشل بأمان، إذ يرمي FeedbackError("not consented") بدلاً من الموافقة نيابةً عن مستخدمك.
ما تحصل عليه مجانًا
- صفر إعادة رسم للمضيف. يعمل React/Native كورقة شقيقة معزولة عن موفّر i18n؛ ويحتفظ Vue/Svelte بحالة الفتح في مخزنهما الخاص. إضافة الملاحظات أو فتحها لا يعيد رسم تطبيقك أبدًا.
- تُدار الموافقة وجلسة الخادم. قبول شروط الاستخدام، والجلسة المُولَّدة من جانب الخادم، والحصول على JWT، والتجديد الدوّار، وإعادة محاولة شفافة واحدة عند 401, كل ذلك داخل الـ SDK. ولا يطلق استثناءً أبدًا في مسار الرسم لديك.
- نقل مُؤجَّل ومُجمَّع دفعيًا. تُصفّ التقييمات والاقتراحات وتُرسَل عند انقضاء التأجيل (1.5 ثانية افتراضيًا)، أو عند بلوغ الحد الأقصى للدفعة، أو عند الإغلاق. أفضل جهد: تُعاد دفعة فاشلة إلى الصف مرة واحدة، ثم تُمتَصّ.
- إشراف قبل النشر. لا شيء يقدّمه مستخدم نهائي ينشر مباشرةً تلقائيًا. تصل الاقتراحات بحالة
pendingفي صف الإشراف في لوحة تحكمك, توافق أو ترفض أو تطبّق عبر مسار التحرير المُدقَّق المعتاد. وتتجمّع التقييمات في لوحة فورية لكل مفتاح / لكل لغة.