Lingara Lingara المستندات أدلة التعلّم API المكتبات التطبيقات البناء التضمين تطبيق الويب
اللغة: العربية

أداة الموقع الإلكتروني

هذه الصفحة مترجمة من الإنجليزية. إذا اختلفت النسختان، فالصفحة الإنجليزية هي الصحيحة. اقرأ الصفحة الإنجليزية

على الموقع الإلكتروني، تعمل Lingara كعنصر مخصّص، <lingara-embed>. يرسم العنصر تدريب Lingara داخل إطار تقدّمه Lingara، فلا ترى صفحتك بيانات المتعلّم أبدًا، ولا ترى صفحة Lingara بياناتك أبدًا.

ركّب العنصر

ثبّت @lingara/embed من npm، أو حمّل نسخة الوحدة منه من CDN إذا لم يكن لموقعك أداة تجميع. ثم ضع <lingara-embed> على الصفحة وعرّفه. يأخذ العنصر:

  • plan-id، خطة الدرس المراد التدرّب عليها، وهي خطة يملكها اللاعب؛
  • surface، ما يُعرض: practice، القيمة الافتراضية والواجهة الوحيدة الموجودة؛
  • theme، واحد من light أو dark أو auto؛
  • locale، لغة كلمات الإطار نفسه، والتي تتبع المتعلّم ما لم تُحدَّد؛
  • title، الاسم الذي يُعرَّف به الإطار لتقنيات الوصول.

يتوقّف التنسيق عند السمة. لا يمكن لصفحتك الوصول إلى داخل الإطار، والسمة هي الإعداد الوحيد الذي يوفّره.

زوّده بالرموز

عيّن tokenProvider الخاص بالعنصر إلى دالة تطلب رمزًا من خادمك أنت. يُصدر خادمك الرمز بأصل الصفحة، باستخدام حزمة خادم، ويجيب بالرمز وموعد انتهاء صلاحيته. يسلّم العنصر الرمز إلى الإطار عبر رسالة، ولا ينتقل الرمز أبدًا في عنوان URL.

سجّل كل أصل تعمل عليه الأداة قبل أن تُصدر رمزًا له، في إعدادات عميلك في وحدة التحكم. تشرح صفحة حِزم الخادم كيف. يرفض الـ API أي رمز أُصدر لأصل آخر، ويعرض الإطار خطأً بدلًا من التدريب.

يمكن لحزمة الخادم أيضًا أن تكتب لك المقتطف كله، العنصر ومزوّد الرموز معًا، مع قيمة nonce للسكربت في صفحتك إذا كانت سياسة أمان المحتوى لديك تتطلب ذلك. يعرض المثال الأخير أدناه ذلك.

استمع إلى ما يقوله الإطار

يتحدث الإطار إلى العنصر عبر بروتوكول رسائل ذي إصدارات، ويحوّل العنصر ما تحتاجه صفحتك إلى أحداث DOM:

  • lingara:practice-completed عندما يُنهي المتعلّم مجموعة، مع planId وsetNumber وcorrect وtotal الخاصة بها؛
  • lingara:error عندما يتوقف الإطار، مع code وretryable. يعني الخطأ القابل لإعادة المحاولة أن تركيبًا جديدًا قد ينجح؛ أما متى تحاول مجددًا فقرارك أنت.

تصل نتيجة التدريب إلى خادمك أيضًا، كما هو موضّح في نتائج التدريب.

npm install @lingara/embed@0.1.0-alpha.6
# No bundler? One module script instead:
# <script type="module" src="https://cdn.jsdelivr.net/npm/@lingara/embed@0.1.0-alpha.6/dist/lingara-embed.min.js"></script>
<!-- plan-id is a lesson plan your server made for this player. -->
<lingara-embed surface="practice" plan-id="0b9d4f2e-3c1a-4e8b-9f6d-2a7c5e1b8d40" theme="auto" title="Practice"></lingara-embed>
<script type="module">
  import { defineLingaraEmbed } from '@lingara/embed'
  defineLingaraEmbed()
