Lingara Lingara Documentazione Guide API Librerie App Crea Incorporare App web
Lingua: Italiano

Widget per siti web

Questa pagina è tradotta dall'inglese. Se le due versioni differiscono, fa fede la pagina in inglese. Leggi la pagina in inglese

Su un sito web, Lingara funziona come elemento personalizzato, <lingara-embed>. L’elemento disegna la pratica di Lingara in un frame servito da Lingara, così la tua pagina non vede mai i dati dello studente e la pagina di Lingara non vede mai i tuoi.

Montare l’elemento

Installa @lingara/embed da npm, oppure carica la sua build come modulo da una CDN se il tuo sito non ha un bundler. Poi inserisci <lingara-embed> nella pagina e definiscilo. L’elemento accetta:

  • plan-id, il piano di lezione da praticare, uno di cui il giocatore è proprietario;
  • surface, cosa mostrare: practice, il valore predefinito e l’unica superficie esistente;
  • theme, uno tra light, dark o auto;
  • locale, la lingua dei testi propri del frame, che altrimenti segue lo studente;
  • title, il nome accessibile del frame.

La personalizzazione dello stile si ferma al tema. La tua pagina non può raggiungere l’interno del frame, e il tema è l’unica impostazione che offre.

Fornirgli i token

Imposta il tokenProvider dell’elemento su una funzione che chiede un token al tuo server. Il tuo server lo emette con l’origine della pagina, usando un kit server, e risponde con il token e la sua scadenza. L’elemento consegna il token al frame tramite messaggio, e il token non viaggia mai in un URL.

Registra ogni origine su cui gira il widget prima di emettere token per essa, nelle impostazioni del tuo client nella console. La pagina dei kit server spiega come. L’API rifiuta un token emesso per qualsiasi altra origine, e il frame mostra un errore al posto della pratica.

Un kit server può anche scrivere per te l’intero snippet, elemento e fornitore di token insieme, con il nonce di script della tua pagina se la tua content security policy ne richiede uno. L’ultimo esempio qui sotto lo mostra.

Ascoltare ciò che dice il frame

Il frame comunica con l’elemento tramite un protocollo di messaggi con versione, e l’elemento trasforma ciò di cui la tua pagina ha bisogno in eventi DOM:

  • lingara:practice-completed quando lo studente completa un set, con i suoi planId, setNumber, correct e total;
  • lingara:error quando il frame si arresta, con un code e retryable. Un errore ripetibile significa che un nuovo montaggio potrebbe riuscire; quando riprovare lo decidi tu.

Anche il risultato della pratica raggiunge il tuo server, come descritto in risultati della pratica.

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)
})
Linguaggio dell'esempio di codice

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