Lingara Lingara Dokumentation Ratgeber API Bibliotheken Apps Erstellen Einbetten Web-App
Sprache: Deutsch

Website-Widget

Diese Seite ist aus dem Englischen übersetzt. Wenn die beiden voneinander abweichen, ist die englische Seite maßgeblich. Englische Seite lesen

Auf einer Website läuft Lingara als Custom Element, <lingara-embed>. Das Element zeichnet die Übungen von Lingara in einem Frame, den Lingara ausliefert, sodass Ihre Seite die Daten des Lernenden nie sieht und die Seite von Lingara nie Ihre.

Das Element einbinden

Installieren Sie @lingara/embed über npm, oder laden Sie seinen Modul-Build von einem CDN, wenn Ihre Website keinen Bundler hat. Setzen Sie dann <lingara-embed> auf die Seite und definieren Sie es. Das Element nimmt:

  • plan-id, den zu übenden Lektionsplan, einen, der dem Spieler gehört;
  • surface, was angezeigt werden soll: practice, der Standard und die einzige Oberfläche, die es gibt;
  • theme, eines von light, dark oder auto;
  • locale, die Sprache der eigenen Wörter des Frames, die sonst dem Lernenden folgt;
  • title, der barrierefreie Name des Frames.

Das Styling endet beim Theme. Ihre Seite kann nicht in den Frame hineingreifen, und das Theme ist die einzige Einstellung, die er anbietet.

Ihm Tokens geben

Setzen Sie den tokenProvider des Elements auf eine Funktion, die Ihren eigenen Server um ein Token bittet. Ihr Server stellt es mit dem Origin der Seite aus, mit einem Server-Kit, und antwortet mit dem Token und seinem Ablaufzeitpunkt. Das Element übergibt das Token per Nachricht an den Frame, und es wird nie in einer URL übertragen.

Registrieren Sie jeden Origin, auf dem das Widget läuft, bevor Sie dafür ein Token ausstellen, in den Einstellungen Ihres Clients in der Konsole. Die Seite Server-Kits beschreibt, wie. Die API lehnt ein Token ab, das für einen anderen Origin ausgestellt wurde, und der Frame zeigt dann einen Fehler statt der Übung.

Ein Server-Kit kann auch das ganze Snippet für Sie schreiben, Element und Token-Provider zusammen, mit der Script-Nonce Ihrer Seite, falls Ihre Content Security Policy eine verlangt. Das letzte Beispiel unten zeigt es.

Hören, was der Frame sagt

Der Frame spricht mit dem Element über ein versioniertes Nachrichtenprotokoll, und das Element macht aus dem, was Ihre Seite braucht, DOM-Events:

  • lingara:practice-completed, wenn der Lernende ein Set abschließt, mit seiner planId, setNumber, correct und total;
  • lingara:error, wenn der Frame stoppt, mit einem code und retryable. Ein Fehler mit Wiederholungsmöglichkeit bedeutet, dass ein neues Einbinden gelingen kann; wann Sie es erneut versuchen, entscheiden Sie.

Das Ergebnis der Übung erreicht auch Ihren Server, wie unter Übungsergebnisse beschrieben.

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)
})
Sprache des Codebeispiels

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();