</script>
// Your own endpoint, on your own server. It mints with this page's origin and
// returns exactly { token, expires_at }: the token (lgr_et_…) never touches a URL.
widget.tokenProvider = async () => {
  const response = await fetch('/lingara/token', { method: 'POST', credentials: 'same-origin' })
  if (!response.ok) throw new Error(`token endpoint answered ${response.status}`)
  return response.json()
}
widget.addEventListener('lingara:practice-completed', (event) => {
  const { planId, setNumber, correct, total } = (event as CustomEvent).detail
  console.log(`set ${setNumber} of ${planId}: ${correct}/${total}`)
})
widget.addEventListener('lingara:error', (event) => {
  const { code, retryable } = (event as CustomEvent).detail
  // `retryable` means a remount may succeed; when to try is yours to decide.
  if (retryable) setTimeout(() => widget.reload(), 30_000)
  else console.warn('Lingara practice stopped:', code)
})
لغة مثال الشيفرة

TypeScript

// The page that mounts the widget: the token never appears in it.
export function practicePage(planId: string, cspNonce: string): string {
  const widget = widgetSnippet({ planId, tokenEndpoint: '/lingara/token', theme: 'auto', locale: 'fr', title: 'Pratique', nonce: cspNonce })
  return `<!doctype html><title>Practice</title>${widget}`
}

Rust

// The element, its script build and a token provider that POSTs to your
// token route. The token never appears in the markup.
let html = widget_snippet(&SnippetOptions {
    locale: Some("fr".into()),
    nonce: Some(csp_nonce.into()), // your page's CSP nonce, for a strict script-src
    ..SnippetOptions::new(plan_id, "/lingara/token")
})?;

Go

// The plan your server made for this player, and the token route above.
// Pass the request's CSP nonce when your script-src allows no inline
// script; the token itself never appears in the markup.
theme := "auto"
html, err := lingaraembed.WidgetSnippet(lingaraembed.SnippetOptions{
	PlanID:        planID,
	TokenEndpoint: "/lingara/token",
	Theme:         &theme,
	Nonce:         &cspNonce,
})
if err != nil {
	return "", err // a *lingaraembed.SnippetError names the refused option
}
return template.HTML(html), nil // already escaped by the kit

Java

// planId is the lesson plan your server made for this player. The element
// POSTs to tokenEndpoint with same-origin cookies: the token never appears
// in the markup. Pass this request's CSP nonce if your page sets one.
String html =
    WidgetSnippet.render(
        SnippetOptions.builder(planId, "/lingara/token").locale("fr").nonce(cspNonce).build());

Kotlin

// planId is the lesson plan your server made for this player. The element
// POSTs to tokenEndpoint with same-origin cookies: the token never appears
// in the markup. Pass this request's CSP nonce if your page sets one.
val html =
    widgetSnippet(
        SnippetOptions(planId, tokenEndpoint = "/lingara/token").apply {
            locale = "fr"
            nonce = cspNonce
        },
    )

Ruby

# The plan your server made for this player, rendered into your page.
# nonce: your page's CSP nonce, when its script-src allows no inline script.
def self.widget(plan_id, csp_nonce)
  Lingara::Embed.widget_snippet(plan_id: plan_id, token_endpoint: "/lingara/token", locale: "fr", nonce: csp_nonce)
end

PHP

use Lingara\Embed\SnippetOptions;
use Lingara\Embed\WidgetSnippet;

// In your page's template: the plan your server made for this player, and
// your token route. Pass the request's CSP nonce if your script-src allows
// no inline script. A bad option throws SnippetException naming its field.
function practiceWidget(string $planId, string $cspNonce): string
{
    return WidgetSnippet::render(new SnippetOptions(
        planId: $planId,
        tokenEndpoint: '/lingara/token',
        theme: 'auto',
        nonce: $cspNonce,
    ));
}

C#

app.MapGet("/practice/{planId}", (string planId, HttpContext http) =>
{
    var widget = WidgetSnippet.Render(new SnippetOptions
    {
        PlanId = planId,                              // the plan your server made for this player
        TokenEndpoint = "/lingara/token",             // same origin: the element sends this page's cookies
        Nonce = http.Items["csp-nonce"] as string,     // your CSP's nonce, if script-src has one
    });
    return Results.Content($"<!doctype html><title>Practice</title>\n{widget}", "text/html");
}).RequireAuthorization();