Lingara Lingara 文档 学习指南 API 库 应用 构建 网页版
语言: 中文简体

卡片

本页译自英文。如两者有出入,以英文页面为准。 阅读英文页面

卡片是一份简短的元素列表,由 Lingara 以自己的样式绘制。卡片里没有输入框、图片、脚本或标记。应用无法把自己的代码或自己的外观放到学习者的屏幕上,所以每张卡片读起来都和 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 自己的“此应用没有响应”卡片(以他们的语言显示),或者你上一张正常的卡片,并标记为“不是最新”。学习者永远不会被告知原因。只有你,作为应用的所有者,能看到原因:

  • 你的回复: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.
}