Lingara Lingara مستندات رهنماها API کتابخانه‌ها اپلیکیشن‌ها ساختن جاسازی اپلیکیشن وب
زبان: دری

ویجت ویب‌سایت

این صفحه از انگلیسی ترجمه شده است. اگر این دو با هم فرق داشته باشند، صفحهٔ انگلیسی درست است. صفحهٔ انگلیسی را بخوانید

در یک ویب‌سایت، Lingara به شکل یک عنصر سفارشی، <lingara-embed>، اجرا می‌شود. این عنصر تمرین Lingara را در فریمی که Lingara ارائه می‌کند ترسیم می‌کند، پس صفحهٔ شما هرگز معلومات آموزنده را نمی‌بیند و صفحهٔ Lingara هرگز معلومات شما را نمی‌بیند.

گذاشتن عنصر

@lingara/embed را از npm نصب کنید، یا اگر سایت شما bundler ندارد، نسخهٔ ماژول آن را از یک 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();