Lingara Lingara 說明文件 學習指南 API 函式庫 App 建立 網頁版
語言: 繁體中文

卡片

本頁譯自英文。如兩者有出入,以英文頁面為準。 閱讀英文頁面

卡片是一份簡短的元素清單,由 Lingara 以自己的樣式繪製。卡片裡沒有輸入欄位、圖片、指令碼或標記。App 無法把自己的程式碼或自己的外觀放到學習者的螢幕上,所以每張卡片讀起來都和 Lingara 的其他部分一致。你的回覆是 JSON:一個 card,其中包含它的 elements,以及選用的 tutor_note(請參閱導師備註)。

元素

每個元素都有一個 type,其值為以下八種之一:

元素欄位顯示內容
headingtext, level一個標題。level 為 1 或 2,其他任何值都會變成 2。其文字最多 80 個字元。
texttext, lang一個最多 600 個字元的段落。這是唯一會保留換行的地方。
termword, reading, gloss, lang一個要學習的詞。只有 word 是必填。詞最多 60 個字元,讀音最多 120 個,釋義最多 160 個。
listitems最多 20 個項目。每個項目是一個 text 或一個 term,欄位與同名元素相同。
progressvalue, label一個進度列。value 的範圍是 0 到 1,標籤最多 60 個字元。
divider卡片各部分之間的一條分隔線。
buttonlabel, action, style一個把其 action 傳送到你的伺服器的按鈕。style 為 primary 或 secondary,標籤最多 32 個字元。
linklabel, url一個連結,Lingara 先詢問學習者,再於其瀏覽器中開啟。標籤最多 60 個字元。

lang 是一個語言標記,例如 ja,讓 Lingara 繪製正確的文字系統。不是語言標記的值會被移除。

限制

  • 一張卡片最多包含 24 個元素。第 24 個之後的元素會被捨棄。
  • 一份清單最多包含 20 個項目。第 20 個之後的項目會被捨棄,空的清單也會被捨棄。
  • 一張卡片最多包含 4 個按鈕。第 4 個之後的按鈕會被捨棄。
  • 超過其限制的文字會被截短以符合限制,並以 … 結尾。字元以 Unicode 純量值計算。
  • 每個字串中的控制字元和方向覆寫字元都會被移除。
  • 位址不是 HTTPS、帶有使用者名稱或指向 IP 位址的連結會被捨棄。
  • action 超過 64 個字元,或使用了字母、數字、_、.、: 和 - 以外任何字元的按鈕會被捨棄。
  • 經過這些規則後已不剩任何文字的元素會被捨棄。

這些規則很寬容:Lingara 會截短或捨棄放不下的部分,並繪製其餘部分。套件會在你回覆之前套用相同的規則,讓你能看到哪些內容會被改變。

嚴格解析

詞彙是封閉的。未知的元素類型、元素沒有的欄位、JSON 型別錯誤的值,或者根本不是 JSON 的回覆,都會讓整個回覆變成備援卡片。Lingara 絕不會繪製它無法讀取的卡片的一部分。

大小與時間

回覆最多 32 KiB。Lingara 給你的伺服器 3 秒回應呈現請求、5 秒回應動作請求,兩者都不會重試。回覆必須是狀態 200 並帶有 JSON 內容類型。不會跟隨重新導向。

按鈕與動作

學習者按下按鈕時,Lingara 會向你的伺服器傳送一個帶有該按鈕 action 的動作請求。你的伺服器以一張新卡片回應,新卡片會取代舊卡片。同一次按下可能會送達兩次,所以要讓每個動作都能安全地重複執行。

卡片失敗時

當 Lingara 無法取得一張它能繪製的卡片時,學習者會看到 Lingara 自己的「此 App 沒有回應」卡片(以他們的語言顯示),或是你上一張正常的卡片,並標示為「並非最新」。學習者永遠不會被告知原因。只有你,作為 App 的擁有者,能看到原因:

  • 你的回覆:invalid(不是 Lingara 能讀取的卡片)、empty(套用限制後已沒有可顯示的內容)或 too_large(超過 32 KiB)。
  • 連線到你的伺服器:timeout、http_error(200 以外的任何狀態)、transport(連線或 TLS 失敗,例如憑證過期)或 blocked(render_url 指向私人位址或 IP 常值位址)。
  • Lingara 這一方:unavailable(Lingara 無法為請求簽章)或 busy(處理中的請求已經太多)。
程式碼範例語言

TypeScript

function todayCard(streak: number): Card {
  return card()
    .heading("Today's five", 1)
    .term({ word: "雨", reading: "yǔ", gloss: "rain", lang: "zh" })
    .list([item.text("Review 3 words"), item.term({ word: "二", reading: "èr" })])
    .button(`Done (${streak})`, "done")
    .build(); // Throws CardLimitError naming the rule the relay would clamp.
}

Rust

fn today_card(title: &str, lang: &str) -> Result<lingara_apps::Card, BoxError> {
    Ok(card()
        .heading(title, 1)
        .term(Term::new("雨").reading("yǔ").gloss("rain").lang(lang))
        .list([item::text("Say it aloud"), item::term(Term::new("下雨").gloss("to rain"))])
        .button("Next word", "next")
        .build()?)
}

Go

card, err := lingaraapps.NewCard().
	Heading("Today", 1).
	Term(lingaraapps.Term{Word: "雨", Reading: "yǔ", Gloss: "rain", Lang: "zh"}).
	List(
		lingaraapps.Item.Text("Say it aloud three times."),
		lingaraapps.Item.Term(lingaraapps.Term{Word: "雨天", Gloss: "rainy day", Lang: "zh"}),
	).
	Button("Next", "next").
	Build()
if err != nil {
	// A *lingaraapps.CardLimitError names the rule the card breaks,
	// such as text_length or list_items. Nothing is cut for you.
	return lingaraapps.Card{}, err
}

Java

import com.getlingara.apps.Card;
import com.getlingara.apps.Item;

Card card =
    Card.card()
        .heading("Today's five", 1)
        .term("雨", "yǔ", "rain", "zh")
        .list(Item.text("Say it aloud"), Item.term("二", "èr", "two", "zh"))
        .button("Next", "next")
        .build(); // a CardLimitException names the first rule the card breaks

Kotlin

import com.getlingara.apps.kotlin.Card
import com.getlingara.apps.kotlin.card

val c =
    card {
        heading("Today's five", 1)
        term("雨", reading = "yǔ", gloss = "rain", lang = "zh")
        list(item.text("Say it aloud"), item.term("二", reading = "èr", gloss = "two", lang = "zh"))
        button("Next", "next")
    } // a CardLimitException names the first rule the card breaks

Ruby

def self.today_card(streak)
  Lingara::Apps.card
    .heading("Today's five", 1)
    .term(word: "雨", reading: "yǔ", gloss: "rain", lang: "zh")
    .list([Lingara::Apps.item.text("Review 3 words"), Lingara::Apps.item.term(word: "二", reading: "èr")])
    .button("Done (#{streak})", "done")
    .build # Raises Lingara::Apps::CardLimitError naming the rule Lingara would clamp.
end

PHP

use Lingara\Apps\Card;
use Lingara\Apps\Generated\Card as CardModel;
use Lingara\Apps\Item;

function todayCard(int $streak): CardModel
{
    return Card::create()
        ->heading("Today's five", 1)
        ->term('雨', reading: 'yǔ', gloss: 'rain', lang: 'zh')
        ->list([Item::text('Review 3 words'), Item::term('二', reading: 'èr')])
        ->button("Done ({$streak})", 'done')
        ->build(); // Throws CardLimitException naming the rule the relay would clamp.
}