ウェブサイトウィジェット このページは英語から翻訳されています。内容に相違がある場合は、英語のページが正しい内容です。 英語のページを読む
ウェブサイトでは、Lingara はカスタム要素 <lingara-embed> として動作します。この要素は Lingara が配信するフレームの中に Lingara の練習を描画するため、あなたのページが学習者のデータを見ることはなく、Lingara のページがあなたのデータを見ることもありません。
要素をマウントする
npm から @lingara/embed をインストールするか、サイトにバンドラーがない場合は 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 Rust Go Java Kotlin Ruby PHP C# 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 ();