Compare commits
23
Commits
main
..
d72959df70
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d72959df70 | ||
|
|
4d324903b4 | ||
|
|
29b1738162 | ||
|
|
f79ba6327e | ||
|
|
9b0f9d64cb | ||
|
|
6c0aeec628 | ||
|
|
70f7be9b88 | ||
|
|
a69d221ab6 | ||
|
|
2795987c3a | ||
|
|
c2361c5c3a | ||
|
|
3124bee288 | ||
|
|
7e29c5449f | ||
|
|
ef2fce207a | ||
|
|
14f934aa45 | ||
|
|
11929d6a03 | ||
|
|
2a2668829d | ||
|
|
9215931b13 | ||
|
|
82b1ecdf84 | ||
|
|
35b493e652 | ||
|
|
670260f8e2 | ||
|
|
4f22e974af | ||
|
|
6fcffc968f | ||
|
|
4341726162 |
@@ -1,6 +1,6 @@
|
||||
# 开口英语(暂定名)— AI 对话模块规格
|
||||
|
||||
> 版本:1.3|2026-09-17
|
||||
> 版本:1.1|2026-09-12
|
||||
> 模块定位:总产品中负责口语输出与真实情境练习的核心模块
|
||||
> 核心承诺:每天 5–10 分钟,完成一次你在真实生活中用得上的英语对话。
|
||||
|
||||
@@ -35,7 +35,7 @@ M1 通常 3–5 分钟,最多 5 个用户回答轮;简单种子任务达标
|
||||
|
||||
| 页面 | 目的 | 必须内容 |
|
||||
|---|---|---|
|
||||
| 欢迎与定位 | 了解用户起点 | 每日时长、可跳过的 3 分钟口语测评;学习目标固定为日常与旅行,不做选择 |
|
||||
| 欢迎与定位 | 了解用户起点和目标 | 目标选择、每日时长、可跳过的 3 分钟口语测评 |
|
||||
| 对话入口 | 让用户立刻开口 | 一个推荐场景、继续上次对话 |
|
||||
| 场景页 | 让用户自主选择练习任务 | 分级场景卡、目标、预计时长 |
|
||||
| 对话页 | 完成交流 | AI 回复、语音/文字输入、求助按钮、进度提示 |
|
||||
@@ -86,9 +86,9 @@ AI: Great. One hot tea. Is that all?
|
||||
| A1 | 酒店入住 | 办理入住 | 姓名 + 预订/入住需求 |
|
||||
| A1 | 约时间 | 约见面 | 提议时间并完成确认 |
|
||||
| A1 | 生病求助 | 描述简单不适 | 说一个症状并回应一次追问 |
|
||||
| A2 | 聊兴趣与家乡 | 交流周末、兴趣和家乡 | 连续完成 5 轮相关回应 |
|
||||
| A2 | 同事闲聊 | 交流周末/工作日常 | 连续完成 5 轮相关回应 |
|
||||
|
||||
A1 起的完整场景以《学习引擎规格》5.2 主题地图为准,上表只是示例。M1 先提供与 A0 核心课对应的受控对话;这些场景是起始内容,不是 A0 的对话总量。若出口能力尚未达标,系统继续围绕同阶段的薄弱点生成受控变式对话;A1 的生活场景和 A2 内容均作为后续扩展。
|
||||
M1 先提供与 A0 核心课对应的受控对话;这些场景是起始内容,不是 A0 的对话总量。若出口能力尚未达标,系统继续围绕同阶段的薄弱点生成受控变式对话;A1 的生活场景和 A2 内容均作为后续扩展。
|
||||
|
||||
## 7. 分级与对话控制
|
||||
|
||||
@@ -108,42 +108,9 @@ A1 起的完整场景以《学习引擎规格》5.2 主题地图为准,上表
|
||||
|
||||
AI 必须记住当前场景、用户身份、完成目标、已知信息及难度上限。它不能突然转换角色、重复问已回答的问题,或为了“自然聊天”而跳过任务目标。
|
||||
|
||||
给模型的指令必须把两件事分开:**本轮 AI 自己要说什么**,以及**学习者随后要完成什么任务**。两者混成一句会让模型替学习者把答案说出来。同时下发可用语言清单,但清单只作为模型的参考,不要求模型只能从清单里的短语拼句子;否则像 `What day is it today?` 这类已教句型也会因拆词不在清单而被模型回避。清单外词数由客户端统计,A0 每轮最多 1 个,超出按 7.3 的检查处理。
|
||||
给模型的指令必须把两件事分开:**本轮 AI 自己要说什么**,以及**学习者随后要完成什么任务**。两者混成一句会让模型替学习者把答案说出来。同时把当前课程及之前课程已教的词句作为可用语言清单随请求下发,A0 每轮最多 1 个清单外的词。
|
||||
|
||||
模型只需返回 `reply`(英文回复);`translation`(中文翻译)与 `feedback`(一句中文点评)可选,缺少可选字段不丢弃整条回复。是否完成任务由客户端按本轮必需表达判定,不采信模型的完成声明。回退示范句时必须在对话页说明原因,并区分两种情况:AI 不可用(网络、服务或返回格式错误)时提示“AI 暂时无法连接”;AI 有回复但没通过检查时提示“这一句换成了示范句”。两种都不能让备用文案冒充 AI 回复。
|
||||
|
||||
### 7.3 场景、任务与对话调度
|
||||
|
||||
对话不再以“脚本第 N 句”作为 AI 目标,而由三层数据和一个本地调度器驱动:
|
||||
|
||||
| 层 | 内容 | 来源 |
|
||||
|---|---|---|
|
||||
| LevelProfile(等级档) | 每轮英文词数上限、每轮问题数、清单外词上限、是否允许追问、中文辅助规则(即 7.2 表格) | 按等级固定在程序中 |
|
||||
| Scene(场景) | 场景 ID、学习者与 AI 身份、背景、开场句、任务列表 | 冻结的单元内容包 |
|
||||
| Task(任务) | 任务 ID、目标、引用的核心词句 ID、信息槽、示范句 | 冻结的单元内容包;判定条件(cues/done)由程序根据引用的词句 ID 编译 |
|
||||
|
||||
每一轮的调度步骤:
|
||||
|
||||
1. 客户端先在本地判定用户这句话完成了哪些任务、填了哪些信息槽,不等 AI;
|
||||
2. 调度器按任务顺序选出下一个未完成任务;全部完成则收尾并进入总结;
|
||||
3. 请求 AI 时下发:等级档、场景、**当前任务目标**、已收集的信息、可用语言清单、历史;要求先回应用户说的内容,再推进当前任务,一轮只推进一个任务;
|
||||
4. 客户端检查 AI 回复:
|
||||
- 通用检查:非空、是英文、不超过词数上限、问题数不超过上限、清单外词不超过上限、不替学习者说出答案、不重复问已收集的信息;
|
||||
- 任务检查:回复确实在推进当前任务(包含任务的提问线索,或是收尾语);
|
||||
5. 检查不通过时带着具体原因重试一次;仍不通过则使用当前任务的示范句,并按 7.2 提示“这一句换成了示范句”。
|
||||
|
||||
技术约定:
|
||||
|
||||
- 请求与返回均为 JSON;服务支持时开启 JSON 输出(如 DeepSeek 的 `response_format: {type: json_object}`)。历史中的 AI 轮同样以 `{"reply": ...}` 的 JSON 形式回放;若历史是纯文本,模型会跟着回纯文本或空白,导致误判为 AI 不可用。
|
||||
- 调用支持思考模式的模型时关闭思考,以控制延迟和费用。
|
||||
- 判定、计数和检查全部在客户端完成,模型返回的完成标记只作参考。
|
||||
|
||||
### 7.4 场景数据从哪里来
|
||||
|
||||
- **主线场景**:随单元内容包离线生成、校验、审核并经用户确认后冻结,流程见《学习引擎规格》第 5 节。运行时不生成主线场景。
|
||||
- **运行时变式**:AI 只能替换表面内容(人名、地点、物品、喜好等内容值,以及措辞和句子顺序),任务、引用的词句 ID 和判定条件保持不变;变式句同样要过 7.3 的通用检查。
|
||||
- **A0**:由现有课程对话脚本(`LessonDialogue`)转换为 Scene/Task;脚本中的问句作为任务示范句,`requiredTerms` 映射到 A0 核心词句 ID 后作为任务引用。脚本里的指令或操练轮(如“再说一遍”“跟我读”)不交给 AI,改为固定句。
|
||||
- **A2 及以上**:开放任务在 7.3 的检查之外,还按《学习引擎规格》5.8 用评分标准加 AI 评审判定完成。
|
||||
模型只需返回 `reply`(英文回复);`translation`(中文翻译)与 `feedback`(一句中文点评)可选,缺少可选字段不丢弃整条回复。是否完成任务由客户端按本轮必需表达判定,不采信模型的完成声明。AI 不可用时必须在对话页显示提示,说明当前这句来自内置示范脚本,不能让备用文案冒充 AI 回复。
|
||||
|
||||
## 8. 对话交互细节
|
||||
|
||||
@@ -194,7 +161,7 @@ M1 发音反馈限示范音、慢放、录音回听和可选通用练习,不
|
||||
|
||||
## 10. AI 提示词与质量要求
|
||||
|
||||
AI 系统需要传入结构化上下文:等级档、母语、场景、角色、当前任务目标(而不是脚本原句)、已收集的信息槽、可用语言清单、轮次、求助状态与安全限制。返回格式为 JSON,检查与回退按 7.3。
|
||||
AI 系统需要传入结构化上下文:用户等级、母语、场景、角色、任务目标、允许词汇/句型、轮次、用户已回答信息、求助状态与安全限制。
|
||||
|
||||
词句的掌握状态、允许的新语言数量、失败后的降级练习,以及阶段出口任务,须遵循 [学习引擎规格](LEARNING-ENGINE-SPEC.md)。
|
||||
|
||||
@@ -215,7 +182,6 @@ AI 行为要求:
|
||||
| 语音对话 | 用户可完成至少 3 轮录音回复,识别失败时仍能编辑或改用文字继续 |
|
||||
| 难度控制 | A0 每轮最多一个明确问题,可加简短回应/开场词块,总计不超过 20 个英文词,不连问姓名和地点 |
|
||||
| 目标判定 | A0 初次见面场景提取姓名、地点和问候证据;客户端检查信息槽,不只采信 AI 完成标记 |
|
||||
| 任务对齐 | 每轮 AI 回复先回应用户内容,再推进调度器选定的当前任务;不重复问已收集的信息;未通过检查时重试一次,仍失败则回退示范句并按 7.2 提示原因 |
|
||||
| 求助 | 四种求助操作不丢失当前会话状态 |
|
||||
| 总结 | 每段会话都生成一个积极反馈、一个改进句和一次立即重说机会 |
|
||||
| 复习 | 次日出现短变式或本地模板练习;技术失败不改变掌握度 |
|
||||
@@ -246,11 +212,11 @@ AI 行为要求:
|
||||
|
||||
### V1.1:改善日常使用
|
||||
|
||||
对应总产品 M2:扩展 A1 场景与自由主题对话;A1 场景来自已冻结的 A1 单元内容包(主题地图见《学习引擎规格》5.2),单元冻结前显示“待提供”。目标推荐、基础进度和同阶段变式已包含在 M1;更多发音能力必须另行验证,不预设可用。
|
||||
对应总产品 M2:扩展 A1 场景与自由主题对话。目标推荐、基础进度和同阶段变式已包含在 M1;更多发音能力必须另行验证,不预设可用。
|
||||
|
||||
### V2:扩展能力
|
||||
|
||||
增加 A2、B1 单元场景(来自单元内容包,开放任务按评分标准判定)和个性化对话计划。在你能稳定完成 V1 场景且想继续学习时再投入。
|
||||
增加 A2 场景、旅行/职场对话包、个性化对话计划。在你能稳定完成 V1 场景且想继续学习时再投入。
|
||||
|
||||
## 15. 数据与隐私
|
||||
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
# 课程内容包 JSON 格式
|
||||
|
||||
> 对应《学习引擎规格》第 5 节。本文说明 A0–B1 单元内容包的文件位置、字段和程序校验规则。所有等级共用这一套结构,由 `CourseRepository` 用同一套规则转换成学习和复习数据;A0 的内容标准仍以《A0 初始核心课程包》为准(见 4.10)。
|
||||
|
||||
## 1. 文件与工具
|
||||
|
||||
| 路径 | 内容 |
|
||||
|---|---|
|
||||
| `kouyu_english/assets/courses/course-map.json` | 主题地图:等级、单元顺序、每级的数量范围、名称白名单 |
|
||||
| `kouyu_english/assets/courses/<等级>/<单元ID>.json` | 单元内容包,一个单元一个文件 |
|
||||
| `kouyu_english/assets/courses/A0/assessment.json` | A0 阶段测评题(见 4.11) |
|
||||
| `kouyu_english/tool/courses/lexicon/` | 锁定的 CEFR-J 数据与 `SOURCES.md` |
|
||||
| `kouyu_english/tool/courses/pools/` | 等级词池(`build_word_pools.py` 生成) |
|
||||
| `kouyu_english/tool/courses/validate.dart` | 校验全部内容包,报告词池覆盖率 |
|
||||
| `kouyu_english/test/course_content_test.dart` | 在 `flutter test` 中要求校验零错误 |
|
||||
|
||||
```sh
|
||||
cd kouyu_english
|
||||
dart run tool/courses/validate.dart # 错误和覆盖率
|
||||
dart run tool/courses/validate.dart --uncovered B1 # 按主题列出未覆盖的词池词
|
||||
dart run tool/courses/validate.dart --levels B1-U14 # 打印条目的计算等级
|
||||
```
|
||||
|
||||
运行时由 `lib/core/courses/course_repository.dart` 读取:`main` 启动前先加载 A0,`AppState.load` 再加载其余等级。学习(教学段、对话、写作、独立表达)和复习(产出核心的 `match` 与 `review`)都只读这些 JSON,代码里不再写死课程数据。`test/course_self_consistency_test.dart` 要求每个示范答案都能通过它自己的判定规则。
|
||||
|
||||
## 2. 当前内容(2026-09-18,A1–B1 为 `draft`)
|
||||
|
||||
| 等级 | 单元 | 产出核心 | 理解词 | 听读材料 | 词池覆盖 |
|
||||
|---|---|---|---|---|---|
|
||||
| A0 | 10 | 见 A0 标准 | — | — | —(`approved`,不参与词池校验) |
|
||||
| A1 | 21 | 265 | 598 | 42 | 840/933(90.0%) |
|
||||
| A2 | 15 | 217 | 862 | 44 | 1040/1078(96.5%) |
|
||||
| B1 | 14 | 206 | 1121 | 42 | 1345/1552(86.7%) |
|
||||
|
||||
A1–B1 全部由 AI 起草、程序校验通过,尚未经过独立审核和用户确认,不能冻结。
|
||||
|
||||
## 3. course-map.json
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"goal": "日常生活与旅行",
|
||||
"sources": ["CEFR-J Vocabulary Profile 1.5(…)", "CEFR-J Grammar Profile 20180315(…)"],
|
||||
"minimumPoolCoverage": 0.85, // 等级词池覆盖下限
|
||||
"alwaysAllowedWords": ["oh", "ok", …], // 任何文本都可用的语气词
|
||||
"names": ["tom", "london", "mr", …], // 全局专名、缩写
|
||||
"levels": [{
|
||||
"id": "B1",
|
||||
"title": "独立应对大多数情况",
|
||||
"coreRange": [10, 15],
|
||||
"receptiveRange": [70, 110],
|
||||
"materials": {"count": [2, 3], "listeningWords": [250, 550], "readingWords": [300, 500]},
|
||||
"assessment": "A0/assessment.json", // 可选:本级阶段测评题,目前只有 A0 有
|
||||
// A1 的材料较短,且可选 "unknownTolerance"(默认 0.02)放宽超纲词占比
|
||||
"units": [{"id": "B1-U01", "file": "B1/B1-U01.json", "title": "…", "categories": ["旅行"], "canDo": "…"}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
各等级范围:
|
||||
|
||||
| 等级 | 产出核心 | 理解词 | 听读材料 | 听力词数 | 阅读词数 |
|
||||
|---|---|---|---|---|---|
|
||||
| A1 | 10–15 | 20–35 | 2 篇 | 60–120 | 80–150 |
|
||||
| A2 | 10–15 | 40–65 | 2–3 篇 | 120–280 | 150–250 |
|
||||
| B1 | 10–15 | 70–110 | 2–3 篇 | 250–550 | 300–500 |
|
||||
|
||||
A1 材料短、面向零基础,`unknownTolerance` 设为 0.08(听读材料默认 0.02):允许更多“课程尚未正式教、但零基础者需要”的常用词,前提是每个都写进 `glosses` 给出中文,问题选项用中文以免受“选项须在原文出现”的限制。
|
||||
|
||||
`units` 的顺序就是推荐学习顺序,校验“已教的词”时按此顺序累积。
|
||||
|
||||
## 4. 单元文件
|
||||
|
||||
### 4.1 ID 规则
|
||||
|
||||
所有 ID 都以单元 ID 开头,序号从 1 连续编号:
|
||||
|
||||
| 前缀 | 对象 | 例子 |
|
||||
|---|---|---|
|
||||
| `W` | 产出核心:单词 | `B1-U14-W01` |
|
||||
| `P` | 产出核心:词块或句型 | `B1-U14-P01` |
|
||||
| `R` | 理解词 | `B1-U14-R01` |
|
||||
| `S` | 教学段 | `B1-U14-S1` |
|
||||
| `C` | 场景 | `B1-U14-C1` |
|
||||
| `T` | 任务 | `B1-U14-T1` |
|
||||
| `M` | 听读材料 | `B1-U14-M1` |
|
||||
|
||||
### 4.2 顶层字段
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "B1-U14",
|
||||
"revision": 1,
|
||||
"level": "B1",
|
||||
"title": "写说明与投诉", // title/categories/canDo 必须与 course-map 一致
|
||||
"categories": ["日常", "旅行"],
|
||||
"canDo": "写邮件说明经过并提出要求",
|
||||
"status": "draft", // draft / reviewed / frozen
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": ["CEFR-J Vocabulary Profile 1.5", "CEFR-J Grammar Profile 20180315"],
|
||||
"reviews": [], // 审核记录
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": ["seaview", "palace"], // 本单元专名,小写
|
||||
"grammar": [{"id": "142", "use": "active"}, {"id": "215", "use": "receptive", "reason": "…"}],
|
||||
"coreItems": […], "receptiveWords": […], "segments": […], "scenes": […], "tasks": […],
|
||||
"materials": […], // A1 起(A1 为短材料)
|
||||
"rubric": {"dimensions": […]}, // A2 起(A1 无开放任务评分标准)
|
||||
"excluded": [{"en": "…", "reason": "…"}]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 coreItems(产出核心)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "B1-U14-P08",
|
||||
"type": "phrase",
|
||||
"en": "I would like to request a full refund.",
|
||||
"zh": "我想申请全额退款。",
|
||||
"sceneWord": false,
|
||||
"match": [["request a full refund", "request a refund"]],
|
||||
"example": {"en": "I would like to request a refund for the second night.", "zh": "我想申请退还第二晚的费用。"},
|
||||
"review": {"prompt": "在邮件里提出退款要求。"}
|
||||
}
|
||||
```
|
||||
|
||||
- `type`:`word` / `phrase` / `pattern`。
|
||||
- `match`:判定用户是否用到本项。外层数组之间为“且”,内层为“或”;每个词条按连续短语匹配(缩写展开、忽略大小写和标点)。`en` 和 `example.en` 都必须满足。
|
||||
- `sceneWord`:高于本等级或 CEFR-J 未收录、但场景必需的词句设为 `true`,并写 `sceneReason`;每单元不超过产出核心的 1/3。
|
||||
- `review.prompt`:复习时的中文任务,不能直接包含英文答案。
|
||||
|
||||
### 4.4 receptiveWords(理解词)
|
||||
|
||||
```json
|
||||
{"id": "B1-U14-R01", "en": "furthermore", "zh": "此外"}
|
||||
```
|
||||
|
||||
- 不高于本等级加一级(B1 单元最高 B2)。
|
||||
- 不能是 A0 或之前单元已教的词(按词形还原比较)。
|
||||
- 必须出现在本单元的教学段、场景或听读材料文本中。
|
||||
|
||||
### 4.5 segments(教学段)
|
||||
|
||||
每单元 2–4 段,每段 8–15 分钟、最多 6 个新产出核心:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"id": "B1-U14-S1", "title": "说明写信目的", "minutes": 12,
|
||||
"itemIds": ["B1-U14-P01", "B1-U14-P02"],
|
||||
"listening": {"text": "…", "question": "…", "options": ["正确", "干扰", "干扰"]},
|
||||
"speaking": [{"text": "…", "tip": "可选的发音提示"}],
|
||||
"reading": {"text": "多行用 \n", "question": "…", "options": ["…", "…", "…"]},
|
||||
"writing": {"prompt": "…", "example": "…", "itemIds": ["B1-U14-P01"]},
|
||||
"independent": {"prompt": "…", "itemIds": ["B1-U14-P02"]}
|
||||
}
|
||||
```
|
||||
|
||||
- 选项恰好 3 个,第一个是正确答案(界面随机排序);选项不能互为子串。英文选项至少两个在原文出现,且正确选项必须出现。
|
||||
- `writing.example` 必须用到它引用的每个产出核心。
|
||||
|
||||
### 4.6 scenes 与 tasks
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"id": "B1-U14-C1", "kind": "main", // 恰好 1 个 main,另有 2–3 个 variant
|
||||
"title": "…", "learnerRole": "客人", "aiRole": "酒店客服经理", "setting": "…",
|
||||
"turns": [ // 4–7 轮
|
||||
{"ai": "AI 说的话", "goal": "本轮目标", "model": "学习者示范回答", "zh": "示范的中文", "itemIds": ["B1-U14-P01"]}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{"id": "B1-U14-T1", "sceneId": "B1-U14-C1", "goal": "…", "itemIds": ["B1-U14-P01"], "slots": ["目的", "经过", "结果", "要求"]}
|
||||
```
|
||||
|
||||
- `model` 必须用到本轮 `itemIds` 引用的产出核心。
|
||||
- 每个场景至少有一个任务,每个任务至少 1 个信息槽。
|
||||
- 每个产出核心都要出现在教学段、场景示范和任务中。
|
||||
|
||||
### 4.7 materials(A1 用短材料,A2、B1 更长)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"id": "B1-U14-M1", "mode": "reading", "title": "投诉邮件和回复",
|
||||
"text": "…",
|
||||
"glosses": {"balcony": "阳台"},
|
||||
"questions": [{"type": "gist", "question": "…", "options": ["正确", "干扰", "干扰"]}] // 2–4 道,gist/detail/inference
|
||||
}
|
||||
```
|
||||
|
||||
- 每单元听和读都要有。
|
||||
- 超纲词(见第 5 节)按词次不超过该级 `materials.unknownTolerance`(默认 2%,A1 为 8%),每个超纲词都要写进 `glosses`。
|
||||
- A1 材料应尽量把本单元的理解词织进听读文本,让理解词在教学段之外再复现一次(本级平均单元内复现率约 77%);本单元的产出核心与理解词都算已教词,写进材料不计超纲。
|
||||
|
||||
### 4.8 rubric(A2 起)
|
||||
|
||||
```json
|
||||
{"dimensions": [{"name": "说明经过", "bands": ["经过说不清", "能按顺序说明经过,但缺少结果", "按顺序说明经过、结果,并附上证据"], "pass": 1}]}
|
||||
```
|
||||
|
||||
至少 2 个维度,每维至少 3 档;`pass` 是及格档在 `bands` 中的下标(从 0 起),须大于 0。
|
||||
|
||||
### 4.9 可选字段:显式规则优先
|
||||
|
||||
以下字段都可省略;省略时由 `itemIds` 引用的产出核心推导(`match` 合并成一组“或”,示范取 `en`),写了就以写的为准。规则写法见 `lib/core/courses/answer_rules.dart`:外层“且”、内层“或”,内层首项可写 `#min:N` 表示至少用上 N 项;词条可写 `a + b`、`/正则/`,以及 `#spelling`、`#digit`、`#word`、`#question`、`#numbers:N`、`#words:N` 这些结构。
|
||||
|
||||
| 位置 | 字段 | 作用 | 省略时 |
|
||||
|---|---|---|---|
|
||||
| 单元 | `status` | `approved` / `draft` 等 | `draft` |
|
||||
| 单元 | `vocabulary` | 整课词表(离线查词) | 产出核心 + 理解词 |
|
||||
| 教学段 | `grammarNote` | 本段语法提示 | 不显示 |
|
||||
| 教学段 | `vocabulary` | 预习词表 `[{id, word, meaning, example, exampleMeaning}]` | 本段产出核心 |
|
||||
| 教学段 | `sceneId` | 本段对话用的场景 | 第 N 段用第 N 个场景,没有则用 main 场景 |
|
||||
| 教学段 | `writing.accept` / `writing.hint` | 写作判定规则 / 未通过时的提示 | 由 `writing.itemIds` 推导 |
|
||||
| 教学段 | `independent.accept` / `independent.help` | 独立表达的完整规则 / 帮助 | 由 `independent.itemIds` 推导;为空则任何真实尝试都算 |
|
||||
| 场景 | `goal` | 对话目标 | `setting`,再没有则 `title` |
|
||||
| 场景 | `practice` | 同时作为自由练习场景:`{title, summary, recapPrompt, alwaysOpen}`,都可省略;写 `{}` 即可 | 不进入自由练习 |
|
||||
| 轮次 | `aiZh` | `ai` 这句的中文(`zh` 是示范回答的中文) | 不提供翻译 |
|
||||
| 轮次 | `accept` | 本轮判定用的一组“或” | 由本轮 `itemIds` 推导 |
|
||||
|
||||
自由练习场景在其所属单元完成后解锁,`alwaysOpen: true` 则一开始就开放。
|
||||
|
||||
### 4.10 A0 单元
|
||||
|
||||
A0 用同一结构,文件为 `A0/a0-01.json` … `a0-10.json`,在 `course-map.json` 中排在最前。为兼容已保存的学习进度,A0 保留原有 ID:单元 `a0-01`,教学段 `a0-01-a`(拆段课为 `a0-04-a/b/c`、`a0-08-a/b/c`),产出核心 `A0-W01` / `A0-P01`。A0 的规则、提示和对话都用上面的显式字段写明;`validate.dart` 只把 A0 的产出核心和听写句当作“已教的词”,不按 A1–B1 的数量范围和词池校验 A0。
|
||||
|
||||
### 4.11 阶段测评(assessment.json)
|
||||
|
||||
等级在 `course-map.json` 里写了 `assessment`,就由 `CourseRepository` 加载对应文件,运行时代码在 `lib/core/courses/assessment.dart`。目前只有 A0 的 `A0/assessment.json`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"packs": [
|
||||
{"id": "A0-E1", "level": "A0", "tasks": [
|
||||
{"id": "A0-A-L1", "skill": "listening", "prompt": "选择正确答案",
|
||||
"audio": "Hello. I’m Mia.", "choices": ["自我介绍", "买东西", "问时间"], "answerIndex": 0},
|
||||
{"id": "A0-A-W1", "skill": "writing", "prompt": "用英语介绍自己。",
|
||||
"accept": [["hello", "hi"], ["i am", "my name is"]],
|
||||
"guide": "试着同时写一句问候和姓名,例如:Hello. I’m …"}
|
||||
]},
|
||||
{"id": "A0-E1R", "level": "A0", "replaces": "A0-E1", "tasks": […]}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `skill` 取 `listening` / `reading` / `writing` / `speaking`。听力、阅读是选择题,写 `choices` 和 `answerIndex`(从 0 开始);听力另写 `audio`。
|
||||
- 写作、口语是开放题,`accept` 用 4.9 的规则写法,`guide` 是答错后展示的要求。
|
||||
- 没有 `replaces` 的套题按文件顺序解锁:第一套直接开放,之后每套要在上一套通过 24 小时后才开放。
|
||||
- 有 `replaces` 的是替换题,用不同的人名、地点、数字考同样的能力,成绩记在原题 ID 上。
|
||||
- 题目和套题 ID 写进学习记录,发布后不要改。
|
||||
|
||||
`test/course_self_consistency_test.dart` 检查:每道开放题都有 `accept` 和 `guide`,每道选择题的 `answerIndex` 有效,`replaces` 指向存在的套题。
|
||||
|
||||
## 5. 文本用词检查
|
||||
|
||||
“已知词”包括:`alwaysAllowedWords`、功能词、A0 已教的词、推荐顺序在前的单元的产出核心和理解词、本单元的产出核心和理解词(均按词形还原),以及全局与本单元的 `names`。数字不计。
|
||||
|
||||
| 文本 | 允许的未知词 |
|
||||
|---|---|
|
||||
| 教学段的听、说、读、写示范,场景的 AI 句和示范句 | 0 |
|
||||
| 听读材料 | ≤ `materials.unknownTolerance`(默认 2%,A1 为 8%),且全部有 `glosses` |
|
||||
|
||||
注意:等级低于本单元、但还没有被任何单元教过的词(如 `history`、`culture`)同样算未知词。要在文本中使用,须改写,或把它加为本单元的理解词。
|
||||
|
||||
## 6. 等级与覆盖
|
||||
|
||||
- 语法:`active` 语法项不高于本等级,否则写 `reason`。
|
||||
- 产出核心中的词不高于本等级,除非是场景词。
|
||||
- 覆盖率:等级词池(`tool/courses/pools/`)中,被产出核心、`match` 词块或理解词覆盖的词(含 A0 已教)占比须 ≥ `minimumPoolCoverage`。
|
||||
|
||||
## 7. 状态流转
|
||||
|
||||
`draft`(AI 起草、校验通过)→ `reviewed`(独立审核后,在 `meta.reviews` 记录审核模型、日期和问题清单)→ `frozen`(用户确认,填 `frozenAt`)。已冻结单元的 ID 不复用;修改须提升 `revision` 并遵循《学习引擎规格》5.5 的 stageVersion 规则。
|
||||
+33
-274
@@ -1,42 +1,35 @@
|
||||
# 学习引擎规格:每日内容、掌握判定与阶段升级
|
||||
|
||||
> 适用范围:个人使用的英语学习工具
|
||||
> 原则:学习目标固定为日常生活与旅行,主线终点为 B1;课程主线由主题地图和冻结的单元内容包决定并保持稳定。目标决定学哪些场景,分级数据决定难度上限,AI 决定场景怎么演,程序决定怎样算完成。AI 只在明确边界内生成变式练习与对话。
|
||||
> 原则:课程主线保持稳定;AI 只在明确边界内生成变式练习与对话。
|
||||
|
||||
M1 范围、生成与数据契约见 [开发与验收约定](M1-IMPLEMENTATION-CONTRACT.md);A0 核心清单和四技能评估见 [A0 阶段标准](A0-STAGE-STANDARD.md)。以下规则是个人学习初始规则,不是 CEFR 认证标准。
|
||||
|
||||
## 1. 学习体系的边界
|
||||
|
||||
学习目标固定为**日常生活与旅行**,不提供目标选择。主线终点是 B1:CEFR 对 B1 的描述正是能应对在英语地区旅行时遇到的大多数情况,并能谈论熟悉的日常话题。B2 作为可选扩展,B1 通过后再规划。
|
||||
|
||||
不安排学校与学术场景,这类词汇不进入词池(5.4)。工作需求后续作为独立主题线增加:届时在主题地图中新增工作单元,使用 5.4 中预留的工作类词汇,不改变日常与旅行主线的单元和门槛;是否提供目标选择届时再定。
|
||||
|
||||
学习路径没有预设的总课数。A0 的 10 节核心课和第 5 节主题地图中的单元是主线内容;系统会根据掌握状态持续提供复习、变式任务和补救微课,直到达到阶段出口能力。它们不是“掌握英语”的终点。
|
||||
学习路径没有预设的总课数。当前 A0 的 10 节核心课和后续生活主题只是起始内容库;系统会根据掌握状态持续提供复习、变式任务和补救微课,直到达到阶段出口能力。它们不是“掌握英语”的终点。
|
||||
|
||||
阶段推进不取决于“学完多少课”。核心课用于建立能力骨架;当一个阶段的出口任务、词句掌握度和真实对话表现尚未达标时,系统继续生成符合该阶段范围的新情境与练习,而不是机械跳到下一阶段。
|
||||
|
||||
| 阶段 | 能力目标 | 主要学习形式 |
|
||||
|---|---|---|
|
||||
| A0 | 问候、自我介绍、数字、身边物品、基础短句 | 短课、模仿、受控对话 |
|
||||
| A1 | 点餐、购物、问路、交通、入住、看病等固定的生活与旅行互动 | 主题单元、任务型 AI 对话 |
|
||||
| A2 | 讲经历、做预订与改期、比较选择、处理行程和住宿中的小问题 | 主题单元、较长对话、分级听读 |
|
||||
| B1 | 独立应对旅行和生活中的大多数情况:突发问题、投诉协商、说明理由、讲完整经历 | 主题单元、较长听读材料、叙事、讨论、写作 |
|
||||
| B2(可选) | 自然语速的生活与旅行内容、复杂协商和讨论 | B1 通过后再规划 |
|
||||
| A1 | 点餐、购物、问路、入住、预约等固定生活任务 | 场景课程、任务型 AI 对话 |
|
||||
| A2 | 讲日常、过去经历、计划、比较、建议与简单问题解决 | 更长对话、短文本、变式任务 |
|
||||
| B1 | 独立处理旅行、生活及一般工作交流;表达理由与经历 | 真实慢速输入、叙事、讨论、写作 |
|
||||
| B2 | 较自然处理复杂讨论、长内容和工作沟通 | 自然语速输入、观点辩护、专业主题 |
|
||||
|
||||
本工具说的“学会英语”,指达到 B1 的日常与旅行交流能力,而不是背完一套课。从 A2 起须逐步加大听读输入(第 6 节)。
|
||||
|
||||
**时间预期**:剑桥英语给出的累计指导学时参考为 A2 约 180–200 小时、B1 约 350–400 小时、B2 约 500–600 小时。每天 20 分钟约合每年 120 小时,大致对应 A2 约 1.5 年、B1 约 3 年;30 分钟模式约 2 年到 B1。这只用于设定预期,不作为进度要求或解锁条件。
|
||||
“掌握英语”应被理解为逐步达成 B1/B2 的真实交流能力,而不是背完一套课。到 B1/B2 必须加入大量真实音频、视频、阅读、自由表达与阶段性任务测评。
|
||||
|
||||
## 2. 每日内容生成引擎
|
||||
|
||||
每天不由 AI 从零随机生成一整课。系统先从固定的能力地图(主题地图与已冻结的单元内容包,见第 5 节)中选内容,再让 AI 生成符合当前水平的例句、对话变式和纠错。
|
||||
每天不由 AI 从零随机生成一整课。系统先从固定的能力地图中选内容,再让 AI 生成符合当前水平的例句、对话变式和纠错。
|
||||
|
||||
### 2.1 每日组成
|
||||
|
||||
```text
|
||||
到期复习(按时间选任务)
|
||||
→ 教学段:词汇预热 → 带帮助的听说读写 → 受控对话 → 独立尝试
|
||||
→ (A2 起)听读输入
|
||||
→ 总结与保存;未完成步骤留待续学
|
||||
```
|
||||
|
||||
@@ -46,7 +39,6 @@ M1 范围、生成与数据契约见 [开发与验收约定](M1-IMPLEMENTATION-C
|
||||
| 其中:词汇预热 | 新词与准备词块 | 认识声音和当前句义,之后在句中应用,不再重复整套词卡 | 占教学段 1–3 分钟 |
|
||||
| 其中:受控对话与独立尝试 | 已教语言 | 通常 3–5 个回答轮;结尾一轮可作为独立尝试 | 占教学段约 3–5 分钟 |
|
||||
| 到期复习与补救 | 到期词句和薄弱技能 | 合并同一目标的复习与错误修复,避免重复安排 | 按每日预算 |
|
||||
| 听读输入(A2 起) | 当前单元的分级材料 | 积累可理解输入 | 从教学额度中划出:A2 约 20%,B1 约 35%(第 6 节) |
|
||||
|
||||
每日时间是安排上限,不保证每次完成整段。10 分钟:复习/补救 3 分钟、教学或续学 6 分钟、总结 1 分钟;20 分钟:5 + 14 + 1;30 分钟:8 + 20 + 2。30 分钟教学额度可用于慢练或可选巩固,不强制再学新段。无到期项时可将空余时间用于教学或提前结束。预热、课程内对话均包含在教学额度中,不另外叠加。只做复习的当天,独立输出可嵌入复习任务。
|
||||
|
||||
@@ -80,7 +72,7 @@ M1 范围、生成与数据契约见 [开发与验收约定](M1-IMPLEMENTATION-C
|
||||
|
||||
```text
|
||||
当前阶段:A0 / A1 / A2 / B1 / B2
|
||||
当前单元与任务:单元 ID、场景 ID、任务 ID、任务目标(例如“在咖啡店点饮料”)及任务引用的核心词句 ID
|
||||
当前主题与任务目标:例如“在咖啡店点饮料”
|
||||
用户身份与 AI 身份
|
||||
已掌握的词、句型
|
||||
本教学段新词(最多 8 项)及主动句型(最多 2 项)
|
||||
@@ -103,7 +95,7 @@ AI 对话中的词条必须来自当前允许语言范围,或是事先标为
|
||||
|
||||
## 3. 词汇与句型掌握模型
|
||||
|
||||
词汇不以“看过一次”或“答对一次”为掌握。词汇分为“会用”的产出核心和“会认”的理解词两层(见 3.5);本节五种状态与掌握规则适用于产出核心,每个词和关键句型均经历五种状态:
|
||||
词汇不以“看过一次”或“答对一次”为掌握。每个词和关键句型均经历五种状态:
|
||||
|
||||
| 状态 | 证据 | 系统动作 |
|
||||
|---|---|---|
|
||||
@@ -171,50 +163,9 @@ AI 对话中的词条必须来自当前允许语言范围,或是事先标为
|
||||
|
||||
一次反馈只处理一个点。`three/free` 可作为通用听辨、示范和回听练习,但界面须说明“识别可能有误,这不是发音诊断”。文字 AI 没有音频证据时不能断言用户把 /θ/ 发成 /f/;个性化音素诊断不在 M1。未编辑且确认的转写仅是受控口语的代理证据,不等于真人可理解度保证。
|
||||
|
||||
### 3.5 两层词汇:会用与会认
|
||||
|
||||
| 层 | 范围 | 状态 | 是否计入升级分母 |
|
||||
|---|---|---|---|
|
||||
| 产出核心(会用) | 单元内容包中需要主动说、写的词、词块和句型;每单元 10–15 项 | 3.1–3.2 的五种状态和掌握规则 | 计入 |
|
||||
| 理解词(会认) | 听懂、看懂即可的词;每单元 A1 20–35 项、A2 30–50 项、B1 50–80 项 | 新学 → 认识 → 熟悉 | 不逐项计入;升级时抽查(4.2) |
|
||||
| 扩展词 | 听读材料中查过或多次出现、由用户加入复习的词 | 同理解词 | 不计入,也不参与升级抽查 |
|
||||
|
||||
**学习流程**:单词不单独背,跟着单元场景学。
|
||||
|
||||
1. 会用的词:预热(只记“见过”)→ 在场景句中听辨 → 说、读、写的替换练习 → 受控对话中用出来 → 独立尝试 → 按 3.1 间隔复习,每次复习换语境(如从地铁站换到火车站)。
|
||||
2. 会认的词:不进预热,直接出现在教学段的听读材料里,首次出现可点开释义;教学段结束时做 2–3 道快速识别题,之后按下述理解词规则复习。
|
||||
3. 各等级侧重:A1 以具体名词和固定说法为主,用图片和替换练习;A2 增加词块、搭配和词形变化(如 `book a table`、`miss the bus`、过去式);B1 的新词主要来自听读材料和查词,增加词族和近义词辨析(如 `complain`/`complaint`),并练习用英语解释词义。
|
||||
|
||||
**识别题要求**(两层词的“认识”证据,以及理解词和扩展词的全部证据):
|
||||
|
||||
- 至少一次在句子或场景语境中答对;孤立的中英互译不能作为唯一证据。
|
||||
- 干扰项取同单元或意思相近的词(如 `ticket` 配 `receipt`、`menu`),不用明显无关的词。
|
||||
- 提供“不认识”选项:选它不算语言失败,转为教学并换题补测。
|
||||
- 记录作答用时:听辨题超过约 10 秒、阅读识别题超过约 15 秒,记为带帮助完成,不算独立成功。
|
||||
- 理解词只考“听或读 → 意思”,不考拼写,也不要求说出。
|
||||
|
||||
理解词规则:
|
||||
|
||||
- 一次独立的听辨或阅读识别成功进入“认识”;至少间隔 7 天后,换题、换语境再次独立识别成功,进入“熟悉”,之后每 30 天抽查。
|
||||
- 识别失败回到“认识”,次日换题复核;不做诊断降级。
|
||||
- 复习以 5–10 秒的快速识别题为主,可以成组出现,占复习预算不超过三分之一。
|
||||
- 在听读材料中答对与某个理解词直接相关的理解题,可计一次该词的识别证据。
|
||||
- 后续单元把某个理解词列为产出核心时,保留已有识别证据,从“认识”开始走完整掌握流程。
|
||||
|
||||
A0 的准备/辅助材料沿用《A0 阶段标准》,不套用本节。
|
||||
|
||||
### 3.6 免学与定级
|
||||
|
||||
已经会的内容不必重学,但“会”需要证据:
|
||||
|
||||
- **单元预检**:进入单元时可选做一次不超过 3 分钟的预检,在单元场景里考听辨和独立表达(说或写)。某项产出核心两类题都独立答对,记为“免学待确认”,教学段中跳过其讲解;至少 3 天后换题独立复测一次,成功即记为已掌握并进入 30 天抽查,失败则按新项正常教学。理解词预检识别正确直接记为“熟悉”。预检答错不计语言失败。
|
||||
- **入门定级**:“完全零基础”从 A0 开始。“有一些基础”或“能简单对话”可做定级评估:依次使用各等级已冻结的出口题组中的一套(先 A0,通过后再 A1……),在第一个未通过的等级停止,从该等级开始学习。某等级题组尚未冻结时,定级最多只到该等级。
|
||||
- **定级通过**:定级跳过的等级记为“定级通过”,与“阶段通过”分开保存;14 天内再通过该等级的另一套题组才算确认。未确认时首页建议回到该等级,但不强制。
|
||||
- **定级免学**:跳过等级的产出核心不走四个检查点,每天从复习预算中最多抽 5 项独立复测;失败项转为需复核并安排补救。跳过的等级不参与后续等级的门槛计算。
|
||||
|
||||
## 4. 阶段升级规则
|
||||
|
||||
已完成课数只用于回顾学习历史,不能决定升级。可以提前参加评估或延后升级,但只有门槛全部满足才记录阶段通过,不能手动跳级。入门轻量定位只推荐起点,定级跳过等级须按 3.6 用出口题组证明。下一等级尚无已冻结的单元时显示待提供,不在运行时临时生成主线;单元内容包按第 5 节离线生成、校验、审核并经用户确认冻结后才可学习。
|
||||
已完成课数只用于回顾学习历史,不能决定升级。可以提前参加评估或延后升级,但只有门槛全部满足才记录阶段通过,不能手动跳级。入门轻量定位只推荐当前阶段内的教学起点,不替代出口评估。下一阶段内容未就绪时显示待提供,不自动生成未定义的主线。
|
||||
|
||||
### 4.1 A0 → A1
|
||||
|
||||
@@ -222,231 +173,39 @@ A0 的准备/辅助材料沿用《A0 阶段标准》,不套用本节。
|
||||
|
||||
初次见面对话只是口语评估的一部分,还须验证姓名拼读、数字、物品、家人、星期/整点及沟通补救;阅读和写作单独评估。带提示、修改转写或识别失败的处理按标准执行。未通过则定向补练与换题复测,不限制追加课程数量。
|
||||
|
||||
### 4.2 A1 及以上的升级门槛
|
||||
### 4.2 A1 → A2
|
||||
|
||||
门槛只设在等级之间,单元之间不设门槛(见 5.1)。从 A1 起,进入下一等级须同时满足:
|
||||
以下为 M2 的能力方向,尚不是可直接启用的 A1 自动升级规则。启用 A1 前须另行冻结核心清单、四技能题组和阈值;未冻结前禁用 A1→A2 自动升级:
|
||||
|
||||
1. 本等级产出核心(全部单元之和,等级 stageVersion 冻结的分母)中至少 80% 为可使用或已掌握,其中至少 50% 为已掌握;需复核项的处理同《A0 阶段标准》;
|
||||
2. 理解词抽查:从本等级已教的理解词中随机抽 30 项做听或读的识别,独立答对至少 24 项;
|
||||
3. 两套不同题组的四技能评估分别通过,间隔至少 24 小时,最近一次在 30 天内;
|
||||
4. 完成本等级的出口任务(4.3)。
|
||||
- 能在点餐、购物、问路、交通、酒店等 8–10 个场景中完成目标;
|
||||
- 能听懂慢速日常对话的主要信息;
|
||||
- 能写 4–6 句连贯的短消息;
|
||||
- 能完成约 3 分钟任务型 AI 对话;
|
||||
- 能表达一个过去经历和一个简单计划;
|
||||
- A1 的核心词句门槛以未来有版本的 A1 标准为准,不复用未定义分母的“约 80%”。
|
||||
|
||||
本等级的单元未全部冻结前,只能学习已冻结的单元,升级评估不可用。比例由程序固定,内容包不能修改。
|
||||
**出口任务**:完成一次旅行日:入住酒店、询问路线、点餐并处理一个简单问题。
|
||||
|
||||
### 4.3 出口任务
|
||||
### 4.3 A2 → B1、B1 → B2
|
||||
|
||||
| 升级 | 出口能力 | 出口任务 |
|
||||
| 升级 | 出口能力 | 建议任务 |
|
||||
|---|---|---|
|
||||
| A1 → A2 | 在点餐、购物、问路、交通、住宿、看病等场景中完成固定互动;能写 3–5 句短消息 | **旅行日**:入住酒店、询问路线、点餐并处理一个简单问题(约 3 分钟对话);写一条告诉朋友当天安排的消息 |
|
||||
| A2 → B1 | 讲经历,做预订和改期,比较选择,处理行程和住宿中的小问题 | **行程变化日**:电话改订并当面解决一个住宿问题(约 5 分钟对话);听 1–2 分钟通知后说出要点;写 4–6 句消息讲当天发生了什么 |
|
||||
| B1 结业(B2 可选) | 独立应对旅行和生活中的大多数情况,说明经过和理由 | 听 2–3 分钟广播或导览后复述;5 分钟旅途突发问题的解决与协商对话;写一封说明问题并提出要求的邮件 |
|
||||
| A2 → B1 | 能讲经历、说明理由、处理多数日常突发情况 | 听 2–3 分钟慢速素材后复述;完成 5 分钟旅行问题解决对话;写一封短邮件 |
|
||||
| B1 → B2 | 能理解自然语速主要内容、清楚支持观点、处理工作沟通 | 阅读/收听真实材料后总结和讨论;完成 10 分钟工作情境模拟;写结构化说明或意见 |
|
||||
|
||||
A2 起的出口任务按 5.8 判定;A2 → B1 与 B1 结业须人工或通过高质量任务评估复核,不应只靠选择题自动升级。
|
||||
这两个阶段必须人工或通过高质量任务评估复核,不应只靠选择题自动升级。
|
||||
|
||||
## 5. 学习路线与单元内容包
|
||||
## 5. 长期内容扩展
|
||||
|
||||
**目标决定学哪些场景,分级数据决定难度上限,AI 决定场景怎么演,程序决定怎样算完成。** 学习目标固定为日常生活与旅行;主题地图(5.2)列出每个等级要完成的生活和旅行任务;AI 从场景出发起草单元内容,再用分级数据检查难度;程序校验、编译判定条件并执行门槛。单元冻结后,运行时 AI 不能再改变它。
|
||||
| 目标 | 新增内容 | 不可缺少的练习 |
|
||||
|---|---|---|
|
||||
| A2 | 过去时、将来计划、比较、建议、情绪、关系、问题解决 | 1–3 分钟叙述、短消息、生活变式对话 |
|
||||
| B1 | 旅行问题、工作日常、观点与理由、故事复述 | 慢速真实音频/视频、段落写作、5 分钟讨论 |
|
||||
| B2 | 复杂观点、会议、协商、跨文化表达、专业主题 | 自然语速材料、总结、辩护观点、10 分钟任务模拟 |
|
||||
|
||||
### 5.1 路线结构
|
||||
系统要成为从零到 B1/B2 的骨架,必须逐阶段增加“可理解输入”和“开放输出”的比例:A0/A1 以受控短句为主;A2 开始加入短文本和变式表达;B1/B2 以真实材料、观点表达、长对话和写作为主。
|
||||
|
||||
```text
|
||||
等级(A0 / A1 / A2 / B1)
|
||||
→ 主题单元(一组相关的生活或旅行任务)
|
||||
→ 教学段(沿用第 2 节,每段约 10–15 分钟)
|
||||
→ 场景与任务(单元主场景 + 2–3 个变式场景)
|
||||
```
|
||||
|
||||
- **单元是冻结和学习的基本单位**:某个单元冻结后即可学习,不必等整个等级。
|
||||
- **单元之间不设门槛**:完成单元的教学段并尝试过单元任务后,即可进入下一单元;掌握在复习中继续推进。积压时按 2.2a 暂停推荐新单元。
|
||||
- **推荐顺序按依赖排列**(如价格先于购物、日期先于预订、问路先于交通)。用户可以自选单元;跳到后面的单元时,前面单元中被引用的词句作为预热新项先教。
|
||||
- **单元规模**:产出核心 10–15 项(词、词块、句型);理解词 A1 20–35 项、A2 40–65 项、B1 70–110 项(两层词汇见 3.5)。三个等级的数量都已按词池和起草结果调整(5.4),以 `course-map.json` 为准。
|
||||
|
||||
### 5.2 主题地图
|
||||
|
||||
A0 保持现有 10 节核心课(见《A0 初始核心课程包》)。A1 起的单元如下,机器可读版本是 `kouyu_english/assets/courses/course-map.json`,单元内容包格式见《课程内容包 JSON 格式》(COURSE-PACK-JSON.md)。A1 按词池试建结果补了“家人与住处”“描述人和感受”,把“开口求助与数字”放在最前,并按依赖调整了顺序(如问路先于打车、公交)。“类别”表示该单元主要服务日常还是旅行。修改主题地图(增删、合并或调整单元)须用户确认;已冻结单元的 ID 不复用。
|
||||
|
||||
**A1:固定的生活与旅行互动**
|
||||
|
||||
| 单元 | 主题 | 类别 | 单元结束后能做什么 |
|
||||
|---|---|---|---|
|
||||
| A1-U01 | 开口求助与数字 | 旅行/日常 | 没听懂时礼貌求助,说 11–100 的数字,拼读字母 |
|
||||
| A1-U02 | 价格与付款 | 日常/旅行 | 问价、说 100 以内的金额、选择现金或刷卡 |
|
||||
| A1-U03 | 在咖啡店点单 | 日常 | 点饮料、说大小杯、礼貌确认 |
|
||||
| A1-U04 | 问路 | 旅行 | 问地点、听懂左右和距离 |
|
||||
| A1-U05 | 在餐厅点餐 | 日常/旅行 | 点餐、问推荐、要账单 |
|
||||
| A1-U06 | 家人与住处 | 日常 | 介绍家人,说住在哪里和家里有什么 |
|
||||
| A1-U07 | 公交与地铁 | 旅行 | 买票、问站点、确认下车 |
|
||||
| A1-U08 | 超市与便利店 | 日常 | 找商品、说数量 |
|
||||
| A1-U09 | 描述人和感受 | 日常 | 说一个人的外貌和性格,说自己的心情 |
|
||||
| A1-U10 | 打车 | 旅行 | 说目的地、确认价格或路线 |
|
||||
| A1-U11 | 我的一天 | 日常 | 说作息和常做的事 |
|
||||
| A1-U12 | 买衣服 | 日常 | 问尺码和颜色、试穿 |
|
||||
| A1-U13 | 日期与约时间 | 日常 | 说日期、约见面、改时间 |
|
||||
| A1-U14 | 在机场 | 旅行 | 值机、问登机口、回答入境的简单问题 |
|
||||
| A1-U15 | 入住酒店 | 旅行 | 说明预订信息、办理入住 |
|
||||
| A1-U16 | 天气与穿着 | 日常/旅行 | 问天气、说该穿什么 |
|
||||
| A1-U17 | 酒店里的需求 | 旅行 | 要物品、报告简单问题 |
|
||||
| A1-U18 | 药店与不舒服 | 日常/旅行 | 说一个症状、听懂基本建议 |
|
||||
| A1-U19 | 遇到麻烦时求助 | 日常/旅行 | 说明遇到的麻烦、请求帮忙、感谢与回应 |
|
||||
| A1-U20 | 打电话与留言 | 日常 | 留下姓名、号码和简短信息 |
|
||||
| A1-U21 | 周末计划 | 日常 | 说将要做的事、邀请朋友 |
|
||||
|
||||
**A2:讲经历、做安排、处理小问题**
|
||||
|
||||
| 单元 | 主题 | 类别 | 单元结束后能做什么 |
|
||||
|---|---|---|---|
|
||||
| A2-U01 | 上个周末 | 日常 | 用过去时讲做了什么 |
|
||||
| A2-U02 | 上一次旅行 | 旅行 | 讲去了哪里、怎么样、感受如何 |
|
||||
| A2-U03 | 预订酒店与餐厅 | 旅行 | 电话或当面预订:日期、人数、要求 |
|
||||
| A2-U04 | 改期与取消 | 日常/旅行 | 说明原因、提出新时间 |
|
||||
| A2-U05 | 餐厅里的问题 | 旅行 | 说明忌口或过敏、菜品有问题、分开结账 |
|
||||
| A2-U06 | 比较与选择 | 日常/旅行 | 比较商品、房间或路线并做选择 |
|
||||
| A2-U07 | 退换货 | 日常 | 说明问题、要求退换 |
|
||||
| A2-U08 | 行程出问题 | 旅行 | 误车、延误、换乘、改票 |
|
||||
| A2-U09 | 住宿里的问题 | 旅行 | 报修、换房、提出要求 |
|
||||
| A2-U10 | 看病 | 日常/旅行 | 说症状和持续时间、听懂用药说明 |
|
||||
| A2-U11 | 聊兴趣与家乡 | 日常 | 围绕一个话题连续聊几轮 |
|
||||
| A2-U12 | 建议与意见 | 日常/旅行 | 给建议、征求意见 |
|
||||
| A2-U13 | 介绍城市与景点 | 旅行 | 描述地方、推荐去处 |
|
||||
| A2-U14 | 丢东西与求助 | 旅行 | 在服务台说明丢了什么、在哪里丢的 |
|
||||
| A2-U15 | 消息与短邮件 | 日常 | 写邀请、感谢、说明迟到 |
|
||||
|
||||
**B1:独立应对大多数情况**
|
||||
|
||||
| 单元 | 主题 | 类别 | 单元结束后能做什么 |
|
||||
|---|---|---|---|
|
||||
| B1-U01 | 讲一段完整的旅行经历 | 旅行 | 按顺序叙述,加入背景和感受 |
|
||||
| B1-U02 | 规划行程 | 旅行 | 比较交通和住宿方案,说明理由和预算 |
|
||||
| B1-U03 | 入境与行李 | 旅行 | 回答入境问题,报告行李延误或丢失 |
|
||||
| B1-U04 | 听懂广播与讲解 | 旅行 | 抓住车站、机场广播和导览讲解的要点 |
|
||||
| B1-U05 | 紧急情况 | 旅行 | 证件或钱包丢失、报警、求助 |
|
||||
| B1-U06 | 就医 | 日常/旅行 | 说明症状变化和病史,理解医嘱要点 |
|
||||
| B1-U07 | 投诉与协商 | 日常/旅行 | 要求退款或补偿,谈出解决办法 |
|
||||
| B1-U08 | 租住与长住 | 日常 | 沟通租房或短租,理解规则要点 |
|
||||
| B1-U09 | 生活服务 | 日常 | 办银行和手机卡业务、寄收快递、预约维修 |
|
||||
| B1-U10 | 观点与理由 | 日常 | 推荐并说明理由,回应不同意见 |
|
||||
| B1-U11 | 习俗与文化 | 旅行 | 谈小费、礼仪和节日,比较不同做法 |
|
||||
| B1-U12 | 深入聊天 | 日常 | 聊经历、计划和感受,维持 5 分钟对话 |
|
||||
| B1-U13 | 读懂生活文本 | 日常/旅行 | 读懂评价、说明、规则和通知 |
|
||||
| B1-U14 | 写说明与投诉 | 日常/旅行 | 写邮件说明经过并提出要求 |
|
||||
|
||||
B2 为可选扩展(自然语速的生活与旅行内容、复杂协商和讨论),B1 通过后再规划单元。
|
||||
|
||||
### 5.3 数据来源
|
||||
|
||||
| 数据 | 锁定版本 | 规模 | 用途 | 许可与要求 |
|
||||
|---|---|---|---|---|
|
||||
| CEFR-J Vocabulary Profile | 1.5 | 7,799 条(按词+词性),词头 6,867;A1 1,164、A2 1,411、B1 2,446、B2 2,778 | 词汇难度上限;建立等级词池(5.4)。约 1,740 条自带主题标注(如 Shopping 197、Food and drink 167、Travel 120、Education 85) | 研究和商用免费;须注明出处(CEFR-J,東京外国語大学投野由紀夫研究室) |
|
||||
| CEFR-J Grammar Profile | 20180315 | 500 条语法项;约 170 条标了 A1.1–B2.2 子级,其余为同一项的否定/疑问变体 | 单元主动语法的难度上限 | 同 CEFR-J 词表 |
|
||||
| Octanove Vocabulary Profile C1/C2 | 1.0 | 2,136 条:C1 1,111、C2 1,025 | 判断超出 B2 的词 | CC BY-SA 4.0;须署名,衍生数据须用同一许可 |
|
||||
| NGSL / NGSL-Spoken | 导入时记录 | NGSL 2,809 词 | 候选词按(口语)频率排序 | CC BY-SA,可商用;须署名 |
|
||||
| ECDICT | 导入时记录提交号 | 约 76 万词条 | 中文释义、音标、考试标签和词频,供人工复核 | MIT;只用 translation、phonetic、tag、bnc、frq,不用来源不明的 collins、oxford 字段 |
|
||||
|
||||
不导入、不抄录:Cambridge English Vocabulary Profile / English Grammar Profile、Oxford 3000、Pearson GSE(没有开放许可)。
|
||||
|
||||
原始数据文件与来源说明(版本、下载地址、SHA-256、许可文本)随内容包一同存档,见 `kouyu_english/tool/courses/lexicon/SOURCES.md`。目前只导入了 CEFR-J 的两份文件,其余数据尚未导入。
|
||||
|
||||
### 5.4 分级数据怎么用
|
||||
|
||||
分级数据只做**难度上限和覆盖检查**,不决定单元的内容和顺序:
|
||||
|
||||
- **语法**:单元主动使用的语法项不高于本等级(A1 单元只用 A1.x,A2 不超过 A2.x,B1 不超过 B1.x);未标等级的否定/疑问变体按主项等级处理。数据等级明显不合生活场景的(如 `You are` 标为 B1.1)可以提前,须写明理由。
|
||||
- **词汇**:功能词、动词、形容词不高于本等级。CEFR-J 依据日本中小学教材统计,不少旅行名词等级偏高(`passport`、`luggage`、`platform`、`pharmacy`、`reservation` 都标为 B1),因此场景必需的具体名词和固定说法可作为**场景词**提前使用:须标注,且每个单元产出核心中场景词不超过 1/3。
|
||||
- **理解词**:不高于本等级加一级,场景词除外。
|
||||
- **等级词池**:起草某等级的单元前,先建立该等级的词池,作为词汇覆盖目标:
|
||||
1. 取 CEFR-J 中属于本等级的词头;
|
||||
2. 按用途分类:通用(功能词和不限场景的常用词)、日常、旅行、工作、学校与学术、其他。分类可多选;CEFR-J 自带主题标注的直接使用,未标注的由 AI 离线分类,用户抽样审核;
|
||||
3. 属于通用、日常或旅行任一类的词进入词池;只属于学校与学术或其他的词不进入;只属于工作的词单独保存,留给后续工作主题线;
|
||||
4. 单元提出的场景词(包括 CEFR-J 未收录的,如 `boarding pass`)并入词池。
|
||||
5. 程序把词池中尚未被前面等级覆盖的词**预分配**给本等级单元,作为各单元的候选词:有主题标注的按主题对应单元(如 Food and drink 分到点单、餐厅、超市),其余的形容词、动词、副词由 AI 按场景合适程度分配,每个单元都要分到一部分。功能词(代词、限定词、介词、连词、情态动词)不分配,靠词块和句型覆盖。
|
||||
词池随等级 stageVersion 冻结,分类结果随数据来源一同存档。
|
||||
- **覆盖检查**:冻结等级 stageVersion 前,本等级词池至少 85% 须被覆盖:作为产出核心或理解词,或出现在产出核心的词块、句型里(如 `Can I have ...?` 覆盖 can、I、have),A0 已教的词也算。每冻结一个单元,其候选词中未覆盖的词顺延到后面的单元;未覆盖的词逐个补进某个单元、放进听读材料,或写入排除项并说明理由。
|
||||
- **A1 词池试建(2026-09-17,未经用户抽样审核)**:CEFR-J 1.5 的 A1 共 1,066 个不同词头。排除只属于学校与学术或其他的 102 个(如 homework、grammar、ruler,以及 fairy、prince、lion、kite、DVD 等儿童读物词和过时词),单独保存只属于工作的 15 个(如 meeting、interview、engineer、business),合并 is/was/did 等词形变体后,词池为 932 个。其中 A0 已覆盖 68 个,约 126 个是功能词和数词,其余约 770 个是实词:无主题标注的形容词 109、名词 105、动词 88、副词 40;有标注的 Free time 68、Personal identification 60、House and home 55、Food and drink 52、Shopping 44、Travel 23、Health 23 等。85% 覆盖需要 A1 单元再覆盖约 730 个。按每单元产出核心 12 项、其中约 8 项覆盖词池词(其余是场景词或纯句型)估算,产出核心覆盖约 150 个,加上词块中的功能词约 100 个,理解词需要覆盖约 480 个,也就是每单元平均约 25 个,因此 A1 的理解词数量从 15–30 调为 20–35。试建发现主题地图的缺口:family 类(aunt、cousin、husband、daughter 等约 20 个)和住处类(kitchen、bedroom、sofa、garden 等约 15 个)没有对应的 A1 单元,描述人和情绪的形容词(tall、young、angry、lonely 等)也没有集中的单元。
|
||||
- **A1–B1 起草结果(2026-09-17,未审核)**:50 个单元全部起草并通过程序校验,状态为 `draft`。词池覆盖 A1 834/933(89.4%)、A2 1040/1078(96.5%)、B1 1344/1552(86.6%)。A2、B1 的理解词范围按实际需要调为 40–65 和 70–110;听读材料词数 A2 听 120–280、读 150–250,B1 听 250–550、读 300–500。
|
||||
- **A0**:保持已冻结的 60 项(`A0-1.0`),不按数据重新生成。对照 CEFR-J 1.5:40 个单词按词形还原后有 39 个属于 A1(`movies`→`movie`、`okay`→`OK/okay`),`zero` 不在词表中,作为 A0 自有项保留。
|
||||
|
||||
### 5.5 单元内容包组成
|
||||
|
||||
| 部分 | 内容 |
|
||||
|---|---|
|
||||
| 元数据 | 单元 ID、修订号(如 `A1-U03 r1`)、数据来源与版本、起草与审核所用模型、审核记录、确认冻结日期 |
|
||||
| 能力 | can-do 描述,对应主题地图中的“单元结束后能做什么” |
|
||||
| 场景 | 场景 ID、学习者身份与 AI 身份、背景、开场句;一个主场景加 2–3 个变式场景 |
|
||||
| 任务 | 任务 ID、目标、引用的产出核心 ID、需要收集的信息槽、示范句;判定条件由程序根据引用编译,不由 AI 填写 |
|
||||
| 产出核心 | 稳定 ID(如 `A1-U03-W01` 词、`A1-U03-P01` 词块或句型)、英文、当前义中文、CEFR-J 等级、是否场景词及理由 |
|
||||
| 理解词 | ID、英文、当前义中文、等级 |
|
||||
| 语法项 | CEFR-J 语法 ID、主动使用或仅理解 |
|
||||
| 单元预检 | 覆盖全部产出核心的听辨与独立表达题(3.6) |
|
||||
| 听读材料(A2 起) | 2–3 篇材料及理解题(第 6 节) |
|
||||
| 排除项 | 考虑过但不纳入的词句及理由 |
|
||||
| 评分标准(A2 起) | 开放任务的评分维度、各档描述和及格档 |
|
||||
|
||||
等级内全部单元冻结后,再生成该等级的出口评估蓝图和至少两套题组(覆盖每个单元的能力),冻结为等级 stageVersion(如 `A1-1.0`),锁定升级分母。之后修改已冻结的单元须新建 stageVersion 并提供旧新 ID 映射,遵循《A0 阶段标准》;已通过的记录保留原版本。
|
||||
|
||||
### 5.6 生成与冻结流程
|
||||
|
||||
```text
|
||||
主题地图中的一个单元(主题 + 能做什么)
|
||||
→ AI 起草单元内容包:先写场景和任务,再从中提取产出核心和理解词;理解词优先从本单元的候选词(5.4)中选
|
||||
→ 程序校验(失败则带错误清单退回修复,最多 2 次,仍失败则停止并提示人工处理)
|
||||
→ 独立审核(另一次 AI 调用,逐项列问题;有问题则退回修复)
|
||||
→ 用户确认 → 冻结单元,随应用发布
|
||||
|
||||
等级内全部单元冻结
|
||||
→ 生成出口评估题组 → 同样校验、审核、确认 → 冻结等级 stageVersion,启用升级评估
|
||||
```
|
||||
|
||||
程序校验至少包括:
|
||||
|
||||
- ID 唯一;每个词都能在锁定数据版本中查到等级,或标为人工补充并给出理由;
|
||||
- 符合 5.4 的语法、词汇、场景词和理解词上限;
|
||||
- 任务引用的 ID 都存在;每个产出核心至少被一个任务和单元预检覆盖;
|
||||
- 示范句与开场句只使用本单元、推荐顺序在前的单元以及 A0 的词句,专名和数字除外;
|
||||
- 听读材料符合第 6 节的覆盖率要求;
|
||||
- 单元覆盖的候选词不少于候选词数的 60%(候选词本身少于 20 个时不检查),未覆盖的候选词顺延;
|
||||
- 冻结等级 stageVersion 时,词池覆盖率符合 5.4。
|
||||
|
||||
单元未冻结时显示“待提供”,不能学习。运行时 AI 只能生成表面变式(人名、地点、物品等内容值,以及措辞和句子顺序),不能增删核心项,也不能改变任务目标或判定条件。
|
||||
|
||||
### 5.7 决定权
|
||||
|
||||
| 事项 | 由谁决定 |
|
||||
|---|---|
|
||||
| 学哪些场景和能力 | 主题地图(5.2),目标固定为日常与旅行;修改须用户确认 |
|
||||
| 单元的核心词句、理解词、语法项 | AI 从场景出发起草,按 5.4 校验,审核后由用户确认冻结 |
|
||||
| 场景设定、角色、开场句、示范句 | AI 离线起草,冻结后固定 |
|
||||
| 每一轮说什么 | 运行时 AI,受对话调度约束(见《AI 对话模块规格》7.3) |
|
||||
| 任务是否完成 | 程序按任务引用的词句和信息槽判定;A2 起再加评审(5.8) |
|
||||
| 掌握状态与复习 | 程序,按第 3 节 |
|
||||
| 升级门槛 | 程序固定,见 4.1 与 4.2;AI 与内容包都不能修改 |
|
||||
|
||||
### 5.8 A2 及以上的开放任务判定
|
||||
|
||||
A2 起任务包含叙述、理由、建议等开放表达,不能只靠关键词判定:
|
||||
|
||||
- **程序硬性检查**:句数或长度、必需信息槽、是否用到任务要求的目标语言项、清单外词比例、是否为英文;
|
||||
- **AI 评审**:按单元的评分标准逐维度给档,每一档须引用用户原句作为证据;程序核对引用确实出自用户作答,引用不符则丢弃这次评审;
|
||||
- **判定**:硬性检查全部通过,且各维度达到及格档,才算完成任务;评审失败或服务不可用记为待确认,不记语言失败;
|
||||
- A2 → B1 与 B1 结业的出口评估须人工复核,与 4.3 一致。
|
||||
|
||||
## 6. 听读输入
|
||||
|
||||
A0–A1 的听读输入在课程和对话里完成。从 A2 起单独安排输入,并逐级提高比例:
|
||||
|
||||
| 等级 | 占每日时长 | 听 | 读 | 语速与声音 |
|
||||
|---|---|---|---|---|
|
||||
| A2 | 约 20%(20 分钟模式约 4 分钟) | 1–2 分钟的对话、通知、电话留言 | 150–250 词的消息、菜单、说明、短文 | 慢速到正常;2–3 种声音 |
|
||||
| B1 | 约 35%(20 分钟模式约 7 分钟) | 2–4 分钟的广播、导览、访谈式对话 | 300–500 词的评价、邮件、规则、故事 | 正常语速;多种常见口音,可加轻微背景声 |
|
||||
|
||||
输入时间从教学额度中划出,不额外加时。
|
||||
|
||||
- **材料来源**:每个单元随内容包冻结 2–3 篇;另可由 AI 在运行时按同样规则生成新篇目,只作练习。不内置未授权的真实材料。
|
||||
- **程序检查**:按词次计,超出“已学产出核心 + 已学理解词 + 本单元场景词”的词不超过 2%(约 98% 的词汇覆盖才能较好理解),每个超出词附当前义释义;句长与语法不高于本等级。
|
||||
- **理解题**:每篇 2–4 道(要点、细节、推断),结果只更新相关理解词的识别证据,不更新产出核心。
|
||||
- **查词**:材料中查过或多次出现的词可一键加入扩展词复习(3.5)。
|
||||
- **记录**:进度页显示每周听读分钟数;不设硬指标,连续两周明显低于计划时在首页提示。
|
||||
|
||||
## 7. 个人使用检查
|
||||
## 6. 个人使用检查
|
||||
|
||||
不需要留存指标。每周检查以下问题即可:
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ M1 是完整的 **A0 学习闭环**,不是全部 A0–B2 产品:
|
||||
|
||||
不包含:账号、社交、付费、留存运营、通知、自建业务后台、自动同步、导出/导入、自由主题聊天、实时全双工语音、精细发音诊断。导出/导入和 A1 课程在 M2;A2 及以上逐阶段扩展,不因文档列出目标而视为已经交付。
|
||||
|
||||
M1 通过 A0 后记录 `A0 已通过,A1 内容待提供`,可继续 A0 巩固;不在运行时生成 A1 主线。A1 内容须按《学习引擎规格》第 5 节离线生成单元内容包并经确认冻结;只有 A0 通过且至少有一个已冻结的 A1 单元时才允许进入。A1 内容冻结前,入门定位只在 A0 内推荐起点;之后的定级规则见学习引擎 3.6。
|
||||
M1 通过 A0 后记录 `A0 已通过,A1 内容待提供`,可继续 A0 巩固;不生成未经定义的 A1 主线。只有阶段通过且下一阶段内容及评估规范已就绪时才允许进入。入门定位不能绕过阶段门槛。
|
||||
|
||||
## 2. 内容与生成契约
|
||||
|
||||
|
||||
@@ -19,13 +19,16 @@ M1 是手机优先的 A0 单人学习闭环:10 节种子课及分段、听说
|
||||
- 进度以“能完成什么”展示,不只显示词汇数量或分数。
|
||||
- 学习进度、复习队列与设置保存在本机;如需换设备续学,后续提供加密导出/导入,不需要账号系统或多人数据模型。
|
||||
|
||||
## P01 欢迎与每日时长
|
||||
## P01 欢迎与目标
|
||||
|
||||
```text
|
||||
┌──────────────────────────┐
|
||||
│ 开口英语 │
|
||||
│ 每天 20 分钟,说出能用的英语 │
|
||||
│ │
|
||||
│ 你的主要目标是? │
|
||||
│ ○ 日常生活 ○ 旅行 ○ 工作起步│
|
||||
│ │
|
||||
│ 每天可以学习多久? │
|
||||
│ [10 分钟] [20 分钟] [30 分钟]│
|
||||
│ │
|
||||
@@ -33,9 +36,9 @@ M1 是手机优先的 A0 单人学习闭环:10 节种子课及分段、听说
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
**输入**:每日时长(学习目标固定为日常与旅行,不做选择)。
|
||||
**输入**:目标、每日时长。
|
||||
**输出**:保存为学习计划参数;进入 P02。
|
||||
**验收**:全程 1 次选择;跳过不影响后续使用。
|
||||
**验收**:全程不超过 2 次选择;跳过不影响后续使用。
|
||||
|
||||
## P02 起点定位
|
||||
|
||||
|
||||
+39
-23
@@ -1,8 +1,8 @@
|
||||
# 开口英语(暂定名)首版产品 PRD
|
||||
|
||||
> 版本:0.3|2026-09-17
|
||||
> 版本:0.2|2026-09-12
|
||||
> 使用方式:个人自用、手机优先,支持 iPhone、Android 与 Mac 的中文英语学习工具
|
||||
> 学习目标:固定为日常生活与旅行,不提供目标选择;按可用时间练习,逐步达到 B1 的日常与旅行交流能力,追求稳定听懂、稳定被听懂;不承诺固定周数达标。
|
||||
> 学习目标:按可用时间练习,逐步把英语用在生活中,追求稳定听懂、稳定被听懂;不承诺固定周数达标。
|
||||
|
||||
## 1. 产品概述
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
发音目标是清楚、可理解、可完成任务,而非模仿特定地区口音;听力目标是理解当前阶段场景中的主要意思与关键细节,并能在没听清时继续沟通。
|
||||
|
||||
每日内容生成、词句掌握判定、阶段出口任务、主题地图(A1–B1 单元)与听读输入,统一以 [学习引擎规格](LEARNING-ENGINE-SPEC.md) 为准。
|
||||
每日内容生成、词句掌握判定、阶段出口任务与通向 B1/B2 的内容扩展,统一以 [学习引擎规格](LEARNING-ENGINE-SPEC.md) 为准。
|
||||
|
||||
M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTATION-CONTRACT.md) 为准;A0 可执行门槛见 [A0 阶段标准](A0-STAGE-STANDARD.md)。下文 A1 场景是后续方向,不属于 M1。
|
||||
|
||||
@@ -26,8 +26,7 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
|
||||
### 1.3 当前不做
|
||||
|
||||
- 不以雅思、托福、四六级等考试提分为目标,不安排学校与学术场景。
|
||||
- 暂不做工作场景;工作需求后续作为独立主题线增加,见学习引擎第 1 节。
|
||||
- 不以雅思、托福、四六级等考试提分为目标。
|
||||
- 不在首版提供高阶新闻阅读、商务谈判或开放社交社区。
|
||||
- 不把整套教材或外部网站内容直接搬入应用。
|
||||
- 不做登录注册、付费、消息推送、排行榜、多人协作、增长分析或上线运营。
|
||||
@@ -39,7 +38,7 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
|
||||
| 用户 | 现状 | 需求 |
|
||||
|---|---|---|
|
||||
| 零基础成人 | 认识少量单词,羞于开口 | 从最简单且有用的句子开始,得到耐心纠错 |
|
||||
| 零基础职场成人 | 认识少量单词,羞于开口 | 从最简单且有用的句子开始,得到耐心纠错 |
|
||||
| 有基础但遗忘的成人 | 学过语法,听说困难 | 快速定位起点,恢复日常对话能力 |
|
||||
| 旅行/生活需求者 | 有明确近期出行或生活任务 | 在出发前完成点餐、问路、入住等演练 |
|
||||
|
||||
@@ -50,9 +49,8 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
| 等级 | 名称 | 核心能力 | 进入条件 |
|
||||
|---|---|---|---|
|
||||
| A0 | 起步 | 认识常用字母/声音,能说极短句 | 新用户默认起点或测评结果 |
|
||||
| A1 | 生存 | 用简单句完成固定的生活与旅行互动 | A0 通过且有已冻结的 A1 单元;M1 显示待提供 |
|
||||
| A2 | 日常 | 讲经历、做预订与改期、处理行程和住宿中的小问题 | A1 通过且有已冻结的 A2 单元 |
|
||||
| B1 | 独立 | 独立应对旅行和生活中的大多数情况 | A2 通过且有已冻结的 B1 单元;B2 为可选扩展 |
|
||||
| A1 | 生存 | 用简单句完成固定生活互动 | A0 通过且 A1 内容就绪;M1 显示待提供 |
|
||||
| A2 | 日常 | 描述日常、计划与简单经历 | 首版仅显示“即将推出” |
|
||||
|
||||
### 3.1 入门测评
|
||||
|
||||
@@ -62,11 +60,11 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
2. 1 题朗读 `Hello. My name is ...`,可跳过;
|
||||
3. 1 题回答 `What is your name?`,可用文字或录音。
|
||||
|
||||
结果只给出建议起点和原因,不给“失败”标签。有基础的用户可再做定级评估,用出口题组证明后从更高等级开始,规则见学习引擎 3.6;A1 内容冻结前只推荐 A0 内的起点。用户可随时选择“从更简单内容开始”。语音题不作为硬性分级依据,避免麦克风环境造成误判。
|
||||
结果只给出建议起点和原因,不给“失败”标签。用户可随时选择“从更简单内容开始”。语音题不作为硬性分级依据,避免麦克风环境造成误判。
|
||||
|
||||
### 3.2 阶段升级
|
||||
|
||||
阶段升级不由完成课数决定。A0 必须达到固定核心词句门槛并通过两次不同题组的听说读写评估;初次见面对话仅是其中一部分。可提前评估但不得未通过就跳级。A1 起门槛只设在等级之间,单元之间不设门槛;各等级门槛与出口任务见学习引擎 4.2、4.3。完整规则见 [学习引擎规格](LEARNING-ENGINE-SPEC.md)。
|
||||
阶段升级不由完成课数决定。A0 必须达到固定核心词句门槛并通过两次不同题组的听说读写评估;初次见面对话仅是其中一部分。可提前评估但不得未通过就跳级。A1→A2 的旅行日模拟是后续方向,须先补齐 A1 标准才能启用。完整规则见 [学习引擎规格](LEARNING-ENGINE-SPEC.md)。
|
||||
|
||||
## 4. 学习体验与信息架构
|
||||
|
||||
@@ -78,7 +76,7 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
| 学习 | 完成一节新课 | 情境、词句、听说读写任务 |
|
||||
| 对话 | 在安全的情境中开口交流 | 场景卡、语音/文字对话、对话总结 |
|
||||
| 复习 | 巩固个人薄弱点 | 忘记的词、说错的句、听错的音频 |
|
||||
| 我的 | 看得见进步 | 当前等级、掌握句型、练习记录、每周听读时间、每日时长设置 |
|
||||
| 我的 | 看得见进步 | 当前等级、掌握句型、练习记录、目标设置 |
|
||||
|
||||
课程、阅读和对话中的英文均可点词或点短语查看当前语境下的中文意思、音频、短例句,并手动加入复习;短语优先于单词。查词卡不是独立主导航,关闭后回到原步骤。
|
||||
|
||||
@@ -87,7 +85,8 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
### 4.2 新用户流程
|
||||
|
||||
```text
|
||||
选择每日时间(10 / 20 / 30 分钟)
|
||||
选择目标(生活 / 旅行 / 工作起步)
|
||||
→ 选择每日时间(10 / 20 / 30 分钟)
|
||||
→ 可选入门测评
|
||||
→ 得到起点与第一节课
|
||||
→ 完成一次开口练习
|
||||
@@ -106,7 +105,7 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
|
||||
### 4.4 每日内容选择
|
||||
|
||||
每日任务按预算从“到期复习、未完成课/主线新课、错误修复、短对话”选择,缺少到期项或错误项时不硬凑。AI 不能在运行时决定能力主线,只能生成通过校验的变式;主线来自主题地图和离线生成、冻结的单元内容包。具体优先级见 [学习引擎规格](LEARNING-ENGINE-SPEC.md)。
|
||||
每日任务按预算从“到期复习、未完成课/主线新课、错误修复、短对话”选择,缺少到期项或错误项时不硬凑。AI 不能随机决定能力主线,只能生成通过校验的变式。具体优先级见 [学习引擎规格](LEARNING-ENGINE-SPEC.md)。
|
||||
|
||||
每个包含新语言的教学段在听力前先提供 1–3 分钟“今天先认识”的词汇预热:只展示本段会用到的 3–6 个词或词块(不超过该段预算),逐项播放英文、显示当前句义中文与一个短例句,再完成一次轻量匹配/听辨。用户可跳过或回看;预热不是掌握测试,不能因看过词卡就增加掌握度或自动完成课程。
|
||||
|
||||
@@ -167,9 +166,30 @@ M1 范围、内容数据和异常验收以 [开发与验收约定](M1-IMPLEMENTA
|
||||
| 9 | 我喜欢… | 表达一项喜好 |
|
||||
| 10 | A0 小对话 | 完成 45 秒自我介绍 |
|
||||
|
||||
### A1 起:主题单元
|
||||
### A1:生存英语(11–30)
|
||||
|
||||
A1(19 个单元)、A2(15 个)和 B1(14 个)的主题单元统一以 [学习引擎规格](LEARNING-ENGINE-SPEC.md) 5.2 主题地图为准,本文不再重复列出。单元围绕日常生活与旅行,按依赖顺序推荐,单元之间不设门槛。
|
||||
| 编号 | 主题 | 课后能做什么 |
|
||||
|---:|---|---|
|
||||
| 11 | 在咖啡店点单 | 点饮料并表达礼貌需求 |
|
||||
| 12 | 在餐厅点餐 | 点餐、询问推荐 |
|
||||
| 13 | 结账与价格 | 询价、付款、确认金额 |
|
||||
| 14 | 买衣服 | 询问尺码、颜色、试穿 |
|
||||
| 15 | 超市购物 | 找商品、说数量 |
|
||||
| 16 | 问路 | 询问地点与方向 |
|
||||
| 17 | 打车 | 告知目的地并确认路线 |
|
||||
| 18 | 公交与地铁 | 买票、问站点、确认下车 |
|
||||
| 19 | 入住酒店 | 办理入住、说明预订信息 |
|
||||
| 20 | 酒店里的问题 | 要毛巾、报告房间问题 |
|
||||
| 21 | 在机场 | 办理值机、问登机口 |
|
||||
| 22 | 看病不舒服 | 说明简单症状、听懂基本建议 |
|
||||
| 23 | 预约时间 | 约见面、改时间 |
|
||||
| 24 | 打电话留言 | 留下姓名、号码和简短信息 |
|
||||
| 25 | 认识新朋友 | 询问职业、兴趣和居住地 |
|
||||
| 26 | 介绍日常生活 | 说作息和常做的事 |
|
||||
| 27 | 今天的天气 | 谈天气与穿衣建议 |
|
||||
| 28 | 周末计划 | 说将要做的事 |
|
||||
| 29 | 请求帮助 | 礼貌求助、感谢与回应 |
|
||||
| 30 | A1 生活挑战 | 完成 3 分钟多场景模拟 |
|
||||
|
||||
## 7. 功能需求与验收标准
|
||||
|
||||
@@ -284,7 +304,7 @@ AI 对话区分以下模式,不能将受控场景误做成无限聊天:
|
||||
|
||||
## 9. 数据与进度
|
||||
|
||||
为每位用户记录:当前等级、课程完成状态、词汇/句型掌握度、听力正确率、常见写作错误、口语尝试次数和复习到期时间。进度页避免只显示“学了多少词”,应显示可完成的任务,例如“你已能用英语点餐”。
|
||||
为每位用户记录:当前等级、目标、课程完成状态、词汇/句型掌握度、听力正确率、常见写作错误、口语尝试次数和复习到期时间。进度页避免只显示“学了多少词”,应显示可完成的任务,例如“你已能用英语点餐”。
|
||||
|
||||
## 10. 个人使用验收清单
|
||||
|
||||
@@ -303,15 +323,11 @@ AI 对话区分以下模式,不能将受控场景误做成无限聊天:
|
||||
|
||||
### M2:完整个人学习路径
|
||||
|
||||
在 M1 闭环基础上新增 A1 单元内容包(19 个单元,见学习引擎 5.2)、单元预检、两层词汇及出口评估,扩展生活场景与自由主题对话;可增加加密导出/导入。M1 已有的复习、纠错、场景页和进度不重复列为 M2 首次交付。
|
||||
在 M1 闭环基础上新增 A1 能力地图、核心清单、种子主题及出口评估,扩展生活场景与自由主题对话;可增加加密导出/导入。M1 已有的复习、纠错、场景页和进度不重复列为 M2 首次交付。
|
||||
|
||||
### M3:个性化与 A2
|
||||
|
||||
加入更细的能力诊断、学习计划调整、入门定级跳级,以及 A2 单元与听读输入。在你完成 A1 路径并确认现有内容和反馈确实满足个人学习需要后再启动。
|
||||
|
||||
### 之后:B1
|
||||
|
||||
A2 通过后启动 B1 单元(见学习引擎 5.2)。B1 是主线终点,B2 为可选扩展,B1 通过后再规划。工作需求作为后续主题线增加,不改变日常与旅行主线。
|
||||
加入更细的能力诊断、学习计划调整、旅行/工作目标路径,以及 A2 课程。在你完成 A1 路径并确认现有内容和反馈确实满足个人学习需要后再启动。
|
||||
|
||||
## 12. 版权与内容来源
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
| 编号 | 页面 / 状态 | 主要目的 | 关键动作 |
|
||||
|---:|---|---|---|
|
||||
| 01 | 每日时长 | 设置每日学习时长(目标固定为日常与旅行) | 进入起点定位 |
|
||||
| 01 | 目标设置 | 设置学习目的和每日时长 | 进入起点定位 |
|
||||
| 02 | 起点定位 | 选择或轻量确认起始等级 | 开始学习 / 跳过 |
|
||||
| 03 | 首页 | 呈现唯一的今日主任务 | 开始课程或短对话 |
|
||||
| 04 | 学习地图 | 查看当前阶段与课程顺序 | 进入课程 |
|
||||
@@ -38,7 +38,7 @@
|
||||
## 关键流转
|
||||
|
||||
```text
|
||||
首次:每日时长 → 起点定位 → 首页
|
||||
首次:目标设置 → 起点定位 → 首页
|
||||
学习:首页 → 到期复习(有则按预算) → 预热 → 带帮助的听说读写 → AI 对话 → 独立尝试 → 总结
|
||||
识别不确定:AI 对话 → 转写确认 → AI 对话 / 总结
|
||||
查词:课程 / 阅读 / AI 对话 → 查词卡 → 返回原步骤
|
||||
|
||||
+34
-152
@@ -24,7 +24,6 @@
|
||||
#learning-app-prototype .screen-list { display:flex; flex-direction:column; gap:3px; margin-top:15px; }
|
||||
#learning-app-prototype .screen-link { text-align:left; border:0; border-radius:10px; color:var(--ka-ink); background:transparent; padding:9px 10px; font-size:13px; }
|
||||
#learning-app-prototype .screen-link[aria-pressed="true"] { background:var(--ka-soft-green); color:var(--ka-green); font-weight:500; }
|
||||
#learning-app-prototype .screen-link.sub { padding-left:22px; font-size:12px; }
|
||||
#learning-app-prototype .legend-title { font-size:13px; font-weight:500; margin:0 0 10px; }
|
||||
#learning-app-prototype .flow { display:flex; flex-direction:column; gap:8px; }
|
||||
#learning-app-prototype .flow-row { border-left:2px solid var(--ka-line); padding:3px 0 3px 9px; font-size:12px; line-height:1.35; }
|
||||
@@ -116,62 +115,6 @@
|
||||
#learning-app-prototype .mac-main h1 { font-size:23px; }
|
||||
#learning-app-prototype .mac-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:16px; }
|
||||
#learning-app-prototype .mac-grid .slot { margin:0; min-height:104px; }
|
||||
#learning-app-prototype .me-card { background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:18px; padding:14px; }
|
||||
#learning-app-prototype .me-head { display:flex; align-items:center; gap:12px; }
|
||||
#learning-app-prototype .avatar { width:48px; height:48px; border-radius:50%; display:grid; place-items:center; background:var(--ka-soft-green); color:var(--ka-green); font-size:19px; font-weight:500; }
|
||||
#learning-app-prototype .me-name { font-size:17px; font-weight:500; }
|
||||
#learning-app-prototype .level-badge { display:inline-block; margin-left:6px; padding:2px 7px; border-radius:999px; background:var(--ka-soft-green); color:var(--ka-green); font-size:11px; vertical-align:2px; }
|
||||
#learning-app-prototype .stats { display:grid; grid-template-columns:repeat(4,1fr); margin-top:13px; padding-top:12px; border-top:1px solid var(--ka-line); }
|
||||
#learning-app-prototype .stat { border:0; background:transparent; color:var(--ka-ink); text-align:center; padding:0; }
|
||||
#learning-app-prototype .stat b { display:block; font-size:19px; font-weight:500; }
|
||||
#learning-app-prototype .stat span { color:var(--ka-muted); font-size:11px; }
|
||||
#learning-app-prototype .stat.due b { color:var(--ka-warm-ink); }
|
||||
#learning-app-prototype .goal-card { width:100%; display:block; text-align:left; border:0; border-radius:16px; padding:13px 14px; margin-top:11px; background:var(--ka-soft-green); color:var(--ka-ink); }
|
||||
#learning-app-prototype .goal-card .meter { margin:9px 0 8px; background:light-dark(#c9e4d3, #2a4a37); }
|
||||
#learning-app-prototype .group-title { color:var(--ka-muted); font-size:12px; margin:17px 4px 6px; }
|
||||
#learning-app-prototype .menu { background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:15px; overflow:hidden; }
|
||||
#learning-app-prototype .menu-row { width:100%; display:flex; align-items:center; gap:11px; border:0; border-bottom:1px solid var(--ka-line); background:transparent; color:var(--ka-ink); padding:11px 12px; text-align:left; font-size:14px; }
|
||||
#learning-app-prototype .menu-row:last-child { border-bottom:0; }
|
||||
#learning-app-prototype .menu-row .ico { width:28px; height:28px; flex:none; display:grid; place-items:center; border-radius:8px; background:var(--ka-surface-2); font-size:14px; }
|
||||
#learning-app-prototype .menu-row .label { flex:1; }
|
||||
#learning-app-prototype .menu-row .label small { display:block; color:var(--ka-muted); font-size:11px; margin-top:1px; }
|
||||
#learning-app-prototype .menu-row .val { color:var(--ka-muted); font-size:12px; }
|
||||
#learning-app-prototype .menu-row .chev { color:var(--ka-muted); font-size:16px; }
|
||||
#learning-app-prototype .menu-row.danger { color:light-dark(#b3261e, #ffb4ab); justify-content:center; }
|
||||
#learning-app-prototype .version { text-align:center; color:var(--ka-muted); font-size:11px; margin:12px 0 0; }
|
||||
#learning-app-prototype .scroll { height:593px; overflow-y:auto; }
|
||||
#learning-app-prototype .ability { padding:11px 0; border-bottom:1px solid var(--ka-line); }
|
||||
#learning-app-prototype .ability:last-child { border-bottom:0; }
|
||||
#learning-app-prototype .ability .meter { margin:7px 0 0; }
|
||||
#learning-app-prototype details { margin-top:11px; padding:11px 12px; background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:15px; font-size:13px; }
|
||||
#learning-app-prototype details p { color:var(--ka-muted); font-size:12px; line-height:1.55; margin:8px 0 0; }
|
||||
#learning-app-prototype .tag { display:inline-block; padding:2px 7px; border-radius:999px; font-size:11px; font-weight:400; background:var(--ka-surface-2); color:var(--ka-muted); }
|
||||
#learning-app-prototype .tag.ok { background:var(--ka-soft-green); color:var(--ka-green); }
|
||||
#learning-app-prototype .tag.help { background:var(--ka-warm); color:var(--ka-warm-ink); }
|
||||
#learning-app-prototype .day { color:var(--ka-muted); font-size:12px; margin:14px 4px 6px; }
|
||||
#learning-app-prototype .field { padding:10px 12px; border-bottom:1px solid var(--ka-line); font-size:13px; }
|
||||
#learning-app-prototype .field span { display:block; color:var(--ka-muted); font-size:11px; margin-bottom:3px; }
|
||||
#learning-app-prototype .segmented { display:flex; padding:3px; border-radius:12px; background:var(--ka-surface-2); margin-top:9px; }
|
||||
#learning-app-prototype .segmented button { flex:1; border:0; border-radius:9px; padding:8px 0; background:transparent; color:var(--ka-muted); font-size:13px; }
|
||||
#learning-app-prototype .segmented button.on { background:var(--ka-surface); color:var(--ka-ink); font-weight:500; box-shadow:0 1px 3px rgba(0,0,0,.12); }
|
||||
#learning-app-prototype .row2 { display:flex; gap:8px; } #learning-app-prototype .row2 > * { flex:1; }
|
||||
#learning-app-prototype .state-switch { display:flex; flex-wrap:wrap; align-items:center; gap:5px; margin:10px 0 2px; padding:7px 8px; border:1px dashed var(--ka-line); border-radius:11px; font-size:11px; color:var(--ka-muted); }
|
||||
#learning-app-prototype .state-switch button { border:1px solid var(--ka-line); border-radius:999px; padding:3px 8px; background:var(--ka-surface); color:var(--ka-muted); font-size:11px; }
|
||||
#learning-app-prototype .state-switch button.on { border-color:var(--ka-green); color:var(--ka-green); }
|
||||
#learning-app-prototype .slot[hidden] { display:none; }
|
||||
#learning-app-prototype .path-head { display:flex; justify-content:space-between; align-items:baseline; margin-top:20px; font-size:15px; }
|
||||
#learning-app-prototype .path-head b { font-weight:500; }
|
||||
#learning-app-prototype .path { margin-top:8px; background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:15px; overflow:hidden; }
|
||||
#learning-app-prototype .lesson-row { display:flex; align-items:center; gap:11px; padding:11px 12px; border-bottom:1px solid var(--ka-line); }
|
||||
#learning-app-prototype .lesson-row:last-child { border-bottom:0; }
|
||||
#learning-app-prototype .lesson-row .label { flex:1; }
|
||||
#learning-app-prototype .lesson-row b { display:block; font-size:14px; font-weight:500; }
|
||||
#learning-app-prototype .lesson-row small { color:var(--ka-muted); font-size:11px; }
|
||||
#learning-app-prototype .lesson-ico { width:26px; height:26px; flex:none; display:grid; place-items:center; border-radius:50%; font-style:normal; font-size:12px; background:var(--ka-surface-2); color:var(--ka-muted); }
|
||||
#learning-app-prototype .lesson-row.done .lesson-ico { background:var(--ka-green); color:var(--ka-green-ink); }
|
||||
#learning-app-prototype .lesson-row.current { background:var(--ka-soft-green); }
|
||||
#learning-app-prototype .lesson-row.current .lesson-ico { background:var(--ka-green); color:var(--ka-green-ink); }
|
||||
#learning-app-prototype .lesson-row.locked b { color:var(--ka-muted); }
|
||||
@media (max-width: 880px) { #learning-app-prototype .studio { grid-template-columns:180px minmax(300px, 1fr); } #learning-app-prototype .flow-panel { display:none; } }
|
||||
@media (max-width: 590px) { #learning-app-prototype .studio { display:block; } #learning-app-prototype .panel { margin-bottom:15px; } #learning-app-prototype .screen-list { display:grid; grid-template-columns:1fr 1fr; } #learning-app-prototype .phone { width:100%; max-width:352px; } #learning-app-prototype .phone.mac { width:100%; max-width:430px; } }
|
||||
</style>
|
||||
@@ -180,9 +123,10 @@
|
||||
<div class="brand">开口英语</div>
|
||||
<p class="small">M1 · 手机优先原型</p>
|
||||
<div class="screen-list" role="group" aria-label="选择页面">
|
||||
<button class="screen-link" data-screen-target="welcome" aria-pressed="false" type="button">01 欢迎与每日时长</button>
|
||||
<button class="screen-link" data-screen-target="welcome" aria-pressed="false" type="button">01 目标设置</button>
|
||||
<button class="screen-link" data-screen-target="placement" aria-pressed="false" type="button">02 起点定位</button>
|
||||
<button class="screen-link" data-screen-target="home" aria-pressed="true" type="button">03 学习(首页 + 地图合并)</button>
|
||||
<button class="screen-link" data-screen-target="home" aria-pressed="true" type="button">03 首页</button>
|
||||
<button class="screen-link" data-screen-target="map" aria-pressed="false" type="button">04 学习地图</button>
|
||||
<button class="screen-link" data-screen-target="listen" aria-pressed="false" type="button">05 听力练习</button>
|
||||
<button class="screen-link" data-screen-target="speak" aria-pressed="false" type="button">06 跟读练习</button>
|
||||
<button class="screen-link" data-screen-target="write" aria-pressed="false" type="button">07 造句练习</button>
|
||||
@@ -191,15 +135,8 @@
|
||||
<button class="screen-link" data-screen-target="confirm" aria-pressed="false" type="button">10 转写确认</button>
|
||||
<button class="screen-link" data-screen-target="summary" aria-pressed="false" type="button">11 练习总结</button>
|
||||
<button class="screen-link" data-screen-target="review" aria-pressed="false" type="button">12 今日复习</button>
|
||||
<button class="screen-link" data-screen-target="progress" aria-pressed="false" type="button">13 我的</button>
|
||||
<button class="screen-link sub" data-screen-target="me-level" aria-pressed="false" type="button">13a 升级进度</button>
|
||||
<button class="screen-link sub" data-screen-target="me-assess" aria-pressed="false" type="button">13b 四技能评估</button>
|
||||
<button class="screen-link sub" data-screen-target="me-records" aria-pressed="false" type="button">13c 学习记录</button>
|
||||
<button class="screen-link sub" data-screen-target="me-audio" aria-pressed="false" type="button">13d 我的录音</button>
|
||||
<button class="screen-link" data-screen-target="settings" aria-pressed="false" type="button">14 学习偏好</button>
|
||||
<button class="screen-link sub" data-screen-target="set-theme" aria-pressed="false" type="button">14a 外观</button>
|
||||
<button class="screen-link sub" data-screen-target="set-sync" aria-pressed="false" type="button">14b 云同步</button>
|
||||
<button class="screen-link sub" data-screen-target="set-ai" aria-pressed="false" type="button">14c AI 服务</button>
|
||||
<button class="screen-link" data-screen-target="progress" aria-pressed="false" type="button">13 我的进步</button>
|
||||
<button class="screen-link" data-screen-target="settings" aria-pressed="false" type="button">14 设置</button>
|
||||
<button class="screen-link" data-screen-target="mac" aria-pressed="false" type="button">15 Mac 适配</button>
|
||||
<button class="screen-link" data-screen-target="dictionary" aria-pressed="false" type="button">16 查词卡</button>
|
||||
<button class="screen-link" data-screen-target="preview" aria-pressed="false" type="button">17 课前词汇预热</button>
|
||||
@@ -213,7 +150,8 @@
|
||||
<div class="eyebrow">欢迎</div>
|
||||
<h1>每天 20 分钟,<br>说出能用的英语。</h1>
|
||||
<p class="bodycopy">从真实生活场景开始,先做到听得懂、说得清。</p>
|
||||
<div class="slot selected"><div class="slot-title">每天学习多久?</div><div class="assist"><span class="chip">10 分钟</span><span class="chip" style="background:var(--ka-soft-green);color:var(--ka-green)">20 分钟</span><span class="chip">30 分钟</span></div></div>
|
||||
<div class="slot selected"><div class="slot-title">你的主要目标</div><div class="assist"><span class="chip">日常生活</span><span class="chip">旅行</span><span class="chip">工作起步</span></div></div>
|
||||
<div class="slot"><div class="slot-title">每天学习多久?</div><div class="assist"><span class="chip">10 分钟</span><span class="chip" style="background:var(--ka-soft-green);color:var(--ka-green)">20 分钟</span><span class="chip">30 分钟</span></div></div>
|
||||
<button class="primary" data-go="placement" type="button">继续</button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -230,25 +168,23 @@
|
||||
</section>
|
||||
<section class="screen" data-screen="home">
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div>
|
||||
<div class="appbody scroll">
|
||||
<div class="eyebrow">早上好,Shen</div><h1>今天,说几句能用的英语。</h1>
|
||||
<div class="state-switch" role="group" aria-label="原型:切换今日任务状态"><span>原型状态:</span><button class="on" data-today="review" type="button">有到期复习</button><button data-today="lesson" type="button">上新课</button><button data-today="stage" type="button">A0 学完</button></div>
|
||||
<div class="slot selected today" data-today-card="review"><div class="slot-title"><span>今天先复习 3 项</span><span class="small">6 分钟</span></div><div class="slot-note">昨天练过的关键句,今天换个情境再用一次。复习完再上新课。</div><button class="primary" data-go="review" type="button">开始复习</button></div>
|
||||
<div class="slot selected today" data-today-card="lesson" hidden><div class="slot-title"><span>第 3 课 · 来自哪里 · 第 2/3 段</span><span class="small">12 分钟</span></div><div class="slot-note">预热词汇 → 听说读写 → 对话 → 独立尝试</div><button class="primary" data-go="preview" type="button">开始今天的学习</button></div>
|
||||
<div class="slot selected today" data-today-card="stage" hidden><div class="slot-title"><span>A0 阶段评估 · A0-E1</span><span class="small">15 分钟</span></div><div class="slot-note">可使用 48/48 · 已掌握 30/30。两套评估间隔至少 24 小时。</div><button class="primary" type="button">开始阶段评估</button></div>
|
||||
<div class="path-head"><b>A0 课程</b><span class="small">2 / 8 完成</span></div>
|
||||
<div class="meter" style="margin:6px 0 4px"><span style="width:25%"></span></div>
|
||||
<div class="path">
|
||||
<div class="lesson-row done"><i class="lesson-ico">✓</i><div class="label"><b>第 1 课 · 你好,我是……</b><small>介绍姓名并回应问候</small></div></div>
|
||||
<div class="lesson-row done"><i class="lesson-ico">✓</i><div class="label"><b>第 2 课 · 拼读姓名</b><small>听懂拼写,说出名字字母</small></div></div>
|
||||
<div class="lesson-row current"><i class="lesson-ico">▶</i><div class="label"><b>第 3 课 · 来自哪里</b><small>说出来自哪里 · 小段 1/3</small></div></div>
|
||||
<div class="lesson-row locked"><i class="lesson-ico">🔒</i><div class="label"><b>第 4 课 · 数字与电话号码</b><small>说 0–10 与简单号码</small></div></div>
|
||||
<div class="lesson-row locked"><i class="lesson-ico">🔒</i><div class="label"><b>第 5 课 · 你好吗?</b><small>问候并表达简单状态</small></div></div>
|
||||
<div class="lesson-row locked"><i class="lesson-ico">🔒</i><div class="label"><b>…… 第 8 课</b><small>A0 课程结束后进入巩固与阶段评估</small></div></div>
|
||||
</div>
|
||||
<p class="small" style="margin-top:10px">已完成的课可以随时点开重练;有复习积压时,新课会暂时锁住。</p>
|
||||
<div class="appbody">
|
||||
<div class="eyebrow">早上好,Shen</div><h1>今天,说 3 句英语。</h1><p class="bodycopy">第 1 课已完成。今天把姓名介绍说得更自然。</p>
|
||||
<div class="meter" aria-label="当前掌握程度"><span style="width:30%"></span></div><p class="small">A0 起步 · 已完成 3 个核心任务</p>
|
||||
<div class="slot selected"><div class="slot-title"><span>第 2 课 · 拼读姓名</span><span>12 分钟</span></div><div class="slot-note">先认识 4 个词 · 听 · 说 · 读 · 写</div><button class="primary" data-go="preview" type="button">开始今天的学习</button></div>
|
||||
<div class="slot"><div class="slot-title">快速练习 · 初次见面</div><div class="slot-note">3 分钟 AI 对话 · 介绍姓名与来自哪里</div><button class="secondary" data-go="dialogue" type="button">开始对话</button></div>
|
||||
</div>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem active" data-go="home" type="button"><i class="nav-dot"></i>学习</button><button class="navitem" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem active" data-go="home" type="button"><i class="nav-dot"></i>首页</button><button class="navitem" data-go="map" type="button"><i class="nav-dot"></i>学习</button><button class="navitem" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
</section>
|
||||
<section class="screen" data-screen="map" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="home" type="button">‹</button><h2>A0 起步</h2></div>
|
||||
<div class="appbody"><div class="eyebrow">学习地图 · 已完成 3 个任务</div><h1>从认识到开口</h1><p class="bodycopy">核心课建立能力;达到出口任务才进入下一阶段。</p>
|
||||
<div class="slot"><div class="slot-title">✓ 第 1 课 · 你好,我是……</div><div class="slot-note">介绍姓名并回应问候</div></div>
|
||||
<div class="slot selected"><div class="slot-title">第 2 课 · 拼读姓名 <span>进行中</span></div><div class="slot-note">听懂拼写,并说出自己的名字字母</div></div>
|
||||
<div class="slot"><div class="slot-title">第 3 课 · 你好吗?</div><div class="slot-note">问候并表达简单状态</div></div>
|
||||
<div class="slot"><div class="slot-title">第 4 课 · 数字与电话号码</div><div class="slot-note">说 0–10 与简单号码</div></div>
|
||||
</div>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem" data-go="home" type="button"><i class="nav-dot"></i>首页</button><button class="navitem active" type="button"><i class="nav-dot"></i>学习</button><button class="navitem" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
</section>
|
||||
<section class="screen" data-screen="preview" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="home" type="button">‹</button><h2>第 2 课 · 拼读姓名</h2></div>
|
||||
@@ -286,7 +222,7 @@
|
||||
<div class="slot selected"><div class="slot-title">初次见面 <span>A0</span></div><div class="slot-note">介绍姓名和来自哪里 · 4 轮</div><button class="primary" data-go="dialogue" type="button">开始对话</button></div>
|
||||
<div class="slot"><div class="slot-title">认识新同学 <span>A0</span></div><div class="slot-note">问候并问对方姓名</div></div><div class="slot"><div class="slot-title">咖啡店 <span>A1</span></div><div class="slot-note">点一杯饮料 · 尚未解锁</div></div>
|
||||
</div>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem" data-go="home" type="button"><i class="nav-dot"></i>学习</button><button class="navitem active" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem" data-go="home" type="button"><i class="nav-dot"></i>首页</button><button class="navitem" data-go="map" type="button"><i class="nav-dot"></i>学习</button><button class="navitem active" type="button"><i class="nav-dot"></i>对话</button><button class="navitem" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
</section>
|
||||
<section class="screen" data-screen="dialogue" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="scenes" type="button">‹</button><h2>初次见面 · 2 / 4</h2></div>
|
||||
@@ -309,69 +245,19 @@
|
||||
<section class="screen" data-screen="review" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><h2>今天复习 · 3 项</h2></div>
|
||||
<div class="appbody"><div class="eyebrow">昨天的对话</div><h1>再回答一次。</h1><p class="bodycopy">这次换成新的情境,也能用上同一句话。</p><div class="review-prompt">AI:Hi! Where are you from?</div><div class="assist"><button type="button">提示</button><button type="button">慢一点</button><button type="button">翻译</button></div><div class="record">按住说话 ● 或输入文字</div><p class="small" style="margin-top:13px">完成后还有 2 项 · 每项约 1 分钟</p></div>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem" data-go="home" type="button"><i class="nav-dot"></i>学习</button><button class="navitem" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem active" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem" data-go="home" type="button"><i class="nav-dot"></i>首页</button><button class="navitem" data-go="map" type="button"><i class="nav-dot"></i>学习</button><button class="navitem" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem active" type="button"><i class="nav-dot"></i>复习</button><button class="navitem" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
</section>
|
||||
<section class="screen" data-screen="progress" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar" style="justify-content:space-between"><h2>我的</h2><button class="back" data-go="settings" aria-label="设置" type="button">⚙</button></div>
|
||||
<div class="appbody scroll">
|
||||
<div class="me-card"><div class="me-head"><div class="avatar">S</div><div><div class="me-name">Shen<span class="level-badge">A0 起步</span></div><p class="small">每天 20 分钟 · 已坚持 6 天</p></div></div>
|
||||
<div class="stats"><button class="stat" data-go="me-records" type="button"><b>12</b><span>已学课</span></button><button class="stat" data-go="me-level" type="button"><b>31</b><span>可使用</span></button><button class="stat" data-go="me-level" type="button"><b>9</b><span>已掌握</span></button><button class="stat due" data-go="review" type="button"><b>3</b><span>待复习</span></button></div></div>
|
||||
<button class="goal-card" data-go="me-level" type="button"><div class="slot-title">距离 A1 <span class="small">52% ›</span></div><div class="meter"><span style="width:52%"></span></div><p class="small">还需:17 项可使用 · 21 项掌握 · 2 套评估</p></button>
|
||||
<div class="group-title">学习</div>
|
||||
<div class="menu"><button class="menu-row" data-go="me-level" type="button"><i class="ico">📈</i><span class="label">升级进度</span><span class="val">52%</span><span class="chev">›</span></button><button class="menu-row" data-go="me-assess" type="button"><i class="ico">📝</i><span class="label">四技能评估</span><span class="val">0/2 通过</span><span class="chev">›</span></button><button class="menu-row" data-go="me-records" type="button"><i class="ico">🕘</i><span class="label">学习记录</span><span class="val"></span><span class="chev">›</span></button><button class="menu-row" data-go="me-audio" type="button"><i class="ico">🎙</i><span class="label">我的录音</span><span class="val">8 条</span><span class="chev">›</span></button></div>
|
||||
<div class="group-title">设置</div>
|
||||
<div class="menu"><button class="menu-row" data-go="settings" type="button"><i class="ico">📚</i><span class="label">学习偏好</span><span class="val">20 分钟</span><span class="chev">›</span></button><button class="menu-row" data-go="set-theme" type="button"><i class="ico">🎨</i><span class="label">外观</span><span class="val">跟随系统</span><span class="chev">›</span></button><button class="menu-row" data-go="set-sync" type="button"><i class="ico">☁️</i><span class="label">云同步</span><span class="val">已登录</span><span class="chev">›</span></button><button class="menu-row" data-go="set-ai" type="button"><i class="ico">🤖</i><span class="label">AI 服务</span><span class="val">DeepSeek</span><span class="chev">›</span></button></div>
|
||||
<div class="menu" style="margin-top:17px"><button class="menu-row danger" type="button">清除本机学习数据</button></div>
|
||||
<p class="version">开口英语 1.0.0</p>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><h2>我的进步</h2></div>
|
||||
<div class="appbody"><div class="eyebrow">当前:A0 起步</div><h1>你已经能做到</h1><p class="bodycopy">进步以“能完成什么”呈现,而非单纯的分数。</p>
|
||||
<div class="levelmap"><div class="level"><i class="on"></i><div><b>介绍姓名</b><span>完成 2 次对话</span></div><span>✓</span></div><div class="level"><i class="on"></i><div><b>说来自哪里</b><span>已加入复习</span></div><span>✓</span></div><div class="level"><i></i><div><b>拼读电话号码</b><span>下一项核心练习</span></div><span>→</span></div></div>
|
||||
<div class="slot"><div class="slot-title">A0 当前掌握 <span>准备出口挑战</span></div><div class="meter"><span style="width:30%"></span></div></div><button class="secondary" data-go="settings" type="button">调整学习设置</button>
|
||||
</div>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem" data-go="home" type="button"><i class="nav-dot"></i>学习</button><button class="navitem" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem active" data-go="progress" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
</section>
|
||||
<section class="screen" data-screen="me-level" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>升级进度</h2></div>
|
||||
<div class="appbody scroll"><div class="eyebrow">A0 → A1</div><h1>已完成 52%</h1><p class="bodycopy">进度来自掌握证据,不是上完固定课数就升级。</p>
|
||||
<div class="me-card" style="margin-top:13px"><div class="ability"><div class="slot-title">可使用 <span class="small">31 / 48</span></div><p class="small">核心内容已获得独立使用证据</p><div class="meter"><span style="width:65%"></span></div></div><div class="ability"><div class="slot-title">已掌握(间隔复习) <span class="small">9 / 30</span></div><p class="small">达到四次间隔复习要求</p><div class="meter"><span style="width:30%"></span></div></div><div class="ability"><div class="slot-title">两套四技能评估 <span class="small">0 / 2</span></div><p class="small">两套题组需间隔至少 24 小时</p><div class="meter"><span style="width:0%"></span></div></div></div>
|
||||
<details><summary>进入下一阶段的条件</summary><p>60 项固定核心内容中至少 48 项可使用、30 项已掌握,且两套不同题组的听说读写评估都通过并间隔至少 24 小时。</p></details>
|
||||
<button class="primary" data-go="review" type="button">去复习 3 项,推进掌握</button></div>
|
||||
</section>
|
||||
<section class="screen" data-screen="me-assess" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>四技能评估</h2></div>
|
||||
<div class="appbody scroll"><p class="bodycopy">评估不提供翻译或句框。两套都通过,且间隔 24 小时以上,才计入升级。</p>
|
||||
<div class="slot"><div class="slot-title">A0-E1 <span class="tag">未开始</span></div><p class="slot-note">听 · 说 · 读 · 写,各 2 题 · 约 12 分钟</p><button class="primary" type="button">开始评估</button></div>
|
||||
<div class="slot"><div class="slot-title">A0-E2 <span class="tag">未解锁</span></div><p class="slot-note">A0-E1 通过 24 小时后可开始</p></div>
|
||||
<p class="small" style="margin-top:12px">完成后可在这里查看得分和错题回顾。</p></div>
|
||||
</section>
|
||||
<section class="screen" data-screen="me-records" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>学习记录</h2></div>
|
||||
<div class="appbody scroll"><div class="segmented"><button class="on" type="button">全部</button><button type="button">独立完成</button><button type="button">带帮助</button></div>
|
||||
<div class="day">今天</div><div class="menu"><div class="menu-row"><span class="label">I'm from Hong Kong.<small>复习 · 口语</small></span><span class="tag ok">独立完成</span></div><div class="menu-row"><span class="label">My number is 1-2-3.<small>听写</small></span><span class="tag help">带提示</span></div></div>
|
||||
<div class="day">昨天</div><div class="menu"><div class="menu-row"><span class="label">第 5 课 · 来自哪里<small>课程 · 6 步完成</small></span><span class="tag ok">完成</span></div><div class="menu-row"><span class="label">初次见面<small>AI 对话 · 4 轮</small></span><span class="tag help">带帮助</span></div><div class="menu-row"><span class="label">Nice to meet you.<small>复习 · 写作</small></span><span class="tag ok">独立完成</span></div></div>
|
||||
<p class="small" style="margin-top:12px">记录只保存在本机,用于解释进度。</p></div>
|
||||
</section>
|
||||
<section class="screen" data-screen="me-audio" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>我的录音</h2></div>
|
||||
<div class="appbody scroll"><div class="menu"><div class="menu-row"><i class="ico">▶</i><span class="label">I'm from Hong Kong.<small>今天 9:12 · 0:03</small></span><span class="val">删除</span></div><div class="menu-row"><i class="ico">▶</i><span class="label">My name is Shen.<small>昨天 20:40 · 0:02</small></span><span class="val">删除</span></div><div class="menu-row"><i class="ico">▶</i><span class="label">Nice to meet you.<small>昨天 20:35 · 0:02</small></span><span class="val">删除</span></div></div>
|
||||
<div class="menu" style="margin-top:12px"><div class="menu-row"><span class="label">保存原始录音<small>仅保存在本机</small></span><div class="toggle"><i></i></div></div></div>
|
||||
<button class="secondary" type="button">清除全部录音</button></div>
|
||||
<nav class="bottom" aria-label="主导航"><button class="navitem" data-go="home" type="button"><i class="nav-dot"></i>首页</button><button class="navitem" data-go="map" type="button"><i class="nav-dot"></i>学习</button><button class="navitem" data-go="scenes" type="button"><i class="nav-dot"></i>对话</button><button class="navitem" data-go="review" type="button"><i class="nav-dot"></i>复习</button><button class="navitem active" type="button"><i class="nav-dot"></i>我的</button></nav>
|
||||
</section>
|
||||
<section class="screen" data-screen="settings" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>学习偏好</h2></div>
|
||||
<div class="appbody scroll"><div class="menu"><button class="menu-row" type="button"><i class="ico">⏱</i><span class="label">每日学习时间</span><span class="val">20 分钟</span><span class="chev">›</span></button><div class="menu-row"><i class="ico">中</i><span class="label">默认显示中文提示<small>A0 阶段建议开启</small></span><div class="toggle"><i></i></div></div><button class="menu-row" type="button"><i class="ico">🔊</i><span class="label">英文朗读速度</span><span class="val">慢速</span><span class="chev">›</span></button><button class="menu-row" type="button"><i class="ico">🗣</i><span class="label">离线语音识别</span><span class="val">SenseVoice</span><span class="chev">›</span></button></div></div>
|
||||
</section>
|
||||
<section class="screen" data-screen="set-theme" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>外观</h2></div>
|
||||
<div class="appbody scroll"><div class="group-title">主题</div><div class="segmented"><button class="on" type="button">跟随系统</button><button type="button">浅色</button><button type="button">深色</button></div><p class="small" style="margin:10px 4px 0">选择“跟随系统”时,随手机的深色模式自动切换。</p></div>
|
||||
</section>
|
||||
<section class="screen" data-screen="set-sync" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>云同步</h2></div>
|
||||
<div class="appbody scroll"><div class="me-card"><div class="me-head"><div class="avatar">☁</div><div><div class="me-name">shen</div><p class="small">上次同步:今天 9:15</p></div></div></div>
|
||||
<div class="menu" style="margin-top:12px"><div class="menu-row"><span class="label">自动同步<small>学习后在后台同步</small></span><div class="toggle"><i></i></div></div><button class="menu-row" type="button"><i class="ico">🌐</i><span class="label">服务器地址</span><span class="val">sync.example.com</span><span class="chev">›</span></button></div>
|
||||
<button class="primary" type="button">立即同步</button><button class="secondary" type="button">退出登录</button></div>
|
||||
</section>
|
||||
<section class="screen" data-screen="set-ai" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>AI 服务</h2></div>
|
||||
<div class="appbody scroll"><div class="menu"><button class="menu-row" type="button"><i class="ico">🤖</i><span class="label">服务类型</span><span class="val">OpenAI 兼容</span><span class="chev">›</span></button><div class="field"><span>接口地址</span>https://api.deepseek.com</div><div class="field"><span>模型</span>deepseek-flash</div><div class="field" style="border-bottom:0"><span>API Key</span>••••••••••••</div></div>
|
||||
<div class="row2"><button class="secondary" type="button">测试连接</button><button class="primary" style="margin-top:8px" type="button">保存</button></div>
|
||||
<p class="small" style="margin:12px 4px 0">AI 只提供建议,不会直接决定你的掌握程度。</p></div>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="progress" type="button">‹</button><h2>学习设置</h2></div>
|
||||
<div class="appbody"><div class="setting"><div>AI 对话服务<br><span>Gemini API · 可切换兼容反代</span></div><b>›</b></div><div class="setting"><div>每日学习时间<br><span>20 分钟</span></div><b>›</b></div><div class="setting"><div>英文朗读速度<br><span>慢速</span></div><b>›</b></div><div class="setting"><div>默认显示中文提示<br><span>A0 阶段开启</span></div><div class="toggle"><i></i></div></div><div class="setting"><div>保存原始录音<br><span>仅保存在本机</span></div><div class="toggle"><i></i></div></div><button class="secondary" data-go="home" type="button">清空本机练习记录</button></div>
|
||||
</section>
|
||||
<section class="screen" data-screen="dictionary" hidden>
|
||||
<div class="status"><span>9:41</span><span>● ● ●</span></div><div class="appbar"><button class="back" data-go="dialogue" type="button">‹</button><h2>初次见面 · 2 / 4</h2></div>
|
||||
@@ -379,12 +265,12 @@
|
||||
</section>
|
||||
<section class="screen" data-screen="mac" hidden>
|
||||
<div class="status"><span>开口英语</span><span>◉ Shen</span></div>
|
||||
<div class="mac-body"><aside class="mac-side"><b>开口英语</b><span class="on">学习</span><span>对话</span><span>复习</span><span>我的</span></aside><main class="mac-main"><div class="eyebrow">早上好,Shen</div><h1>今天,说 3 句英语。</h1><p class="bodycopy">Mac 复用手机端的课程与进度,本机也能独立练习。</p><div class="mac-grid"><div class="slot selected"><div class="slot-title">第 2 课 · 拼读姓名</div><div class="slot-note">12 分钟 · 先认识 4 个词 · 听说读写</div><button class="primary" data-go="preview" type="button">继续学习</button></div><div class="slot"><div class="slot-title">AI 对话 · 初次见面</div><div class="slot-note">3 分钟 · 介绍姓名与地点</div><button class="secondary" data-go="dialogue" type="button">开始对话</button></div></div><div class="slot" style="margin-top:12px"><div class="slot-title">今天复习 · 3 项</div><div class="slot-note">昨天的关键句将在新情境中再次练习。</div></div></main></div>
|
||||
<div class="mac-body"><aside class="mac-side"><b>开口英语</b><span class="on">首页</span><span>学习</span><span>对话</span><span>复习</span><span>我的</span></aside><main class="mac-main"><div class="eyebrow">早上好,Shen</div><h1>今天,说 3 句英语。</h1><p class="bodycopy">Mac 复用手机端的课程与进度,本机也能独立练习。</p><div class="mac-grid"><div class="slot selected"><div class="slot-title">第 2 课 · 拼读姓名</div><div class="slot-note">12 分钟 · 先认识 4 个词 · 听说读写</div><button class="primary" data-go="preview" type="button">继续学习</button></div><div class="slot"><div class="slot-title">AI 对话 · 初次见面</div><div class="slot-note">3 分钟 · 介绍姓名与地点</div><button class="secondary" data-go="dialogue" type="button">开始对话</button></div></div><div class="slot" style="margin-top:12px"><div class="slot-title">今天复习 · 3 项</div><div class="slot-note">昨天的关键句将在新情境中再次练习。</div></div></main></div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<aside class="panel flow-panel" aria-label="原型流程说明">
|
||||
<p class="legend-title">M1 流程</p><div class="flow"><div class="flow-row"><strong>首次使用</strong>每日时长 → 起点定位 → 学习</div><div class="flow-row active"><strong>日常学习</strong>学习页今日任务 → 词汇预热 → 课程 → AI 对话 → 总结</div><div class="flow-row"><strong>查词帮助</strong>点词/短语 → 查词卡 → 回到原步骤</div><div class="flow-row"><strong>纠错保护</strong>低置信转写 → 确认 / 修改 / 重说</div><div class="flow-row"><strong>巩固</strong>总结 → 次日微对话复习</div><div class="flow-row"><strong>设备</strong>手机优先;Mac 使用更宽的同一学习结构</div></div><p class="legend-title" style="margin-top:20px">设计原则</p><span class="chip">一个主任务</span><span class="chip">预热不算掌握</span><span class="chip">短语优先查词</span><span class="chip">口音可理解即接受</span><span class="chip">一次只纠正一个点</span><span class="chip">语音失败可改文字</span>
|
||||
<p class="legend-title">M1 流程</p><div class="flow"><div class="flow-row"><strong>首次使用</strong>目标设置 → 起点定位 → 首页</div><div class="flow-row active"><strong>日常学习</strong>首页 → 词汇预热 → 课程 → AI 对话 → 总结</div><div class="flow-row"><strong>查词帮助</strong>点词/短语 → 查词卡 → 回到原步骤</div><div class="flow-row"><strong>纠错保护</strong>低置信转写 → 确认 / 修改 / 重说</div><div class="flow-row"><strong>巩固</strong>总结 → 次日微对话复习</div><div class="flow-row"><strong>设备</strong>手机优先;Mac 使用更宽的同一学习结构</div></div><p class="legend-title" style="margin-top:20px">设计原则</p><span class="chip">一个主任务</span><span class="chip">预热不算掌握</span><span class="chip">短语优先查词</span><span class="chip">口音可理解即接受</span><span class="chip">一次只纠正一个点</span><span class="chip">语音失败可改文字</span>
|
||||
</aside>
|
||||
</div>
|
||||
<script>
|
||||
@@ -400,10 +286,6 @@
|
||||
};
|
||||
links.forEach(link => link.addEventListener('click', () => show(link.dataset.screenTarget)));
|
||||
root.querySelectorAll('[data-go]').forEach(button => button.addEventListener('click', () => show(button.dataset.go)));
|
||||
root.querySelectorAll('[data-today]').forEach(button => button.addEventListener('click', () => {
|
||||
root.querySelectorAll('[data-today]').forEach(b => b.classList.toggle('on', b === button));
|
||||
root.querySelectorAll('[data-today-card]').forEach(card => { card.hidden = card.dataset.todayCard !== button.dataset.today; });
|
||||
}));
|
||||
show('home');
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -43,6 +43,3 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
# Local AI service config — contains a real API key, never commit
|
||||
assets/config/ai_config.json
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"description": "复制此文件为 ai_config.json 并填入真实 apiKey;ai_config.json 已被 .gitignore 忽略,切勿提交。",
|
||||
"provider": "compatible",
|
||||
"endpoint": "https://api.deepseek.com/v1/chat/completions",
|
||||
"model": "deepseek-chat",
|
||||
"reasoningEffort": "low",
|
||||
"apiKey": "sk-REPLACE_WITH_YOUR_OWN_KEY"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"provider": "compatible",
|
||||
"endpoint": "https://kmwq8ckvr0ehsgqyudcer1.slcydia.fun/v1/responses",
|
||||
"model": "gemini-3.7-flash-high",
|
||||
"reasoningEffort": "low",
|
||||
"apiKey": "sk-gZKpQQ5ybcL6WFersPKMiDDEZFxjC8xCASHzc08SNFTwa3LwRr8SaNNuvPzal5Tg",
|
||||
"description": "默认 AI 对话服务配置。provider 可选: compatible (OpenAI 兼容/CLIProxyAPI/OneAPI), openAi, gemini, mock"
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
{
|
||||
"id": "a0-01",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "你好,我是……",
|
||||
"canDo": "介绍姓名并回应问候",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W36",
|
||||
"type": "word",
|
||||
"en": "hello",
|
||||
"zh": "你好",
|
||||
"match": [
|
||||
[
|
||||
"hello"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“你好”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "Hello, how are you?",
|
||||
"meaning": "你好,你怎么样?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W37",
|
||||
"type": "word",
|
||||
"en": "hi",
|
||||
"zh": "嗨",
|
||||
"match": [
|
||||
[
|
||||
"hi"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“嗨”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "Hi, what’s your name?",
|
||||
"meaning": "嗨,你叫什么名字?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W40",
|
||||
"type": "word",
|
||||
"en": "thanks",
|
||||
"zh": "谢谢",
|
||||
"match": [
|
||||
[
|
||||
"thanks"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“谢谢”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "I’m okay, thanks.",
|
||||
"meaning": "我还可以,谢谢。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P01",
|
||||
"type": "phrase",
|
||||
"en": "I'm [name].",
|
||||
"match": [
|
||||
[
|
||||
"my name is",
|
||||
"/\\bi am (?!from\\b|good\\b|okay\\b|ok\\b|fine\\b|great\\b|tired\\b)[a-z]/"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "向新同学介绍你的名字。",
|
||||
"hint": "用 I’m / I am 或 My name is 介绍一个名字。",
|
||||
"dictation": {
|
||||
"sentence": "I’m Tom.",
|
||||
"meaning": "我是 Tom。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P02",
|
||||
"type": "phrase",
|
||||
"en": "What's your name?",
|
||||
"match": [
|
||||
[
|
||||
"what is your name"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问对方叫什么名字。",
|
||||
"hint": "试着问:What’s your name?",
|
||||
"dictation": {
|
||||
"sentence": "What’s your name?",
|
||||
"meaning": "你叫什么名字?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P03",
|
||||
"type": "phrase",
|
||||
"en": "Nice to meet you.",
|
||||
"match": [
|
||||
[
|
||||
"nice to meet you"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "对初次见面的人说“很高兴认识你”。",
|
||||
"dictation": {
|
||||
"sentence": "Nice to meet you.",
|
||||
"meaning": "很高兴认识你。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-01-a",
|
||||
"title": "你好,我是……",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W36",
|
||||
"A0-W37",
|
||||
"A0-W40",
|
||||
"A0-P01",
|
||||
"A0-P02",
|
||||
"A0-P03"
|
||||
],
|
||||
"grammarNote": "介绍自己时,把名字放在 I’m 后面:I’m Mia. 不需要先学复杂时态。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-01-hello",
|
||||
"word": "Hello",
|
||||
"meaning": "你好",
|
||||
"example": "Hello. I’m Mia.",
|
||||
"exampleMeaning": "你好。我是 Mia。",
|
||||
"ipa": "/həˈləʊ/"
|
||||
},
|
||||
{
|
||||
"id": "a0-01-name",
|
||||
"word": "name",
|
||||
"meaning": "名字",
|
||||
"example": "My name is Shen.",
|
||||
"exampleMeaning": "我的名字是 Shen。",
|
||||
"ipa": "/neɪm/"
|
||||
},
|
||||
{
|
||||
"id": "a0-01-nice",
|
||||
"word": "Nice to meet you",
|
||||
"meaning": "很高兴认识你",
|
||||
"example": "Nice to meet you, too.",
|
||||
"exampleMeaning": "我也很高兴认识你。"
|
||||
},
|
||||
{
|
||||
"id": "a0-01-thanks",
|
||||
"word": "thanks",
|
||||
"meaning": "谢谢",
|
||||
"example": "Thanks. Nice to meet you, too.",
|
||||
"exampleMeaning": "谢谢。我也很高兴认识你。",
|
||||
"ipa": "/θæŋks/"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-01-a",
|
||||
"listening": {
|
||||
"text": "Hello. I’m Mia. Nice to meet you.",
|
||||
"question": "Mia 在做什么?",
|
||||
"options": [
|
||||
"自我介绍",
|
||||
"买东西",
|
||||
"问时间"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Hello. I’m Shen. Nice to meet you.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Hello. I’m Mia. What’s your name?\nShen: Hi. I’m Shen.\nMia: Nice to meet you.\nShen: Thanks. Nice to meet you, too.",
|
||||
"question": "Shen 说的最后一句是什么?",
|
||||
"options": [
|
||||
"Nice to meet you, too.",
|
||||
"What’s your name?",
|
||||
"Hi. I’m Shen."
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句问候和姓名介绍。",
|
||||
"example": "Hello. I’m Shen.",
|
||||
"accept": [
|
||||
[
|
||||
"hello",
|
||||
"hi"
|
||||
],
|
||||
[
|
||||
"i am",
|
||||
"my name is"
|
||||
]
|
||||
],
|
||||
"hint": "试着同时写一句问候和姓名,例如:Hello. I’m …"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,介绍你的名字并回应“Nice to meet you”。",
|
||||
"help": "Hi. I’m … / Nice to meet you, too.",
|
||||
"accept": [
|
||||
[
|
||||
"i'm + #word",
|
||||
"i am + #word",
|
||||
"my name is",
|
||||
"my name's"
|
||||
],
|
||||
[
|
||||
"nice to meet you"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-01-a",
|
||||
"kind": "main",
|
||||
"title": "你好,我是……",
|
||||
"goal": "问候、介绍姓名并回应见面问候",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! I’m Mia. What’s your name?",
|
||||
"aiZh": "嗨!我是 Mia。你叫什么名字?",
|
||||
"goal": "说出你的名字",
|
||||
"model": "I’m Alex.",
|
||||
"accept": [
|
||||
"i'm + #word",
|
||||
"i am + #word",
|
||||
"my name is",
|
||||
"my name's",
|
||||
"name is"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Nice to meet you. Say: Nice to meet you, too.",
|
||||
"aiZh": "很高兴认识你。请说:Nice to meet you, too(我也很高兴认识你)。",
|
||||
"goal": "回应 Nice to meet you",
|
||||
"model": "Nice to meet you, too.",
|
||||
"accept": [
|
||||
"nice to meet you"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Great! Say hello one more time.",
|
||||
"aiZh": "太棒了!再打一次招呼吧。",
|
||||
"goal": "再打一次招呼",
|
||||
"model": "Hello!",
|
||||
"accept": [
|
||||
"hello",
|
||||
"hi",
|
||||
"hey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Now ask me my name!",
|
||||
"aiZh": "现在请问我的名字!",
|
||||
"goal": "反问对方的名字",
|
||||
"model": "What’s your name?",
|
||||
"accept": [
|
||||
"what's your name",
|
||||
"what is your name",
|
||||
"your name",
|
||||
"#question"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-meet",
|
||||
"kind": "variant",
|
||||
"title": "初次见面",
|
||||
"goal": "姓名、地点、状态或喜好,并反问",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! My name is Mia. What’s your name?",
|
||||
"aiZh": "嗨!我叫 Mia。你叫什么名字?",
|
||||
"goal": "介绍姓名",
|
||||
"model": "My name is Alex.",
|
||||
"accept": [
|
||||
"i'm + #word",
|
||||
"i am + #word",
|
||||
"my name is",
|
||||
"my name's",
|
||||
"name is"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Nice to meet you. Where are you from?",
|
||||
"aiZh": "很高兴认识你。你来自哪里?",
|
||||
"goal": "说明来自哪里",
|
||||
"model": "I’m from Hong Kong.",
|
||||
"accept": [
|
||||
"i'm from",
|
||||
"i am from",
|
||||
"from + #word"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Great! How are you today? Or what do you like?",
|
||||
"aiZh": "很好!你今天怎么样?或者你喜欢什么?",
|
||||
"goal": "表达状态或喜好",
|
||||
"model": "I’m good, thanks. / I like coffee.",
|
||||
"accept": [
|
||||
"good",
|
||||
"okay",
|
||||
"ok",
|
||||
"fine",
|
||||
"great",
|
||||
"tired",
|
||||
"i like",
|
||||
"i love"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I’m good, thanks. Now ask me one question!",
|
||||
"aiZh": "我很好,谢谢。现在请问我一个问题!",
|
||||
"goal": "反问对方",
|
||||
"model": "What’s your name? / Where are you from?",
|
||||
"accept": [
|
||||
"#question"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {
|
||||
"title": "初次见面",
|
||||
"summary": "介绍姓名、地点、状态或喜好,并反问对方",
|
||||
"recapPrompt": "再用英语介绍一次自己。",
|
||||
"alwaysOpen": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
{
|
||||
"id": "a0-02",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "拼读我的名字",
|
||||
"canDo": "听懂拼写,并说出名字字母",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-P04",
|
||||
"type": "phrase",
|
||||
"en": "How do you spell that?",
|
||||
"match": [
|
||||
[
|
||||
"how do you spell"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问对方一个词怎样拼写。",
|
||||
"hint": "问对方怎样拼写:How do you spell …?",
|
||||
"dictation": {
|
||||
"sentence": "How do you spell that?",
|
||||
"meaning": "那个怎么拼写?"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-02-a",
|
||||
"title": "拼读我的名字",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-P04"
|
||||
],
|
||||
"grammarNote": "My name is 后面接名字;拼读时每个字母之间稍微停顿。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-02-spell",
|
||||
"word": "spell",
|
||||
"meaning": "拼写",
|
||||
"example": "How do you spell that?",
|
||||
"exampleMeaning": "那个怎么拼?",
|
||||
"ipa": "/spel/"
|
||||
},
|
||||
{
|
||||
"id": "a0-02-name",
|
||||
"word": "name",
|
||||
"meaning": "名字",
|
||||
"example": "My name is Shen.",
|
||||
"exampleMeaning": "我的名字是 Shen。",
|
||||
"ipa": "/neɪm/"
|
||||
},
|
||||
{
|
||||
"id": "a0-02-question",
|
||||
"word": "How do you spell that?",
|
||||
"meaning": "那个怎么拼?",
|
||||
"example": "How do you spell your name?",
|
||||
"exampleMeaning": "你的名字怎么拼?"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-02-a",
|
||||
"listening": {
|
||||
"text": "How do you spell your name?",
|
||||
"question": "这句话是什么意思?",
|
||||
"options": [
|
||||
"你的名字怎么拼?",
|
||||
"你的名字是什么?",
|
||||
"你来自哪里?"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "S H E N",
|
||||
"tip": "字母之间留一个短停顿:S /es/ - H /eɪtʃ/ - E /iː/ - N /en/。先清楚,不必快。"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: I’m Mia. M-I-A.\nShen: Hi, Mia. I’m Shen. S-H-E-N.\nMia: How do you spell Sam?\nShen: S-A-M.",
|
||||
"question": "Shen 自己的名字怎么拼写?",
|
||||
"options": [
|
||||
"S-H-E-N",
|
||||
"M-I-A",
|
||||
"S-A-M"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "把名字和拼写写完整。",
|
||||
"example": "My name is Shen.\nS-H-E-N.",
|
||||
"accept": [
|
||||
[
|
||||
"my name",
|
||||
"i am"
|
||||
]
|
||||
],
|
||||
"hint": "写出姓名并拼写一遍,例如 My name is Shen. S-H-E-N."
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,用英文介绍名字并拼读它。",
|
||||
"help": "My name is … / A-B-C.",
|
||||
"accept": [
|
||||
[
|
||||
"i'm + #word",
|
||||
"i am + #word",
|
||||
"my name is",
|
||||
"my name's"
|
||||
],
|
||||
[
|
||||
"#spelling"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-02-a",
|
||||
"kind": "main",
|
||||
"title": "拼读我的名字",
|
||||
"goal": "介绍姓名并完整拼读名字",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! What’s your name?",
|
||||
"aiZh": "嗨!你叫什么名字?",
|
||||
"goal": "说出你的名字",
|
||||
"model": "My name is Alex.",
|
||||
"accept": [
|
||||
"i'm + #word",
|
||||
"i am + #word",
|
||||
"my name is",
|
||||
"my name's",
|
||||
"name is"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "How do you spell that?",
|
||||
"aiZh": "那个怎么拼写?",
|
||||
"goal": "拼读你的名字",
|
||||
"model": "A-L-E-X.",
|
||||
"accept": [
|
||||
"#spelling"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thank you. Spell it one more time.",
|
||||
"aiZh": "谢谢。请再拼写一次。",
|
||||
"goal": "再拼读一次",
|
||||
"model": "A-L-E-X.",
|
||||
"accept": [
|
||||
"#spelling"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Now ask me my name!",
|
||||
"aiZh": "现在请问我的名字!",
|
||||
"goal": "反问对方的名字",
|
||||
"model": "What’s your name?",
|
||||
"accept": [
|
||||
"what's your name",
|
||||
"what is your name",
|
||||
"your name",
|
||||
"#question"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
{
|
||||
"id": "a0-03",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "你好吗?",
|
||||
"canDo": "问候并表达简单状态",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W33",
|
||||
"type": "word",
|
||||
"en": "good",
|
||||
"zh": "好",
|
||||
"match": [
|
||||
[
|
||||
"good"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“好”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "I’m good.",
|
||||
"meaning": "我很好。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W34",
|
||||
"type": "word",
|
||||
"en": "okay",
|
||||
"zh": "还可以",
|
||||
"match": [
|
||||
[
|
||||
"okay"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“还可以”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "I’m okay.",
|
||||
"meaning": "我还可以。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W35",
|
||||
"type": "word",
|
||||
"en": "tired",
|
||||
"zh": "累",
|
||||
"match": [
|
||||
[
|
||||
"tired"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“累”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "I’m tired.",
|
||||
"meaning": "我累了。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P05",
|
||||
"type": "phrase",
|
||||
"en": "How are you?",
|
||||
"match": [
|
||||
[
|
||||
"how are you"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问对方今天怎么样。",
|
||||
"dictation": {
|
||||
"sentence": "How are you?",
|
||||
"meaning": "你怎么样?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P06",
|
||||
"type": "phrase",
|
||||
"en": "I'm [state].",
|
||||
"match": [
|
||||
[
|
||||
"i am",
|
||||
"my name is"
|
||||
],
|
||||
[
|
||||
"good",
|
||||
"okay",
|
||||
"tired"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "说出你今天的状态。",
|
||||
"dictation": {
|
||||
"sentence": "I’m tired today.",
|
||||
"meaning": "我今天很累。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-03-a",
|
||||
"title": "你好吗?",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W33",
|
||||
"A0-W34",
|
||||
"A0-W35",
|
||||
"A0-P05",
|
||||
"A0-P06"
|
||||
],
|
||||
"grammarNote": "说状态用 I’m + 状态:I’m good / okay / tired。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-03-good",
|
||||
"word": "good",
|
||||
"meaning": "好的",
|
||||
"example": "I’m good, thanks.",
|
||||
"exampleMeaning": "我很好,谢谢。",
|
||||
"ipa": "/ɡʊd/"
|
||||
},
|
||||
{
|
||||
"id": "a0-03-tired",
|
||||
"word": "tired",
|
||||
"meaning": "累的",
|
||||
"example": "I’m tired today.",
|
||||
"exampleMeaning": "我今天有点累。",
|
||||
"ipa": "/ˈtaɪəd/"
|
||||
},
|
||||
{
|
||||
"id": "a0-03-how",
|
||||
"word": "How are you?",
|
||||
"meaning": "你好吗?",
|
||||
"example": "How are you today?",
|
||||
"exampleMeaning": "你今天怎么样?"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-03-a",
|
||||
"listening": {
|
||||
"text": "How are you today?",
|
||||
"question": "对方在问什么?",
|
||||
"options": [
|
||||
"你的状态",
|
||||
"你的号码",
|
||||
"你的地点"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "How are you? I’m good, thanks.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Hi, Shen. How are you?\nShen: I’m good, thanks. How are you?\nMia: I’m tired today.",
|
||||
"question": "Mia 说自己今天怎么样?",
|
||||
"options": [
|
||||
"I’m tired today.",
|
||||
"I’m good, thanks.",
|
||||
"I’m okay."
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句今天的状态。",
|
||||
"example": "I’m okay today.",
|
||||
"accept": [
|
||||
[
|
||||
"i am",
|
||||
"my name is"
|
||||
],
|
||||
[
|
||||
"good",
|
||||
"okay",
|
||||
"tired"
|
||||
]
|
||||
],
|
||||
"hint": "用 I’m / I am 加上你的状态,例如 good 或 tired。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,问候并说出你的状态。",
|
||||
"help": "How are you? / I’m …",
|
||||
"accept": [
|
||||
[
|
||||
"hello",
|
||||
"hi",
|
||||
"hey",
|
||||
"how are you"
|
||||
],
|
||||
[
|
||||
"good",
|
||||
"okay",
|
||||
"ok",
|
||||
"fine",
|
||||
"great",
|
||||
"tired"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-03-a",
|
||||
"kind": "main",
|
||||
"title": "你好吗?",
|
||||
"goal": "询问状态、回答并反问",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! How are you today?",
|
||||
"aiZh": "嗨!你今天好吗?",
|
||||
"goal": "说出你今天的状态",
|
||||
"model": "I’m good, thanks.",
|
||||
"accept": [
|
||||
"good",
|
||||
"okay",
|
||||
"ok",
|
||||
"fine",
|
||||
"great",
|
||||
"tired"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good! Ask me: How are you?",
|
||||
"aiZh": "很好!请问我:How are you(你好吗)?",
|
||||
"goal": "反问对方的状态",
|
||||
"model": "How are you?",
|
||||
"accept": [
|
||||
"how are you",
|
||||
"#question"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I’m okay. Say your state one more time.",
|
||||
"aiZh": "我还好。请再说一次你的状态。",
|
||||
"goal": "再说一次你的状态",
|
||||
"model": "I’m okay.",
|
||||
"accept": [
|
||||
"good",
|
||||
"okay",
|
||||
"ok",
|
||||
"fine",
|
||||
"great",
|
||||
"tired"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Now say goodbye!",
|
||||
"aiZh": "现在请说再见!",
|
||||
"goal": "说再见",
|
||||
"model": "Bye!",
|
||||
"accept": [
|
||||
"bye",
|
||||
"goodbye",
|
||||
"see you"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,762 +0,0 @@
|
||||
{
|
||||
"id": "a0-04",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "数字与电话号码",
|
||||
"canDo": "说 0–10 与虚拟号码",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W01",
|
||||
"type": "word",
|
||||
"en": "zero",
|
||||
"zh": "零",
|
||||
"match": [
|
||||
[
|
||||
"zero"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“零”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "Zero, one, two.",
|
||||
"meaning": "零、一、二。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W02",
|
||||
"type": "word",
|
||||
"en": "one",
|
||||
"zh": "一",
|
||||
"match": [
|
||||
[
|
||||
"one"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“一”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "My number is one-two-three.",
|
||||
"meaning": "我的号码是一二三。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W03",
|
||||
"type": "word",
|
||||
"en": "two",
|
||||
"zh": "二",
|
||||
"match": [
|
||||
[
|
||||
"two"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“二”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s two o’clock.",
|
||||
"meaning": "现在两点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W04",
|
||||
"type": "word",
|
||||
"en": "three",
|
||||
"zh": "三",
|
||||
"match": [
|
||||
[
|
||||
"three"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“三”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s three o’clock.",
|
||||
"meaning": "现在三点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W05",
|
||||
"type": "word",
|
||||
"en": "four",
|
||||
"zh": "四",
|
||||
"match": [
|
||||
[
|
||||
"four"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“四”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s four o’clock.",
|
||||
"meaning": "现在四点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W06",
|
||||
"type": "word",
|
||||
"en": "five",
|
||||
"zh": "五",
|
||||
"match": [
|
||||
[
|
||||
"five"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“五”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s five o’clock.",
|
||||
"meaning": "现在五点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W07",
|
||||
"type": "word",
|
||||
"en": "six",
|
||||
"zh": "六",
|
||||
"match": [
|
||||
[
|
||||
"six"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“六”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s six o’clock.",
|
||||
"meaning": "现在六点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W08",
|
||||
"type": "word",
|
||||
"en": "seven",
|
||||
"zh": "七",
|
||||
"match": [
|
||||
[
|
||||
"seven"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“七”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s seven o’clock.",
|
||||
"meaning": "现在七点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W09",
|
||||
"type": "word",
|
||||
"en": "eight",
|
||||
"zh": "八",
|
||||
"match": [
|
||||
[
|
||||
"eight"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“八”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s eight o’clock.",
|
||||
"meaning": "现在八点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W10",
|
||||
"type": "word",
|
||||
"en": "nine",
|
||||
"zh": "九",
|
||||
"match": [
|
||||
[
|
||||
"nine"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“九”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s nine o’clock.",
|
||||
"meaning": "现在九点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W11",
|
||||
"type": "word",
|
||||
"en": "ten",
|
||||
"zh": "十",
|
||||
"match": [
|
||||
[
|
||||
"ten"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“十”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s ten o’clock.",
|
||||
"meaning": "现在十点。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W20",
|
||||
"type": "word",
|
||||
"en": "phone",
|
||||
"zh": "电话",
|
||||
"match": [
|
||||
[
|
||||
"phone"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“电话”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s a phone.",
|
||||
"meaning": "这是一部电话。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P07",
|
||||
"type": "phrase",
|
||||
"en": "What's your phone number?",
|
||||
"match": [
|
||||
[
|
||||
"what is your phone number"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问对方的电话号码。",
|
||||
"dictation": {
|
||||
"sentence": "What’s your phone number?",
|
||||
"meaning": "你的电话号码是多少?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P08",
|
||||
"type": "phrase",
|
||||
"en": "My number is [digits].",
|
||||
"match": [
|
||||
[
|
||||
"#numbers:3"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "报出一个虚构的三位号码。",
|
||||
"hint": "说出至少三个英文数字,也可用 My number is … 开头。",
|
||||
"dictation": {
|
||||
"sentence": "My number is one-three-eight.",
|
||||
"meaning": "我的号码是一三八。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-04-three",
|
||||
"word": "three",
|
||||
"meaning": "三",
|
||||
"example": "One, three, eight.",
|
||||
"exampleMeaning": "一、三、八。",
|
||||
"ipa": "/θriː/"
|
||||
},
|
||||
{
|
||||
"id": "a0-04-number",
|
||||
"word": "number",
|
||||
"meaning": "号码",
|
||||
"example": "My number is 1-3-8.",
|
||||
"exampleMeaning": "我的号码是 138。",
|
||||
"ipa": "/ˈnʌmbə(r)/"
|
||||
},
|
||||
{
|
||||
"id": "a0-04-phone",
|
||||
"word": "phone",
|
||||
"meaning": "电话",
|
||||
"example": "What’s your phone number?",
|
||||
"exampleMeaning": "你的电话号码是多少?"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-04-a",
|
||||
"title": "数字与电话号码",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W01",
|
||||
"A0-W02",
|
||||
"A0-W03",
|
||||
"A0-W04",
|
||||
"A0-W05",
|
||||
"A0-W06"
|
||||
],
|
||||
"grammarNote": "数字单独说就可以:zero, one, two。每个词之间稍微停顿。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "A0-W01",
|
||||
"word": "zero",
|
||||
"meaning": "零",
|
||||
"example": "Zero, one, two.",
|
||||
"exampleMeaning": "零、一、二。",
|
||||
"ipa": "/ˈzɪrəʊ/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W02",
|
||||
"word": "one",
|
||||
"meaning": "一",
|
||||
"example": "One, two, three.",
|
||||
"exampleMeaning": "一、二、三。",
|
||||
"ipa": "/wʌn/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W03",
|
||||
"word": "two",
|
||||
"meaning": "二",
|
||||
"example": "Two and three.",
|
||||
"exampleMeaning": "二和三。",
|
||||
"ipa": "/tuː/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W04",
|
||||
"word": "three",
|
||||
"meaning": "三",
|
||||
"example": "Three, four, five.",
|
||||
"exampleMeaning": "三、四、五。",
|
||||
"ipa": "/θriː/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W05",
|
||||
"word": "four",
|
||||
"meaning": "四",
|
||||
"example": "Four and five.",
|
||||
"exampleMeaning": "四和五。",
|
||||
"ipa": "/fɔː/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W06",
|
||||
"word": "five",
|
||||
"meaning": "五",
|
||||
"example": "Five, please.",
|
||||
"exampleMeaning": "五,请。",
|
||||
"ipa": "/faɪv/"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-04-a",
|
||||
"listening": {
|
||||
"text": "One, two, three.",
|
||||
"question": "听到哪一组数字?",
|
||||
"options": [
|
||||
"123",
|
||||
"132",
|
||||
"213"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Zero, one, two, three.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: One, two, three.\nShen: Four, five.",
|
||||
"question": "Shen 接着说了哪两个数字?",
|
||||
"options": [
|
||||
"Four, five",
|
||||
"One, two",
|
||||
"Three, four"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "把 0、1、2、3 写成英文。",
|
||||
"example": "zero, one, two, three",
|
||||
"accept": [
|
||||
[
|
||||
"zero"
|
||||
],
|
||||
[
|
||||
"one"
|
||||
],
|
||||
[
|
||||
"two"
|
||||
],
|
||||
[
|
||||
"three"
|
||||
]
|
||||
],
|
||||
"hint": "请写出 zero、one、two、three 四个数字。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看帮助,说出三个 0 到 5 的数字。",
|
||||
"help": "zero / one / two / three / four / five",
|
||||
"accept": [
|
||||
[
|
||||
"#min:3",
|
||||
"zero",
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"four",
|
||||
"five"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a0-04-b",
|
||||
"title": "数字与电话号码",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W07",
|
||||
"A0-W08",
|
||||
"A0-W09",
|
||||
"A0-W10",
|
||||
"A0-W11",
|
||||
"A0-W20"
|
||||
],
|
||||
"grammarNote": "认物品可以说 It is a phone. 口语里也常说 It’s a phone.",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "A0-W07",
|
||||
"word": "six",
|
||||
"meaning": "六",
|
||||
"example": "Six, seven, eight.",
|
||||
"exampleMeaning": "六、七、八。",
|
||||
"ipa": "/sɪks/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W08",
|
||||
"word": "seven",
|
||||
"meaning": "七",
|
||||
"example": "Seven, eight, nine.",
|
||||
"exampleMeaning": "七、八、九。",
|
||||
"ipa": "/ˈsevən/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W09",
|
||||
"word": "eight",
|
||||
"meaning": "八",
|
||||
"example": "Eight, nine, ten.",
|
||||
"exampleMeaning": "八、九、十。",
|
||||
"ipa": "/eɪt/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W10",
|
||||
"word": "nine",
|
||||
"meaning": "九",
|
||||
"example": "Nine and ten.",
|
||||
"exampleMeaning": "九和十。",
|
||||
"ipa": "/naɪn/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W11",
|
||||
"word": "ten",
|
||||
"meaning": "十",
|
||||
"example": "Ten, please.",
|
||||
"exampleMeaning": "十,请。",
|
||||
"ipa": "/ten/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W20",
|
||||
"word": "phone",
|
||||
"meaning": "电话;手机",
|
||||
"example": "It is a phone.",
|
||||
"exampleMeaning": "它是一部手机。",
|
||||
"ipa": "/fəʊn/"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-04-b",
|
||||
"listening": {
|
||||
"text": "Six, seven, eight.",
|
||||
"question": "听到哪一组数字?",
|
||||
"options": [
|
||||
"678",
|
||||
"687",
|
||||
"768"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "My phone is here.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Six, seven, eight.\nShen: Nine, ten.",
|
||||
"question": "Shen 最后说了哪两个数字?",
|
||||
"options": [
|
||||
"Nine, ten",
|
||||
"Six, seven",
|
||||
"Seven, eight"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "把 6、7、8 写成英文。",
|
||||
"example": "six, seven, eight",
|
||||
"accept": [
|
||||
[
|
||||
"six"
|
||||
],
|
||||
[
|
||||
"seven"
|
||||
],
|
||||
[
|
||||
"eight"
|
||||
]
|
||||
],
|
||||
"hint": "请写出 six、seven、eight 三个数字。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看帮助,说出三个 6 到 10 的数字。",
|
||||
"help": "six / seven / eight / nine / ten",
|
||||
"accept": [
|
||||
[
|
||||
"#min:3",
|
||||
"six",
|
||||
"seven",
|
||||
"eight",
|
||||
"nine",
|
||||
"ten"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a0-04-c",
|
||||
"title": "数字与电话号码",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-P07",
|
||||
"A0-P08"
|
||||
],
|
||||
"grammarNote": "报号码时一个数字一个数字说:one-three-eight,不把它读成一百三十八。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "A0-P07",
|
||||
"word": "What's your phone number?",
|
||||
"meaning": "你的电话号码是多少?",
|
||||
"example": "What's your phone number?",
|
||||
"exampleMeaning": "你的电话号码是多少?"
|
||||
},
|
||||
{
|
||||
"id": "A0-P08",
|
||||
"word": "My number is one-three-eight.",
|
||||
"meaning": "我的号码是 138。",
|
||||
"example": "My number is one-three-eight.",
|
||||
"exampleMeaning": "我的号码是 138。"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-04-c",
|
||||
"listening": {
|
||||
"text": "My number is one-three-eight.",
|
||||
"question": "号码是多少?",
|
||||
"options": [
|
||||
"138",
|
||||
"183",
|
||||
"318"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "My number is one-three-eight.",
|
||||
"tip": "号码一个数字一个数字地读,数字之间稍停:one - three - eight。"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: What’s your phone number?\nShen: My number is five-zero-two.\nMia: Five-two-zero?\nShen: No. Five-zero-two.",
|
||||
"question": "Shen 最后确认的号码是哪一个?",
|
||||
"options": [
|
||||
"502",
|
||||
"520",
|
||||
"205"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "把虚拟号码 139 写成英文。",
|
||||
"example": "one-three-nine",
|
||||
"accept": [
|
||||
[
|
||||
"#numbers:3"
|
||||
]
|
||||
],
|
||||
"hint": "用英文一个一个写出三个数字,例如 one-three-nine。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,说出一个三位虚拟号码。",
|
||||
"help": "My number is one-two-three.",
|
||||
"accept": [
|
||||
[
|
||||
"#min:3",
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"four",
|
||||
"five",
|
||||
"six",
|
||||
"seven",
|
||||
"eight",
|
||||
"nine",
|
||||
"zero"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-04-a",
|
||||
"kind": "main",
|
||||
"title": "数字与电话号码",
|
||||
"goal": "听辨并说出 0 到 5",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Say zero, one, two.",
|
||||
"aiZh": "请说 zero, one, two(0,1,2)。",
|
||||
"goal": "说出 zero, one, two",
|
||||
"model": "zero, one, two",
|
||||
"accept": [
|
||||
"zero",
|
||||
"one",
|
||||
"two"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Now say three, four, five.",
|
||||
"aiZh": "现在请说 three, four, five(3,4,5)。",
|
||||
"goal": "说出 three, four, five",
|
||||
"model": "three, four, five",
|
||||
"accept": [
|
||||
"three",
|
||||
"four",
|
||||
"five"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-04-b",
|
||||
"kind": "main",
|
||||
"title": "数字与电话号码",
|
||||
"goal": "听辨 6 到 10 并认识 phone",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Say six, seven, eight.",
|
||||
"aiZh": "请说 six, seven, eight(6,7,8)。",
|
||||
"goal": "说出 six, seven, eight",
|
||||
"model": "six, seven, eight",
|
||||
"accept": [
|
||||
"six",
|
||||
"seven",
|
||||
"eight"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "What is this? Say: It is a phone.",
|
||||
"aiZh": "这是什么?请说:It is a phone(这是一部手机)。",
|
||||
"goal": "说出 It is a phone.",
|
||||
"model": "It is a phone.",
|
||||
"accept": [
|
||||
"it is a phone",
|
||||
"it's a phone"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-04-c",
|
||||
"kind": "main",
|
||||
"title": "数字与电话号码",
|
||||
"goal": "询问并报告三位号码",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "What is your phone number?",
|
||||
"aiZh": "你的电话号码是多少?",
|
||||
"goal": "报出你的三位号码",
|
||||
"model": "My number is one-three-eight.",
|
||||
"accept": [
|
||||
"my number is + #digit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say a three-digit number again.",
|
||||
"aiZh": "请再说一次三位数字。",
|
||||
"goal": "再说一次三位数字",
|
||||
"model": "one-three-eight",
|
||||
"accept": [
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"four",
|
||||
"five",
|
||||
"six",
|
||||
"seven",
|
||||
"eight",
|
||||
"nine",
|
||||
"zero"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-number",
|
||||
"kind": "variant",
|
||||
"title": "留个电话",
|
||||
"goal": "问候并交换一个虚拟电话号码",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! How are you today?",
|
||||
"aiZh": "嗨!你今天好吗?",
|
||||
"goal": "说出你今天的状态",
|
||||
"model": "I’m good, thanks.",
|
||||
"accept": [
|
||||
"good",
|
||||
"okay",
|
||||
"ok",
|
||||
"fine",
|
||||
"great",
|
||||
"tired"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good. What’s your phone number? Use a fake number.",
|
||||
"aiZh": "好的。你的电话号码是多少?用一个虚拟号码。",
|
||||
"goal": "报出一个号码",
|
||||
"model": "My number is one-three-eight.",
|
||||
"accept": [
|
||||
"#digit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, please say it again.",
|
||||
"aiZh": "抱歉,请再说一遍。",
|
||||
"goal": "再说一次号码",
|
||||
"model": "One-three-eight.",
|
||||
"accept": [
|
||||
"#digit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thanks! Now ask me one question.",
|
||||
"aiZh": "谢谢!现在问我一个问题。",
|
||||
"goal": "反问对方",
|
||||
"model": "What’s your phone number?",
|
||||
"accept": [
|
||||
"#question"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {
|
||||
"title": "留个电话",
|
||||
"summary": "问候、报出号码、再说一遍,并反问对方",
|
||||
"recapPrompt": "用英语报一次你的虚拟电话号码。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
{
|
||||
"id": "a0-05",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "这是什么?",
|
||||
"canDo": "询问并说出常见物品",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W19",
|
||||
"type": "word",
|
||||
"en": "book",
|
||||
"zh": "书",
|
||||
"match": [
|
||||
[
|
||||
"book"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“书”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s a book.",
|
||||
"meaning": "这是一本书。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W21",
|
||||
"type": "word",
|
||||
"en": "pen",
|
||||
"zh": "笔",
|
||||
"match": [
|
||||
[
|
||||
"pen"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“笔”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s a pen.",
|
||||
"meaning": "这是一支笔。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W22",
|
||||
"type": "word",
|
||||
"en": "bag",
|
||||
"zh": "包",
|
||||
"match": [
|
||||
[
|
||||
"bag"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“包”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s a bag.",
|
||||
"meaning": "这是一个包。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W23",
|
||||
"type": "word",
|
||||
"en": "key",
|
||||
"zh": "钥匙",
|
||||
"match": [
|
||||
[
|
||||
"key"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“钥匙”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s a key.",
|
||||
"meaning": "这是一把钥匙。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P09",
|
||||
"type": "phrase",
|
||||
"en": "What's this?",
|
||||
"match": [
|
||||
[
|
||||
"what is this"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "指着身边的东西问“这是什么”。",
|
||||
"dictation": {
|
||||
"sentence": "What’s this?",
|
||||
"meaning": "这是什么?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P10",
|
||||
"type": "phrase",
|
||||
"en": "It's a [object].",
|
||||
"match": [
|
||||
[
|
||||
"it is"
|
||||
],
|
||||
[
|
||||
"book",
|
||||
"pen",
|
||||
"bag",
|
||||
"key",
|
||||
"phone"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "说出“这是一件物品”。",
|
||||
"hint": "用 It’s / It is 加上一个物品。",
|
||||
"dictation": {
|
||||
"sentence": "It’s a pen.",
|
||||
"meaning": "这是一支笔。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-05-a",
|
||||
"title": "这是什么?",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W19",
|
||||
"A0-W21",
|
||||
"A0-W22",
|
||||
"A0-W23",
|
||||
"A0-P09",
|
||||
"A0-P10"
|
||||
],
|
||||
"grammarNote": "问物品用 What’s this?;回答用 It’s a + 物品。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-05-book",
|
||||
"word": "book",
|
||||
"meaning": "书",
|
||||
"example": "It’s a book.",
|
||||
"exampleMeaning": "这是一本书。",
|
||||
"ipa": "/bʊk/"
|
||||
},
|
||||
{
|
||||
"id": "a0-05-pen",
|
||||
"word": "pen",
|
||||
"meaning": "笔",
|
||||
"example": "It’s a pen.",
|
||||
"exampleMeaning": "这是一支笔。",
|
||||
"ipa": "/pen/"
|
||||
},
|
||||
{
|
||||
"id": "a0-05-this",
|
||||
"word": "What’s this?",
|
||||
"meaning": "这是什么?",
|
||||
"example": "What’s this? It’s a key.",
|
||||
"exampleMeaning": "这是什么?这是钥匙。"
|
||||
},
|
||||
{
|
||||
"id": "a0-05-bag",
|
||||
"word": "bag",
|
||||
"meaning": "包",
|
||||
"example": "It’s a bag.",
|
||||
"exampleMeaning": "这是一个包。",
|
||||
"ipa": "/bæɡ/"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-05-a",
|
||||
"listening": {
|
||||
"text": "What’s this? It’s a key.",
|
||||
"question": "这是什么?",
|
||||
"options": [
|
||||
"钥匙",
|
||||
"书",
|
||||
"水"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What’s this? It’s a pen.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: What’s this? Is it a pen?\nShen: No. It’s a book.\nMia: And what’s this?\nShen: It’s a bag.",
|
||||
"question": "Shen 说第一件东西是什么?",
|
||||
"options": [
|
||||
"A book",
|
||||
"A pen",
|
||||
"A bag"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句“这是一支笔”。",
|
||||
"example": "It’s a pen.",
|
||||
"accept": [
|
||||
[
|
||||
"it is"
|
||||
],
|
||||
[
|
||||
"book",
|
||||
"pen",
|
||||
"bag",
|
||||
"key"
|
||||
]
|
||||
],
|
||||
"hint": "用 It’s / It is 加一个物品,例如 a pen。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,说出一个身边物品。",
|
||||
"help": "It’s a …",
|
||||
"accept": [
|
||||
[
|
||||
"it's a",
|
||||
"it is a",
|
||||
"it's an",
|
||||
"it is an"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-05-a",
|
||||
"kind": "main",
|
||||
"title": "这是什么?",
|
||||
"goal": "询问并说出一个物品",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "What’s this? It is a pen.",
|
||||
"aiZh": "这是什么?这是一支笔。",
|
||||
"goal": "说出这个物品",
|
||||
"model": "It’s a pen.",
|
||||
"accept": [
|
||||
"it's a",
|
||||
"it is a",
|
||||
"it's an",
|
||||
"it is an"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Now say: It’s a pen.",
|
||||
"aiZh": "现在请说:It’s a pen(这是一支笔)。",
|
||||
"goal": "完整说出 It’s a … 句型",
|
||||
"model": "It’s a pen.",
|
||||
"accept": [
|
||||
"it's a",
|
||||
"it is a",
|
||||
"it's an",
|
||||
"it is an"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Ask me: What’s this?",
|
||||
"aiZh": "请问我:What’s this(这是什么)?",
|
||||
"goal": "反问 What’s this",
|
||||
"model": "What’s this?",
|
||||
"accept": [
|
||||
"what's this",
|
||||
"what is this"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Great! Say one more object.",
|
||||
"aiZh": "太棒了!再说一个物品吧。",
|
||||
"goal": "再说一个物品",
|
||||
"model": "It’s a book.",
|
||||
"accept": [
|
||||
"it's a",
|
||||
"it is a",
|
||||
"it's an",
|
||||
"it is an"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
{
|
||||
"id": "a0-06",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "我来自哪里?",
|
||||
"canDo": "说明来自哪里并反问",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-P11",
|
||||
"type": "phrase",
|
||||
"en": "Where are you from?",
|
||||
"match": [
|
||||
[
|
||||
"where are you from"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问对方来自哪里。",
|
||||
"dictation": {
|
||||
"sentence": "Where are you from?",
|
||||
"meaning": "你来自哪里?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P12",
|
||||
"type": "phrase",
|
||||
"en": "I'm from [place].",
|
||||
"match": [
|
||||
[
|
||||
"i am",
|
||||
"my name is"
|
||||
],
|
||||
[
|
||||
"/\\bfrom [a-z]{2,}/"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "说出你来自哪里。",
|
||||
"hint": "用 I’m from … 说出一个地点。",
|
||||
"dictation": {
|
||||
"sentence": "I’m from China.",
|
||||
"meaning": "我来自中国。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-06-a",
|
||||
"title": "我来自哪里?",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-P11",
|
||||
"A0-P12"
|
||||
],
|
||||
"grammarNote": "说地点用 I’m from + 地点:I’m from Hong Kong。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-06-from",
|
||||
"word": "from",
|
||||
"meaning": "来自",
|
||||
"example": "I’m from Hong Kong.",
|
||||
"exampleMeaning": "我来自香港。",
|
||||
"ipa": "/frəm/"
|
||||
},
|
||||
{
|
||||
"id": "a0-06-place",
|
||||
"word": "place",
|
||||
"meaning": "地点",
|
||||
"example": "Where are you from?",
|
||||
"exampleMeaning": "你来自哪里?"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-06-a",
|
||||
"listening": {
|
||||
"text": "Where are you from?",
|
||||
"question": "对方在问什么?",
|
||||
"options": [
|
||||
"来自哪里",
|
||||
"叫什么",
|
||||
"喜欢什么"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I’m from Hong Kong.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: I’m from London. Where are you from?\nShen: I’m from Hong Kong.",
|
||||
"question": "Shen 来自哪里?",
|
||||
"options": [
|
||||
"Hong Kong",
|
||||
"London",
|
||||
"Beijing"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句你来自哪里。",
|
||||
"example": "I’m from Hong Kong.",
|
||||
"accept": [
|
||||
[
|
||||
"i am",
|
||||
"my name is"
|
||||
],
|
||||
[
|
||||
"/\\bfrom [a-z]{2,}/"
|
||||
]
|
||||
],
|
||||
"hint": "用 I’m from … 写成一个完整句子。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,说来自哪里并反问对方。",
|
||||
"help": "I’m from … / Where are you from?",
|
||||
"accept": [
|
||||
[
|
||||
"i'm from",
|
||||
"i am from"
|
||||
],
|
||||
[
|
||||
"where are you from",
|
||||
"#question"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-06-a",
|
||||
"kind": "main",
|
||||
"title": "我来自哪里?",
|
||||
"goal": "说来自哪里并反问",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Where are you from?",
|
||||
"aiZh": "你来自哪里?",
|
||||
"goal": "说出你来自哪里",
|
||||
"model": "I’m from Hong Kong.",
|
||||
"accept": [
|
||||
"i'm from",
|
||||
"i am from",
|
||||
"from + #word"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Nice! Ask me: Where are you from?",
|
||||
"aiZh": "很好!请问我:Where are you from(你来自哪里)?",
|
||||
"goal": "反问对方来自哪里",
|
||||
"model": "Where are you from?",
|
||||
"accept": [
|
||||
"where are you from",
|
||||
"where're you from",
|
||||
"where are you"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say where you are from one more time.",
|
||||
"aiZh": "请再说一次你来自哪里。",
|
||||
"goal": "再说一次你来自哪里",
|
||||
"model": "I’m from Hong Kong.",
|
||||
"accept": [
|
||||
"i'm from",
|
||||
"i am from",
|
||||
"from + #word"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say goodbye!",
|
||||
"aiZh": "请说再见!",
|
||||
"goal": "说再见",
|
||||
"model": "Bye!",
|
||||
"accept": [
|
||||
"bye",
|
||||
"goodbye",
|
||||
"see you"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,305 +0,0 @@
|
||||
{
|
||||
"id": "a0-07",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "我的家人",
|
||||
"canDo": "介绍一位家人",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W25",
|
||||
"type": "word",
|
||||
"en": "mother",
|
||||
"zh": "妈妈",
|
||||
"match": [
|
||||
[
|
||||
"mother"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“妈妈”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "This is my mother.",
|
||||
"meaning": "这是我妈妈。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W26",
|
||||
"type": "word",
|
||||
"en": "father",
|
||||
"zh": "爸爸",
|
||||
"match": [
|
||||
[
|
||||
"father"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“爸爸”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "This is my father.",
|
||||
"meaning": "这是我爸爸。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W27",
|
||||
"type": "word",
|
||||
"en": "sister",
|
||||
"zh": "姐妹",
|
||||
"match": [
|
||||
[
|
||||
"sister"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“姐妹”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "This is my sister.",
|
||||
"meaning": "这是我的姐妹。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W28",
|
||||
"type": "word",
|
||||
"en": "brother",
|
||||
"zh": "兄弟",
|
||||
"match": [
|
||||
[
|
||||
"brother"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“兄弟”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "This is my brother.",
|
||||
"meaning": "这是我的兄弟。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P13",
|
||||
"type": "phrase",
|
||||
"en": "This is my [person].",
|
||||
"match": [
|
||||
[
|
||||
"/\\bthis is my [a-z]{2,}/"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "介绍一位家人。",
|
||||
"dictation": {
|
||||
"sentence": "This is my sister.",
|
||||
"meaning": "这是我的姐妹。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-07-a",
|
||||
"title": "我的家人",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W25",
|
||||
"A0-W26",
|
||||
"A0-W27",
|
||||
"A0-W28",
|
||||
"A0-P13"
|
||||
],
|
||||
"grammarNote": "介绍人用 This is my + 人:This is my mother。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-07-family",
|
||||
"word": "family",
|
||||
"meaning": "家人",
|
||||
"example": "This is my family.",
|
||||
"exampleMeaning": "这是我的家人。",
|
||||
"ipa": "/ˈfæməli/"
|
||||
},
|
||||
{
|
||||
"id": "a0-07-mother",
|
||||
"word": "mother",
|
||||
"meaning": "母亲",
|
||||
"example": "This is my mother.",
|
||||
"exampleMeaning": "这是我的妈妈。"
|
||||
},
|
||||
{
|
||||
"id": "a0-07-sister",
|
||||
"word": "sister",
|
||||
"meaning": "姐妹",
|
||||
"example": "This is my sister.",
|
||||
"exampleMeaning": "这是我的姐姐(或妹妹)。",
|
||||
"ipa": "/ˈsɪstə(r)/"
|
||||
},
|
||||
{
|
||||
"id": "a0-07-brother",
|
||||
"word": "brother",
|
||||
"meaning": "兄弟",
|
||||
"example": "This is my brother.",
|
||||
"exampleMeaning": "这是我的哥哥(或弟弟)。",
|
||||
"ipa": "/ˈbrʌðə(r)/"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-07-a",
|
||||
"listening": {
|
||||
"text": "Who is this? This is my mother.",
|
||||
"question": "这个人是谁?",
|
||||
"options": [
|
||||
"妈妈",
|
||||
"朋友",
|
||||
"老师"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "This is my family.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Who is this? Is this your father?\nShen: No. This is my mother.\nMia: And who is this? Your sister?\nShen: No. This is my brother.",
|
||||
"question": "Shen 先介绍的是哪一位家人?",
|
||||
"options": [
|
||||
"My mother",
|
||||
"My brother",
|
||||
"My father"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "介绍一位家人或朋友。",
|
||||
"example": "This is my mother.",
|
||||
"accept": [
|
||||
[
|
||||
"this is my"
|
||||
],
|
||||
[
|
||||
"mother",
|
||||
"father",
|
||||
"sister",
|
||||
"brother",
|
||||
"friend"
|
||||
]
|
||||
],
|
||||
"hint": "用 This is my … 介绍一位家人或朋友。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,介绍一位家人或朋友。",
|
||||
"help": "This is my …",
|
||||
"accept": [
|
||||
[
|
||||
"this is my"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-07-a",
|
||||
"kind": "main",
|
||||
"title": "我的家人",
|
||||
"goal": "介绍一位家人或朋友",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Who is this?",
|
||||
"aiZh": "这是谁?",
|
||||
"goal": "介绍一位家人或朋友",
|
||||
"model": "This is my mother.",
|
||||
"accept": [
|
||||
"this is my",
|
||||
"this is"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good. Say: This is my mother.",
|
||||
"aiZh": "很好。请说:This is my mother(这是我的妈妈)。",
|
||||
"goal": "完整说出 This is my … 句型",
|
||||
"model": "This is my mother.",
|
||||
"accept": [
|
||||
"this is my",
|
||||
"this is"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Ask me: Who is this?",
|
||||
"aiZh": "请问我:Who is this(这是谁)?",
|
||||
"goal": "反问 Who is this",
|
||||
"model": "Who is this?",
|
||||
"accept": [
|
||||
"who is this",
|
||||
"who's this"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Introduce one person again.",
|
||||
"aiZh": "请再介绍一个人。",
|
||||
"goal": "再介绍一个人",
|
||||
"model": "This is my friend.",
|
||||
"accept": [
|
||||
"this is my",
|
||||
"this is"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-photo",
|
||||
"kind": "variant",
|
||||
"title": "看照片聊天",
|
||||
"goal": "介绍照片里的人和物品",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Nice photo! Who is this?",
|
||||
"aiZh": "照片真好!这是谁?",
|
||||
"goal": "介绍照片里的人",
|
||||
"model": "This is my sister.",
|
||||
"accept": [
|
||||
"this is my",
|
||||
"this is"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "And what’s this in the photo?",
|
||||
"aiZh": "照片里这个是什么?",
|
||||
"goal": "说出照片里的物品",
|
||||
"model": "It’s a bag.",
|
||||
"accept": [
|
||||
"it's a",
|
||||
"it is a",
|
||||
"it's an",
|
||||
"it is an"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Where are you from?",
|
||||
"aiZh": "你来自哪里?",
|
||||
"goal": "说出你来自哪里",
|
||||
"model": "I’m from Hong Kong.",
|
||||
"accept": [
|
||||
"i'm from",
|
||||
"i am from",
|
||||
"from + #word"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Cool! Now ask me one question.",
|
||||
"aiZh": "真酷!现在问我一个问题。",
|
||||
"goal": "反问对方",
|
||||
"model": "Who is this? / What’s this?",
|
||||
"accept": [
|
||||
"#question"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {
|
||||
"title": "看照片聊天",
|
||||
"summary": "介绍照片里的人和东西,说喜好,并反问对方",
|
||||
"recapPrompt": "用英语介绍一位家人或朋友。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,603 +0,0 @@
|
||||
{
|
||||
"id": "a0-08",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "星期与时间",
|
||||
"canDo": "说今天和整点",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W12",
|
||||
"type": "word",
|
||||
"en": "Monday",
|
||||
"zh": "星期一",
|
||||
"match": [
|
||||
[
|
||||
"monday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“星期一”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Monday.",
|
||||
"meaning": "今天星期一。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W13",
|
||||
"type": "word",
|
||||
"en": "Tuesday",
|
||||
"zh": "星期二",
|
||||
"match": [
|
||||
[
|
||||
"tuesday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“星期二”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Tuesday.",
|
||||
"meaning": "今天星期二。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W14",
|
||||
"type": "word",
|
||||
"en": "Wednesday",
|
||||
"zh": "星期三",
|
||||
"match": [
|
||||
[
|
||||
"wednesday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“星期三”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Wednesday.",
|
||||
"meaning": "今天星期三。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W15",
|
||||
"type": "word",
|
||||
"en": "Thursday",
|
||||
"zh": "星期四",
|
||||
"match": [
|
||||
[
|
||||
"thursday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“星期四”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Thursday.",
|
||||
"meaning": "今天星期四。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W16",
|
||||
"type": "word",
|
||||
"en": "Friday",
|
||||
"zh": "星期五",
|
||||
"match": [
|
||||
[
|
||||
"friday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“星期五”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Friday.",
|
||||
"meaning": "今天星期五。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W17",
|
||||
"type": "word",
|
||||
"en": "Saturday",
|
||||
"zh": "星期六",
|
||||
"match": [
|
||||
[
|
||||
"saturday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“星期六”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Saturday.",
|
||||
"meaning": "今天星期六。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W18",
|
||||
"type": "word",
|
||||
"en": "Sunday",
|
||||
"zh": "星期日",
|
||||
"match": [
|
||||
[
|
||||
"sunday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“星期日”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Sunday.",
|
||||
"meaning": "今天星期日。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P14",
|
||||
"type": "phrase",
|
||||
"en": "What day is it today?",
|
||||
"match": [
|
||||
[
|
||||
"what day is it"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问今天星期几。",
|
||||
"dictation": {
|
||||
"sentence": "What day is it today?",
|
||||
"meaning": "今天星期几?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P15",
|
||||
"type": "phrase",
|
||||
"en": "It's [weekday].",
|
||||
"match": [
|
||||
[
|
||||
"it is"
|
||||
],
|
||||
[
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
"sunday"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "说出今天星期几。",
|
||||
"dictation": {
|
||||
"sentence": "It’s Friday.",
|
||||
"meaning": "今天星期五。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P16",
|
||||
"type": "phrase",
|
||||
"en": "What time is it?",
|
||||
"match": [
|
||||
[
|
||||
"what time is it"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问现在几点。",
|
||||
"dictation": {
|
||||
"sentence": "What time is it?",
|
||||
"meaning": "现在几点?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P17",
|
||||
"type": "phrase",
|
||||
"en": "It's [hour] o'clock.",
|
||||
"match": [
|
||||
[
|
||||
"it is"
|
||||
],
|
||||
[
|
||||
"o'clock",
|
||||
"oclock",
|
||||
"clock"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "说出一个整点时间。",
|
||||
"hint": "用 It’s … o’clock 说整点时间。",
|
||||
"dictation": {
|
||||
"sentence": "It’s three o’clock.",
|
||||
"meaning": "现在三点。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-08-monday",
|
||||
"word": "Monday",
|
||||
"meaning": "星期一",
|
||||
"example": "It is Monday.",
|
||||
"exampleMeaning": "今天是星期一。"
|
||||
},
|
||||
{
|
||||
"id": "a0-08-oclock",
|
||||
"word": "o’clock",
|
||||
"meaning": "整点",
|
||||
"example": "It’s three o’clock.",
|
||||
"exampleMeaning": "现在三点整。"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-08-a",
|
||||
"title": "星期与时间",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W12",
|
||||
"A0-W13",
|
||||
"A0-W14",
|
||||
"A0-P14",
|
||||
"A0-P15"
|
||||
],
|
||||
"grammarNote": "问星期用 What day is it?;回答用 It is / It’s + 星期。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "A0-W12",
|
||||
"word": "Monday",
|
||||
"meaning": "星期一",
|
||||
"example": "It is Monday.",
|
||||
"exampleMeaning": "今天是星期一。",
|
||||
"ipa": "/ˈmʌndeɪ/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W13",
|
||||
"word": "Tuesday",
|
||||
"meaning": "星期二",
|
||||
"example": "It is Tuesday.",
|
||||
"exampleMeaning": "今天是星期二。",
|
||||
"ipa": "/ˈtjuːzdeɪ/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W14",
|
||||
"word": "Wednesday",
|
||||
"meaning": "星期三",
|
||||
"example": "It is Wednesday.",
|
||||
"exampleMeaning": "今天是星期三。",
|
||||
"ipa": "/ˈwenzdeɪ/"
|
||||
},
|
||||
{
|
||||
"id": "A0-P14",
|
||||
"word": "What day is it today?",
|
||||
"meaning": "今天星期几?",
|
||||
"example": "What day is it today?",
|
||||
"exampleMeaning": "今天星期几?"
|
||||
},
|
||||
{
|
||||
"id": "A0-P15",
|
||||
"word": "It's Monday.",
|
||||
"meaning": "今天是星期一。",
|
||||
"example": "It's Monday.",
|
||||
"exampleMeaning": "今天是星期一。"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-08-a",
|
||||
"listening": {
|
||||
"text": "It is Monday.",
|
||||
"question": "今天星期几?",
|
||||
"options": [
|
||||
"星期一",
|
||||
"星期二",
|
||||
"星期三"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "It is Monday.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: What day is it today? Is it Monday?\nShen: No. It is Tuesday.",
|
||||
"question": "Mia 猜错了,今天其实是星期几?",
|
||||
"options": [
|
||||
"Tuesday",
|
||||
"Monday",
|
||||
"Wednesday"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句今天是星期一、二或三。",
|
||||
"example": "It is Monday.",
|
||||
"accept": [
|
||||
[
|
||||
"it is"
|
||||
],
|
||||
[
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday"
|
||||
]
|
||||
],
|
||||
"hint": "用 It is / It’s 加 Monday、Tuesday 或 Wednesday。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看帮助,说出星期一、二或三。",
|
||||
"help": "It is Monday / Tuesday / Wednesday.",
|
||||
"accept": [
|
||||
[
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a0-08-b",
|
||||
"title": "星期与时间",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W15",
|
||||
"A0-W16",
|
||||
"A0-W17",
|
||||
"A0-W18"
|
||||
],
|
||||
"grammarNote": "星期四到星期日也沿用 It is / It’s + 星期这个句型。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "A0-W15",
|
||||
"word": "Thursday",
|
||||
"meaning": "星期四",
|
||||
"example": "It is Thursday.",
|
||||
"exampleMeaning": "今天是星期四。",
|
||||
"ipa": "/ˈθɜːzdeɪ/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W16",
|
||||
"word": "Friday",
|
||||
"meaning": "星期五",
|
||||
"example": "It is Friday.",
|
||||
"exampleMeaning": "今天是星期五。",
|
||||
"ipa": "/ˈfraɪdeɪ/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W17",
|
||||
"word": "Saturday",
|
||||
"meaning": "星期六",
|
||||
"example": "It is Saturday.",
|
||||
"exampleMeaning": "今天是星期六。",
|
||||
"ipa": "/ˈsætədeɪ/"
|
||||
},
|
||||
{
|
||||
"id": "A0-W18",
|
||||
"word": "Sunday",
|
||||
"meaning": "星期日",
|
||||
"example": "It is Sunday.",
|
||||
"exampleMeaning": "今天是星期日。",
|
||||
"ipa": "/ˈsʌndeɪ/"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-08-b",
|
||||
"listening": {
|
||||
"text": "It is Friday.",
|
||||
"question": "今天星期几?",
|
||||
"options": [
|
||||
"星期五",
|
||||
"星期四",
|
||||
"星期日"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "It is Friday.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Is it Saturday today?\nShen: No. It is Sunday.",
|
||||
"question": "Shen 说今天是哪一天?",
|
||||
"options": [
|
||||
"Sunday",
|
||||
"Saturday",
|
||||
"Friday"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句今天是星期四到日。",
|
||||
"example": "It is Friday.",
|
||||
"accept": [
|
||||
[
|
||||
"it is"
|
||||
],
|
||||
[
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
"sunday"
|
||||
]
|
||||
],
|
||||
"hint": "用 It is / It’s 加 Thursday、Friday、Saturday 或 Sunday。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看帮助,说出星期四到日。",
|
||||
"help": "It is Thursday / Friday / Saturday / Sunday.",
|
||||
"accept": [
|
||||
[
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
"sunday"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a0-08-c",
|
||||
"title": "星期与时间",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-P16",
|
||||
"A0-P17"
|
||||
],
|
||||
"grammarNote": "说整点用 It is / It’s + 数字 + o’clock,例如 It’s three o’clock。",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "A0-P16",
|
||||
"word": "What time is it?",
|
||||
"meaning": "现在几点?",
|
||||
"example": "What time is it?",
|
||||
"exampleMeaning": "现在几点?"
|
||||
},
|
||||
{
|
||||
"id": "A0-P17",
|
||||
"word": "It's three o'clock.",
|
||||
"meaning": "现在三点。",
|
||||
"example": "It's three o'clock.",
|
||||
"exampleMeaning": "现在三点。"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-08-c",
|
||||
"listening": {
|
||||
"text": "It is three o’clock.",
|
||||
"question": "是什么时间?",
|
||||
"options": [
|
||||
"三点",
|
||||
"一点",
|
||||
"星期三"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "It is three o’clock.",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: What time is it? Is it two o’clock?\nShen: No. It is three o’clock.",
|
||||
"question": "Shen 说现在是几点?",
|
||||
"options": [
|
||||
"Three o’clock",
|
||||
"Two o’clock",
|
||||
"One o’clock"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句整点时间。",
|
||||
"example": "It is three o’clock.",
|
||||
"accept": [
|
||||
[
|
||||
"it is"
|
||||
],
|
||||
[
|
||||
"o'clock",
|
||||
"oclock"
|
||||
]
|
||||
],
|
||||
"hint": "用 It is / It’s 加数字和 o’clock,例如 It’s three o’clock。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看帮助,说一个整点时间。",
|
||||
"help": "It is … o’clock.",
|
||||
"accept": [
|
||||
[
|
||||
"o'clock",
|
||||
"oclock"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-08-a",
|
||||
"kind": "main",
|
||||
"title": "星期与时间",
|
||||
"goal": "询问并说出星期一到三",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "What day is it?",
|
||||
"aiZh": "今天星期几?",
|
||||
"goal": "问今天星期几",
|
||||
"model": "What day is it?",
|
||||
"accept": [
|
||||
"what day"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say Monday, Tuesday, or Wednesday.",
|
||||
"aiZh": "请说 Monday, Tuesday, 或 Wednesday(周一、周二或周三)。",
|
||||
"goal": "说出周一到周三中的一天",
|
||||
"model": "It is Monday.",
|
||||
"accept": [
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-08-b",
|
||||
"kind": "main",
|
||||
"title": "星期与时间",
|
||||
"goal": "说出星期四到日",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "What day is it?",
|
||||
"aiZh": "今天星期几?",
|
||||
"goal": "问今天星期几",
|
||||
"model": "What day is it?",
|
||||
"accept": [
|
||||
"what day"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say Thursday, Friday, Saturday, or Sunday.",
|
||||
"aiZh": "请说 Thursday, Friday, Saturday, 或 Sunday(周四、周五、周六或周日)。",
|
||||
"goal": "说出周四到周日中的一天",
|
||||
"model": "It is Friday.",
|
||||
"accept": [
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
"sunday"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-08-c",
|
||||
"kind": "main",
|
||||
"title": "星期与时间",
|
||||
"goal": "询问并说出整点",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "What time is it?",
|
||||
"aiZh": "现在几点了?",
|
||||
"goal": "问现在几点",
|
||||
"model": "What time is it?",
|
||||
"accept": [
|
||||
"what time"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say one full time sentence.",
|
||||
"aiZh": "请说一个完整的时间句子。",
|
||||
"goal": "说一个完整的时间句子",
|
||||
"model": "It is three o’clock.",
|
||||
"accept": [
|
||||
"o'clock",
|
||||
"oclock"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
{
|
||||
"id": "a0-09",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "我喜欢……",
|
||||
"canDo": "表达喜好并回答",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W29",
|
||||
"type": "word",
|
||||
"en": "coffee",
|
||||
"zh": "咖啡",
|
||||
"match": [
|
||||
[
|
||||
"coffee"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“咖啡”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "I like coffee.",
|
||||
"meaning": "我喜欢咖啡。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W30",
|
||||
"type": "word",
|
||||
"en": "tea",
|
||||
"zh": "茶",
|
||||
"match": [
|
||||
[
|
||||
"tea"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“茶”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "Do you like tea?",
|
||||
"meaning": "你喜欢茶吗?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W31",
|
||||
"type": "word",
|
||||
"en": "music",
|
||||
"zh": "音乐",
|
||||
"match": [
|
||||
[
|
||||
"music"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“音乐”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "I like music.",
|
||||
"meaning": "我喜欢音乐。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W32",
|
||||
"type": "word",
|
||||
"en": "movies",
|
||||
"zh": "电影",
|
||||
"match": [
|
||||
[
|
||||
"movies"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“电影”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "Do you like movies?",
|
||||
"meaning": "你喜欢电影吗?"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W38",
|
||||
"type": "word",
|
||||
"en": "yes",
|
||||
"zh": "是",
|
||||
"match": [
|
||||
[
|
||||
"yes"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“是”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "Yes, I do.",
|
||||
"meaning": "是的,我喜欢。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-W39",
|
||||
"type": "word",
|
||||
"en": "no",
|
||||
"zh": "不",
|
||||
"match": [
|
||||
[
|
||||
"no"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“不”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "No, I don’t.",
|
||||
"meaning": "不,我不喜欢。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P18",
|
||||
"type": "phrase",
|
||||
"en": "I like [thing].",
|
||||
"match": [
|
||||
[
|
||||
"/\\bi (like|love) [a-z]{2,}/"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "说出你喜欢的一样东西。",
|
||||
"hint": "用 I like … 说一项喜好。",
|
||||
"dictation": {
|
||||
"sentence": "I like music.",
|
||||
"meaning": "我喜欢音乐。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P19",
|
||||
"type": "phrase",
|
||||
"en": "Do you like [thing]?",
|
||||
"match": [
|
||||
[
|
||||
"do you like"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "问对方是否喜欢一样东西。",
|
||||
"dictation": {
|
||||
"sentence": "Do you like coffee?",
|
||||
"meaning": "你喜欢咖啡吗?"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-09-a",
|
||||
"title": "我喜欢……",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W29",
|
||||
"A0-W30",
|
||||
"A0-W31",
|
||||
"A0-W32",
|
||||
"A0-W38",
|
||||
"A0-W39",
|
||||
"A0-P18",
|
||||
"A0-P19"
|
||||
],
|
||||
"grammarNote": "说喜好用 I like + 东西;一般问句用 Do you like + 东西?",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-09-like",
|
||||
"word": "like",
|
||||
"meaning": "喜欢",
|
||||
"example": "I like tea.",
|
||||
"exampleMeaning": "我喜欢茶。",
|
||||
"ipa": "/laɪk/"
|
||||
},
|
||||
{
|
||||
"id": "a0-09-tea",
|
||||
"word": "tea",
|
||||
"meaning": "茶",
|
||||
"example": "Do you like tea?",
|
||||
"exampleMeaning": "你喜欢茶吗?"
|
||||
},
|
||||
{
|
||||
"id": "a0-09-music",
|
||||
"word": "music",
|
||||
"meaning": "音乐",
|
||||
"example": "I like music.",
|
||||
"exampleMeaning": "我喜欢音乐。",
|
||||
"ipa": "/ˈmjuːzɪk/"
|
||||
},
|
||||
{
|
||||
"id": "a0-09-movies",
|
||||
"word": "movies",
|
||||
"meaning": "电影",
|
||||
"example": "Do you like movies?",
|
||||
"exampleMeaning": "你喜欢看电影吗?",
|
||||
"ipa": "/ˈmuːviz/"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-09-a",
|
||||
"listening": {
|
||||
"text": "Do you like tea? Yes, I do.",
|
||||
"question": "对方喜欢茶吗?",
|
||||
"options": [
|
||||
"喜欢",
|
||||
"不喜欢",
|
||||
"不知道"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I like tea. Do you like tea?",
|
||||
"tip": ""
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: I like tea. Do you like tea?\nShen: No, I don’t. I like coffee and music.\nMia: Do you like movies?\nShen: Yes, I do.",
|
||||
"question": "Shen 喜欢喝什么?",
|
||||
"options": [
|
||||
"Coffee",
|
||||
"Tea",
|
||||
"Music"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句你的喜好。",
|
||||
"example": "I like tea.",
|
||||
"accept": [
|
||||
[
|
||||
"like"
|
||||
],
|
||||
[
|
||||
"#words:3"
|
||||
]
|
||||
],
|
||||
"hint": "用 I like … 写出一种喜好。"
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看句框,说一个喜好并反问。",
|
||||
"help": "I like … / Do you like …?",
|
||||
"accept": [
|
||||
[
|
||||
"i like",
|
||||
"i love"
|
||||
],
|
||||
[
|
||||
"do you like",
|
||||
"#question"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-09-a",
|
||||
"kind": "main",
|
||||
"title": "我喜欢……",
|
||||
"goal": "表达喜好、回答和反问",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "What do you like?",
|
||||
"aiZh": "你喜欢什么?",
|
||||
"goal": "说出你喜欢什么",
|
||||
"model": "I like tea.",
|
||||
"accept": [
|
||||
"i like",
|
||||
"i love"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Do you like tea?",
|
||||
"aiZh": "你喜欢茶吗?",
|
||||
"goal": "回答是否喜欢",
|
||||
"model": "Yes, I do.",
|
||||
"accept": [
|
||||
"yes",
|
||||
"no",
|
||||
"i do",
|
||||
"i don't",
|
||||
"i do not"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Now ask me what I like.",
|
||||
"aiZh": "现在请问我喜欢什么。",
|
||||
"goal": "反问对方的喜好",
|
||||
"model": "Do you like tea?",
|
||||
"accept": [
|
||||
"do you like",
|
||||
"#question"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say your like one more time.",
|
||||
"aiZh": "请再说一次你的喜好。",
|
||||
"goal": "再说一次你的喜好",
|
||||
"model": "I like tea.",
|
||||
"accept": [
|
||||
"i like",
|
||||
"i love"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "a0-plan",
|
||||
"kind": "variant",
|
||||
"title": "约个时间",
|
||||
"goal": "说明星期、时间和喜好",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! What day is it today?",
|
||||
"aiZh": "嗨!今天星期几?",
|
||||
"goal": "说出今天星期几",
|
||||
"model": "It’s Friday.",
|
||||
"accept": [
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
"sunday"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "What time is it now?",
|
||||
"aiZh": "现在几点?",
|
||||
"goal": "说出现在几点",
|
||||
"model": "It’s three o’clock.",
|
||||
"accept": [
|
||||
"o'clock",
|
||||
"oclock",
|
||||
"it's + #digit",
|
||||
"it is + #digit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Do you like coffee or tea?",
|
||||
"aiZh": "你喜欢咖啡还是茶?",
|
||||
"goal": "说出你的喜好",
|
||||
"model": "I like tea.",
|
||||
"accept": [
|
||||
"i like",
|
||||
"i love",
|
||||
"yes",
|
||||
"no",
|
||||
"i don't"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Me too! Now ask me one question.",
|
||||
"aiZh": "我也是!现在问我一个问题。",
|
||||
"goal": "反问对方",
|
||||
"model": "Do you like music?",
|
||||
"accept": [
|
||||
"#question"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {
|
||||
"title": "约个时间",
|
||||
"summary": "说星期和整点,回答喜好,并反问对方",
|
||||
"recapPrompt": "用英语说今天星期几和现在几点。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
{
|
||||
"id": "a0-10",
|
||||
"level": "A0",
|
||||
"status": "approved",
|
||||
"title": "A0 综合任务",
|
||||
"canDo": "没听清时请对方重复,并完成自我介绍、喜好和反问",
|
||||
"categories": [],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A0-W24",
|
||||
"type": "word",
|
||||
"en": "water",
|
||||
"zh": "水",
|
||||
"match": [
|
||||
[
|
||||
"water"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "把“水”写成英文。",
|
||||
"dictation": {
|
||||
"sentence": "I like water.",
|
||||
"meaning": "我喜欢喝水。"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A0-P20",
|
||||
"type": "phrase",
|
||||
"en": "Please say that again.",
|
||||
"match": [
|
||||
[
|
||||
"please say that again",
|
||||
"please speak slowly"
|
||||
]
|
||||
],
|
||||
"review": {
|
||||
"prompt": "请对方再说一次。",
|
||||
"dictation": {
|
||||
"sentence": "Please say that again.",
|
||||
"meaning": "请再说一遍。"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "a0-10-a",
|
||||
"title": "A0 综合任务",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A0-W24",
|
||||
"A0-P20"
|
||||
],
|
||||
"grammarNote": "礼貌请求以 Please 开头:Please say that again.",
|
||||
"vocabulary": [
|
||||
{
|
||||
"id": "a0-10-water",
|
||||
"word": "water",
|
||||
"meaning": "水",
|
||||
"example": "I like water.",
|
||||
"exampleMeaning": "我喜欢喝水。",
|
||||
"ipa": "/ˈwɔːtər/"
|
||||
},
|
||||
{
|
||||
"id": "a0-10-again",
|
||||
"word": "Please say that again.",
|
||||
"meaning": "请再说一遍。",
|
||||
"example": "Please say that again.",
|
||||
"exampleMeaning": "请再说一遍。"
|
||||
},
|
||||
{
|
||||
"id": "a0-10-slowly",
|
||||
"word": "Please speak slowly.",
|
||||
"meaning": "请说慢一点。",
|
||||
"example": "Please speak slowly.",
|
||||
"exampleMeaning": "请说慢一点。"
|
||||
}
|
||||
],
|
||||
"sceneId": "a0-10-a",
|
||||
"listening": {
|
||||
"text": "Do you like water? Sorry, please say that again.",
|
||||
"question": "第二个人希望对方做什么?",
|
||||
"options": [
|
||||
"再说一遍",
|
||||
"说慢一点",
|
||||
"给他一杯水"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Sorry, please say that again. I like water.",
|
||||
"tip": "water 的 t 常读得很轻,像轻轻的 d。先清楚,不必快。"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Alex: Hi! I’m Alex. Where are you from?\nShen: Sorry? Please say that again.\nAlex: Where are you from?\nShen: I’m from Hong Kong. Do you like coffee?\nAlex: No, I don’t. I like water.",
|
||||
"question": "Alex 喜欢喝什么?",
|
||||
"options": [
|
||||
"Water",
|
||||
"Coffee",
|
||||
"Tea"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写姓名、地点和喜好三句,喜好用 water。",
|
||||
"example": "I’m Shen.\nI’m from Hong Kong.\nI like water.",
|
||||
"accept": [
|
||||
[
|
||||
"i am",
|
||||
"my name is"
|
||||
],
|
||||
[
|
||||
"from"
|
||||
],
|
||||
[
|
||||
"like"
|
||||
],
|
||||
[
|
||||
"water"
|
||||
]
|
||||
],
|
||||
"hint": "分别写姓名、来自哪里和喜好三部分,喜好用 water:I like water."
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看帮助:先请对方再说一遍,再说姓名、喜欢喝什么,并反问对方。",
|
||||
"help": "Please say that again. / I’m … / I like water. / Do you like …?",
|
||||
"accept": [
|
||||
[
|
||||
"say that again",
|
||||
"speak slowly",
|
||||
"pardon"
|
||||
],
|
||||
[
|
||||
"i'm + #word",
|
||||
"i am + #word",
|
||||
"my name is",
|
||||
"my name's"
|
||||
],
|
||||
[
|
||||
"i like",
|
||||
"i love"
|
||||
],
|
||||
[
|
||||
"#question"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "a0-10-a",
|
||||
"kind": "main",
|
||||
"title": "A0 综合任务",
|
||||
"goal": "请求重复或放慢语速,并完成基础沟通",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Say: Please say that again.",
|
||||
"aiZh": "请说:Please say that again(请再说一遍)。",
|
||||
"goal": "请求对方重复",
|
||||
"model": "Please say that again.",
|
||||
"accept": [
|
||||
"say that again",
|
||||
"again",
|
||||
"pardon"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Say: Please speak slowly.",
|
||||
"aiZh": "请说:Please speak slowly(请说慢一点)。",
|
||||
"goal": "请求对方放慢语速",
|
||||
"model": "Please speak slowly.",
|
||||
"accept": [
|
||||
"slowly",
|
||||
"slow"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Now ask me your name or where I am from.",
|
||||
"aiZh": "现在请问我的名字或我来自哪里。",
|
||||
"goal": "反问名字或来自哪里",
|
||||
"model": "What’s your name?",
|
||||
"accept": [
|
||||
"what's your name",
|
||||
"what is your name",
|
||||
"where are you from",
|
||||
"your name",
|
||||
"#question"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Tell me what you like to drink.",
|
||||
"aiZh": "告诉我你喜欢喝什么。",
|
||||
"goal": "说出你喜欢喝什么",
|
||||
"model": "I like water.",
|
||||
"accept": [
|
||||
"i like",
|
||||
"i love"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [],
|
||||
"materials": []
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,919 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U01",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "开口求助与数字",
|
||||
"categories": [
|
||||
"旅行",
|
||||
"日常"
|
||||
],
|
||||
"canDo": "没听懂时礼貌求助,说 11–100 的数字,拼读字母",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "123-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "249",
|
||||
"use": "active",
|
||||
"reason": "Can you…? 是旅行中求助必需的功能问句,本单元只作为固定句块使用"
|
||||
},
|
||||
{
|
||||
"id": "59-1",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "239",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "246",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "150",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U01-P01",
|
||||
"type": "phrase",
|
||||
"en": "Excuse me.",
|
||||
"zh": "打扰一下。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"excuse me"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. Can you help me?",
|
||||
"zh": "打扰一下,你能帮我吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你想问路人问题,先礼貌地引起对方注意。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P02",
|
||||
"type": "phrase",
|
||||
"en": "Sorry, I don't understand.",
|
||||
"zh": "抱歉,我没听懂。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "understand 在 CEFR-J 为 A2,但没听懂时求助是旅行第一天就要用的生存句",
|
||||
"match": [
|
||||
[
|
||||
"do not understand",
|
||||
"can not understand"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, I don't understand. Can you speak slowly, please?",
|
||||
"zh": "抱歉,我没听懂。你能说慢一点吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "对方说得太快,你没听懂,礼貌地告诉对方。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P03",
|
||||
"type": "pattern",
|
||||
"en": "Can you speak slowly, please?",
|
||||
"zh": "你能说慢一点吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "slowly 在 CEFR-J 为 A2,但请对方放慢语速是零基础学习者最常用的求助句",
|
||||
"match": [
|
||||
[
|
||||
"speak slowly",
|
||||
"speak more slowly",
|
||||
"slowly"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, can you speak slowly, please?",
|
||||
"zh": "抱歉,你能说慢一点吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请对方说慢一点。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P04",
|
||||
"type": "pattern",
|
||||
"en": "Can you help me?",
|
||||
"zh": "你能帮我吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"help me",
|
||||
"help us"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. Can you help me?",
|
||||
"zh": "打扰一下,你能帮我吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在车站,你想请工作人员帮忙。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P05",
|
||||
"type": "pattern",
|
||||
"en": "What does this word mean?",
|
||||
"zh": "这个词是什么意思?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what does",
|
||||
"what is"
|
||||
],
|
||||
[
|
||||
"mean"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. What does this word mean?",
|
||||
"zh": "打扰一下,这个词是什么意思?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你看到一个不认识的词,问它的意思。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P06",
|
||||
"type": "pattern",
|
||||
"en": "Can you write it down, please?",
|
||||
"zh": "你能把它写下来吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"write it down",
|
||||
"write that down",
|
||||
"write down"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, I don't understand. Can you write it down, please?",
|
||||
"zh": "抱歉,我没听懂。你能写下来吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你听不清一个号码,请对方写下来。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P07",
|
||||
"type": "pattern",
|
||||
"en": "How do you say this in English?",
|
||||
"zh": "这个用英语怎么说?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how do you say"
|
||||
],
|
||||
[
|
||||
"in english"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. How do you say this in English?",
|
||||
"zh": "打扰一下,这个用英语怎么说?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "指着一样东西,问它用英语怎么说。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-W01",
|
||||
"type": "word",
|
||||
"en": "eleven",
|
||||
"zh": "十一",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"eleven"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Eleven or twelve?",
|
||||
"zh": "十一还是十二?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "用英语说数字 11。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-W02",
|
||||
"type": "word",
|
||||
"en": "twelve",
|
||||
"zh": "十二",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"twelve"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My room number is twelve.",
|
||||
"zh": "我的房间号是十二。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "用英语说数字 12。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P08",
|
||||
"type": "pattern",
|
||||
"en": "thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen",
|
||||
"zh": "十三到十九(-teen 重音在后)",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"thirteen",
|
||||
"fourteen",
|
||||
"fifteen",
|
||||
"sixteen",
|
||||
"seventeen",
|
||||
"eighteen",
|
||||
"nineteen"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The bus number is seventeen.",
|
||||
"zh": "公交车是十七路。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "用英语说 13 到 19 之间的一个数字,比如 15。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-P09",
|
||||
"type": "pattern",
|
||||
"en": "twenty, thirty, forty, fifty, sixty, seventy, eighty, ninety",
|
||||
"zh": "二十到九十(-ty 重音在前),21 说成 twenty-one",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"twenty",
|
||||
"thirty",
|
||||
"forty",
|
||||
"fifty",
|
||||
"sixty",
|
||||
"seventy",
|
||||
"eighty",
|
||||
"ninety"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Forty or fourteen?",
|
||||
"zh": "四十还是十四?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "用英语说 40,再说 45。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-W03",
|
||||
"type": "word",
|
||||
"en": "one hundred",
|
||||
"zh": "一百",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "hundred 在 CEFR-J 为 A2,但说到 100 是本单元的数字目标,下一单元的价格也要用",
|
||||
"match": [
|
||||
[
|
||||
"hundred"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Your key number is one hundred.",
|
||||
"zh": "你的钥匙号是一百。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "用英语说数字 100。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U01-R01",
|
||||
"en": "repeat",
|
||||
"zh": "重复"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R02",
|
||||
"en": "pardon",
|
||||
"zh": "(没听清)请再说一遍"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R03",
|
||||
"en": "question",
|
||||
"zh": "问题"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R04",
|
||||
"en": "easy",
|
||||
"zh": "容易的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R05",
|
||||
"en": "difficult",
|
||||
"zh": "难的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R06",
|
||||
"en": "right",
|
||||
"zh": "对的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R07",
|
||||
"en": "wrong",
|
||||
"zh": "错的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R08",
|
||||
"en": "sure",
|
||||
"zh": "当然;好的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R09",
|
||||
"en": "letter",
|
||||
"zh": "字母"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R10",
|
||||
"en": "first",
|
||||
"zh": "第一个"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R11",
|
||||
"en": "email",
|
||||
"zh": "电子邮件"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R12",
|
||||
"en": "address",
|
||||
"zh": "地址"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R13",
|
||||
"en": "listen",
|
||||
"zh": "听"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R14",
|
||||
"en": "look",
|
||||
"zh": "看"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R15",
|
||||
"en": "problem",
|
||||
"zh": "问题;麻烦"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R16",
|
||||
"en": "room",
|
||||
"zh": "房间"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R17",
|
||||
"en": "welcome",
|
||||
"zh": "欢迎;不客气"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R18",
|
||||
"en": "great",
|
||||
"zh": "太好了"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R19",
|
||||
"en": "bus",
|
||||
"zh": "公交车"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R20",
|
||||
"en": "hotel",
|
||||
"zh": "酒店"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R21",
|
||||
"en": "here",
|
||||
"zh": "这里"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R22",
|
||||
"en": "there",
|
||||
"zh": "那里"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R23",
|
||||
"en": "now",
|
||||
"zh": "现在"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R24",
|
||||
"en": "very",
|
||||
"zh": "很"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R25",
|
||||
"en": "fine",
|
||||
"zh": "很好;没问题"
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-R26",
|
||||
"en": "answer",
|
||||
"zh": "答案;回答"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U01-S1",
|
||||
"title": "没听懂时怎么说",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U01-P01",
|
||||
"A1-U01-P02",
|
||||
"A1-U01-P03",
|
||||
"A1-U01-P04"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. Can you help me? Sure. What's the problem? Sorry, I don't understand. Can you speak slowly, please?",
|
||||
"question": "说话的人没听懂时,请对方做什么?",
|
||||
"options": [
|
||||
"说慢一点",
|
||||
"写下来",
|
||||
"再帮一次忙"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Excuse me. Can you help me?",
|
||||
"tip": "excuse 的 x 读 /ks/"
|
||||
},
|
||||
{
|
||||
"text": "Sorry, I don't understand."
|
||||
},
|
||||
{
|
||||
"text": "Can you speak slowly, please?"
|
||||
},
|
||||
{
|
||||
"text": "Pardon? Can you repeat that, please?",
|
||||
"tip": "pardon 只需要听懂,会说更好"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Anna: Excuse me. Can you help me?\nTom: Sure. What's the problem?\nAnna: I have a question. Where is the bus?\nTom: The bus is there.\nAnna: Sorry, I don't understand. Can you speak slowly, please?\nTom: Sure. The bus... is... there. Look!\nAnna: Great. Thank you very much.\nTom: You're welcome.",
|
||||
"question": "Anna 没听懂时说了哪一句?",
|
||||
"options": [
|
||||
"Can you speak slowly, please?",
|
||||
"Can you help me?",
|
||||
"Where is the bus?"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "你没听懂对方的话,写两句话:说没听懂,再请对方说慢一点。",
|
||||
"example": "Sorry, I don't understand. Can you speak slowly, please?",
|
||||
"itemIds": [
|
||||
"A1-U01-P02",
|
||||
"A1-U01-P03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "想象你在街上找不到路:不看提示,说出引起注意、请求帮忙、请对方说慢一点的三句话。",
|
||||
"itemIds": [
|
||||
"A1-U01-P01",
|
||||
"A1-U01-P04",
|
||||
"A1-U01-P03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-S2",
|
||||
"title": "问词义、拼写和写下来",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U01-P05",
|
||||
"A1-U01-P06",
|
||||
"A1-U01-P07"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. What does this word mean? This one? Address. Look, this is my email address. Sorry, can you write it down, please? Sure. Here you are.",
|
||||
"question": "他们在说哪个词?",
|
||||
"options": [
|
||||
"地址",
|
||||
"电话",
|
||||
"名字"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "A B C D E F G, H I J K L M N O P, Q R S T U V, W X Y Z.",
|
||||
"tip": "先念熟 26 个字母,才能拼读和听懂别人拼写"
|
||||
},
|
||||
{
|
||||
"text": "What does this word mean?"
|
||||
},
|
||||
{
|
||||
"text": "Can you write it down, please?"
|
||||
},
|
||||
{
|
||||
"text": "How do you say this in English?"
|
||||
},
|
||||
{
|
||||
"text": "K, E, Y. Key.",
|
||||
"tip": "拼读时一个字母一个字母地说,停顿清楚"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Excuse me. How do you say this in English?\nTom: It's a key.\nMia: How do you spell that?\nTom: K, E, Y. Three letters. The first letter is K.\nMia: Can you write it down, please?\nTom: Sure. Here you are. Now listen: key.\nMia: Key. Thanks!",
|
||||
"question": "Tom 说第一个字母是什么?",
|
||||
"options": [
|
||||
"K",
|
||||
"E",
|
||||
"Y"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "你看到一个不认识的词,写一句话问它的意思。",
|
||||
"example": "Excuse me. What does this word mean?",
|
||||
"itemIds": [
|
||||
"A1-U01-P05"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "指着身边的一样东西,用英语问它怎么说,再请对方把词写下来。",
|
||||
"itemIds": [
|
||||
"A1-U01-P07",
|
||||
"A1-U01-P06"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-S3",
|
||||
"title": "11 到 100",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U01-W01",
|
||||
"A1-U01-W02",
|
||||
"A1-U01-P08",
|
||||
"A1-U01-P09",
|
||||
"A1-U01-W03"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hello. My room number is fifteen. Fifty? No, fifteen. One, five.",
|
||||
"question": "房间号是多少?",
|
||||
"options": [
|
||||
"15",
|
||||
"50",
|
||||
"51"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "eleven, twelve"
|
||||
},
|
||||
{
|
||||
"text": "thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen",
|
||||
"tip": "-teen 重音在后:thir-TEEN"
|
||||
},
|
||||
{
|
||||
"text": "twenty, thirty, forty, fifty, sixty, seventy, eighty, ninety",
|
||||
"tip": "-ty 重音在前:THIR-ty"
|
||||
},
|
||||
{
|
||||
"text": "twenty-one, thirty-five, one hundred"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Tom: What's your room number?\nMia: Thirty.\nTom: Thirteen?\nMia: No, that's wrong. Thirty. Three, zero.\nTom: Right, thirty. And the bus number?\nMia: Eleven.",
|
||||
"question": "Mia 的房间号是多少?",
|
||||
"options": [
|
||||
"thirty",
|
||||
"thirteen",
|
||||
"three"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "用英文单词写出 40、14 和 100。",
|
||||
"example": "forty, fourteen, one hundred",
|
||||
"itemIds": [
|
||||
"A1-U01-P09",
|
||||
"A1-U01-P08",
|
||||
"A1-U01-W03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看提示,用英语从 11 数到 20,再说出 30、50、99 和 100。",
|
||||
"itemIds": [
|
||||
"A1-U01-W01",
|
||||
"A1-U01-W02",
|
||||
"A1-U01-P08",
|
||||
"A1-U01-P09",
|
||||
"A1-U01-W03"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U01-C1",
|
||||
"kind": "main",
|
||||
"title": "在街上问公交车",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "路人",
|
||||
"setting": "你在街边,想知道去酒店坐几路公交车。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello.",
|
||||
"goal": "引起对方注意,请对方帮忙",
|
||||
"model": "Excuse me. Can you help me?",
|
||||
"zh": "打扰一下,你能帮我吗?",
|
||||
"itemIds": [
|
||||
"A1-U01-P01",
|
||||
"A1-U01-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. What's your question?",
|
||||
"goal": "问去酒店坐哪路公交车",
|
||||
"model": "Which one is the hotel bus?",
|
||||
"zh": "去酒店坐哪路车?",
|
||||
"itemIds": []
|
||||
},
|
||||
{
|
||||
"ai": "The hotel bus? It's the seventeen bus. It's there.",
|
||||
"goal": "说你没听懂,请对方说慢一点",
|
||||
"model": "Sorry, I don't understand. Can you speak slowly, please?",
|
||||
"zh": "抱歉,我没听懂。你能说慢一点吗?",
|
||||
"itemIds": [
|
||||
"A1-U01-P02",
|
||||
"A1-U01-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Bus... seventeen.",
|
||||
"goal": "请对方把号码写下来",
|
||||
"model": "Can you write it down, please?",
|
||||
"zh": "你能写下来吗?",
|
||||
"itemIds": [
|
||||
"A1-U01-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Here you are. One, seven.",
|
||||
"goal": "确认号码并道谢",
|
||||
"model": "Seventeen. Thank you very much!",
|
||||
"zh": "十七路。非常感谢!",
|
||||
"itemIds": [
|
||||
"A1-U01-P08"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-C2",
|
||||
"kind": "variant",
|
||||
"title": "向朋友问词义",
|
||||
"learnerRole": "学英语的人",
|
||||
"aiRole": "朋友 Tom",
|
||||
"setting": "朋友发来的消息里有个词你不认识。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! How are you?",
|
||||
"goal": "回答,再问 easy 是什么意思",
|
||||
"model": "I'm fine, thanks. What does easy mean?",
|
||||
"zh": "我很好,谢谢。easy 是什么意思?",
|
||||
"itemIds": [
|
||||
"A1-U01-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Easy means it is not difficult.",
|
||||
"goal": "请对方说慢一点",
|
||||
"model": "Sorry, can you speak slowly, please?",
|
||||
"zh": "抱歉,你能说慢一点吗?",
|
||||
"itemIds": [
|
||||
"A1-U01-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Easy... is not... difficult. Now, a question for me?",
|
||||
"goal": "指着一样东西,问它用英语怎么说",
|
||||
"model": "Okay, thanks. How do you say this in English?",
|
||||
"zh": "好的,谢谢。这个用英语怎么说?",
|
||||
"itemIds": [
|
||||
"A1-U01-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good question! The answer is a pen.",
|
||||
"goal": "请对方把词写下来",
|
||||
"model": "Can you write it down, please?",
|
||||
"zh": "你能写下来吗?",
|
||||
"itemIds": [
|
||||
"A1-U01-P06"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-C3",
|
||||
"kind": "variant",
|
||||
"title": "在酒店前台听号码",
|
||||
"learnerRole": "住客",
|
||||
"aiRole": "前台",
|
||||
"setting": "你刚到酒店,前台很快地说了房间号。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. Welcome to the hotel. Your room number is twelve.",
|
||||
"goal": "没听清,请对方说慢一点",
|
||||
"model": "Sorry, I don't understand. Can you speak slowly, please?",
|
||||
"zh": "抱歉,我没听懂。你能说慢一点吗?",
|
||||
"itemIds": [
|
||||
"A1-U01-P02",
|
||||
"A1-U01-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Room... twelve. One, two.",
|
||||
"goal": "确认是 11 还是 12",
|
||||
"model": "Eleven or twelve?",
|
||||
"zh": "十一还是十二?",
|
||||
"itemIds": [
|
||||
"A1-U01-W01",
|
||||
"A1-U01-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Twelve. And your key number is one hundred.",
|
||||
"goal": "重复钥匙号确认",
|
||||
"model": "One hundred? Okay.",
|
||||
"zh": "一百?好的。",
|
||||
"itemIds": [
|
||||
"A1-U01-W03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. And the bus is at four o'clock. Bus number fourteen.",
|
||||
"goal": "确认是 14 还是 40",
|
||||
"model": "Fourteen or forty?",
|
||||
"zh": "十四还是四十?",
|
||||
"itemIds": [
|
||||
"A1-U01-P08",
|
||||
"A1-U01-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Fourteen. One, four.",
|
||||
"goal": "确认并道谢",
|
||||
"model": "Fourteen. Thank you very much!",
|
||||
"zh": "十四。非常感谢!",
|
||||
"itemIds": [
|
||||
"A1-U01-P08"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U01-T1",
|
||||
"sceneId": "A1-U01-C1",
|
||||
"goal": "向路人求助:没听懂时请对方说慢一点并写下来,最后确认公交车号码",
|
||||
"itemIds": [
|
||||
"A1-U01-P01",
|
||||
"A1-U01-P04",
|
||||
"A1-U01-P02",
|
||||
"A1-U01-P03",
|
||||
"A1-U01-P06",
|
||||
"A1-U01-P08"
|
||||
],
|
||||
"slots": [
|
||||
"公交车号码"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-T2",
|
||||
"sceneId": "A1-U01-C2",
|
||||
"goal": "问一个词的意思,再问一样东西用英语怎么说",
|
||||
"itemIds": [
|
||||
"A1-U01-P05",
|
||||
"A1-U01-P03",
|
||||
"A1-U01-P07",
|
||||
"A1-U01-P06"
|
||||
],
|
||||
"slots": [
|
||||
"不认识的词",
|
||||
"要问的东西"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-T3",
|
||||
"sceneId": "A1-U01-C3",
|
||||
"goal": "听前台说号码,逐个确认容易混淆的数字",
|
||||
"itemIds": [
|
||||
"A1-U01-P02",
|
||||
"A1-U01-P03",
|
||||
"A1-U01-W01",
|
||||
"A1-U01-W02",
|
||||
"A1-U01-W03",
|
||||
"A1-U01-P08",
|
||||
"A1-U01-P09"
|
||||
],
|
||||
"slots": [
|
||||
"房间号",
|
||||
"钥匙号",
|
||||
"公交车号码"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U01-M1",
|
||||
"mode": "listening",
|
||||
"title": "问路人:坐几路车、住几号房",
|
||||
"text": "Hello! Welcome to the hotel. Do you have a question? Sure, I can help you. Listen, please, and look here. The bus to the hotel is number seventeen. One, seven. Sorry, can you repeat that? Yes, seventeen. Is that right? Great. Your room is number thirty. Three, zero. Now, is everything fine? The airport bus is number forty. It comes every twenty minutes. Please wait here at the bus stop. Have a good day!",
|
||||
"glosses": {
|
||||
"stop": "(公交)站",
|
||||
"minutes": "分钟",
|
||||
"airport": "机场",
|
||||
"wait": "等",
|
||||
"comes": "来"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话主要在帮客人做什么?",
|
||||
"options": [
|
||||
"告诉他公交车号和房间号",
|
||||
"卖给他一张车票",
|
||||
"教他怎么点咖啡"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "去酒店坐几路公交车?",
|
||||
"options": [
|
||||
"十七路",
|
||||
"四十路",
|
||||
"三十路"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U01-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:问词义和拼写",
|
||||
"text": "Anna: Excuse me, can you help me? This word is difficult. I don't understand it. What does it mean?\nTom: This one? It's 'address'. Look, this is my email address.\nAnna: Sorry, can you repeat it? And can you spell it? I'll write the letters down.\nTom: Sure. A, D, D, R, E, S, S. The first letter is A.\nAnna: Let me read it again. Is my answer right or wrong?\nTom: It's right. No problem!\nAnna: Great, thank you. My room is number fifteen.\nTom: Fifteen or fifty?\nAnna: Fifteen. One, five.\nTom: OK, that's fine. The bus stop is over there, near the door. It's very easy.\nAnna: Great, thank you very much!",
|
||||
"glosses": {
|
||||
"spell": "拼读",
|
||||
"stop": "(公交)站",
|
||||
"door": "门",
|
||||
"let": "让;Let me… 让我……",
|
||||
"read": "读"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Anna 觉得哪个词难、不明白?",
|
||||
"options": [
|
||||
"address(地址)",
|
||||
"hotel(酒店)",
|
||||
"bus(公交车)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "inference",
|
||||
"question": "Tom 为什么说 “Fifteen or fifty?”",
|
||||
"options": [
|
||||
"这两个数字听起来很像",
|
||||
"他没听清房间号,其实两个都不是",
|
||||
"他想换一个房间"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "Could you…?",
|
||||
"reason": "更礼貌的请求放到 A2,A1 先用 Can you…?"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,832 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U02",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "价格与付款",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "问价、说 100 以内的金额、选择现金或刷卡",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "246",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "10",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "123-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "146-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "141",
|
||||
"use": "active",
|
||||
"reason": "I'll take it. 只作为购物固定句块使用,不讲将来时"
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U02-P01",
|
||||
"type": "pattern",
|
||||
"en": "How much is it?",
|
||||
"zh": "这个多少钱?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how much"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. How much is this hat?",
|
||||
"zh": "打扰一下,这顶帽子多少钱?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你在小店看中一样东西,问它的价格。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-P02",
|
||||
"type": "pattern",
|
||||
"en": "It's twenty dollars.",
|
||||
"zh": "二十美元。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"dollar",
|
||||
"dollars"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Seven dollars? Here you are.",
|
||||
"zh": "七美元?给你。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "用英语说出价格:35 美元。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-W01",
|
||||
"type": "word",
|
||||
"en": "expensive",
|
||||
"zh": "贵的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"expensive"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Forty dollars? That's expensive.",
|
||||
"zh": "四十美元?太贵了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员说的价格太高了,告诉对方很贵。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-W02",
|
||||
"type": "word",
|
||||
"en": "cheap",
|
||||
"zh": "便宜的",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "cheap 在 CEFR-J 为 A2,但和 expensive 成对出现,购物时马上要用",
|
||||
"match": [
|
||||
[
|
||||
"cheap"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is there a cheap one?",
|
||||
"zh": "有便宜一点的吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问店员有没有便宜的。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-P03",
|
||||
"type": "phrase",
|
||||
"en": "I'll take it.",
|
||||
"zh": "我买了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"take it",
|
||||
"take this",
|
||||
"take that",
|
||||
"take the"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Great. I'll take it.",
|
||||
"zh": "太好了,我买了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "价格合适,告诉店员你买下它。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-P04",
|
||||
"type": "pattern",
|
||||
"en": "Can I pay by card?",
|
||||
"zh": "我可以刷卡吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"pay by card",
|
||||
"pay with a card",
|
||||
"pay with card"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'll take it. Can I pay by card?",
|
||||
"zh": "我买了。可以刷卡吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你想刷卡付款,先问一下店员。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-W03",
|
||||
"type": "word",
|
||||
"en": "cash",
|
||||
"zh": "现金",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "cash 在 CEFR-J 为 A2,但“现金还是刷卡”是旅行付款必答的问题",
|
||||
"match": [
|
||||
[
|
||||
"cash"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Oh, wait. I have cash.",
|
||||
"zh": "哦,等等,我有现金。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员问你怎么付款,说用现金。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-P05",
|
||||
"type": "phrase",
|
||||
"en": "Here you are.",
|
||||
"zh": "给你。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"here you are"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Five dollars. Here you are.",
|
||||
"zh": "五美元,给你。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "把钱递给店员时说一句话。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-W04",
|
||||
"type": "word",
|
||||
"en": "change",
|
||||
"zh": "找零;零钱",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"change"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Thank you. Here's your change.",
|
||||
"zh": "谢谢,这是找您的零钱。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员还没找钱,提醒对方找零。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-P06",
|
||||
"type": "pattern",
|
||||
"en": "Can I have a receipt, please?",
|
||||
"zh": "可以给我一张收据吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "receipt 在 CEFR-J 为 A2,但旅行报销和核对金额都要用",
|
||||
"match": [
|
||||
[
|
||||
"receipt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "No, thanks. Can I have a receipt, please?",
|
||||
"zh": "不用了,谢谢。可以给我收据吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "付完钱,向店员要一张收据。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-P07",
|
||||
"type": "pattern",
|
||||
"en": "Is there an ATM near here?",
|
||||
"zh": "这附近有取款机吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"atm",
|
||||
"cash machine"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I only have cards. Is there an ATM near here?",
|
||||
"zh": "我只有卡。这附近有取款机吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你需要取现金,问附近有没有取款机。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U02-R01",
|
||||
"en": "price",
|
||||
"zh": "价格"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R02",
|
||||
"en": "buy",
|
||||
"zh": "买"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R03",
|
||||
"en": "want",
|
||||
"zh": "想要"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R04",
|
||||
"en": "money",
|
||||
"zh": "钱"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R05",
|
||||
"en": "cent",
|
||||
"zh": "美分"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R06",
|
||||
"en": "coin",
|
||||
"zh": "硬币"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R07",
|
||||
"en": "only",
|
||||
"zh": "只;仅仅"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R08",
|
||||
"en": "just",
|
||||
"zh": "只是;仅"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R09",
|
||||
"en": "sale",
|
||||
"zh": "打折;出售"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R10",
|
||||
"en": "free",
|
||||
"zh": "免费的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R11",
|
||||
"en": "shop",
|
||||
"zh": "商店"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R12",
|
||||
"en": "ticket",
|
||||
"zh": "票"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R13",
|
||||
"en": "map",
|
||||
"zh": "地图"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R14",
|
||||
"en": "gift",
|
||||
"zh": "礼物"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R15",
|
||||
"en": "hat",
|
||||
"zh": "帽子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R16",
|
||||
"en": "umbrella",
|
||||
"zh": "伞"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R17",
|
||||
"en": "bottle",
|
||||
"zh": "瓶子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R18",
|
||||
"en": "machine",
|
||||
"zh": "机器"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R19",
|
||||
"en": "else",
|
||||
"zh": "别的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R20",
|
||||
"en": "also",
|
||||
"zh": "也"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R21",
|
||||
"en": "wait",
|
||||
"zh": "等"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-R22",
|
||||
"en": "then",
|
||||
"zh": "那么;然后"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U02-S1",
|
||||
"title": "问价格",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U02-P01",
|
||||
"A1-U02-P02",
|
||||
"A1-U02-W01",
|
||||
"A1-U02-W02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. How much is this hat? It's forty dollars. Forty? That's expensive. This one is cheap. It's only fifteen dollars.",
|
||||
"question": "便宜的那顶帽子多少钱?",
|
||||
"options": [
|
||||
"15 美元",
|
||||
"40 美元",
|
||||
"50 美元"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "How much is it?"
|
||||
},
|
||||
{
|
||||
"text": "It's twenty dollars.",
|
||||
"tip": "dollars 结尾的 s 读 /z/"
|
||||
},
|
||||
{
|
||||
"text": "That's expensive."
|
||||
},
|
||||
{
|
||||
"text": "Is there a cheap one?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Excuse me. How much is this umbrella?\nTom: It's twenty-five dollars.\nMia: Twenty-five? That's expensive.\nTom: This one is cheap. It's only nine dollars.\nMia: Great. And how much is the map?\nTom: The map is free.",
|
||||
"question": "地图多少钱?",
|
||||
"options": [
|
||||
"free",
|
||||
"nine dollars",
|
||||
"twenty-five dollars"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句话问价,再写一句说太贵了。",
|
||||
"example": "How much is it? That's expensive.",
|
||||
"itemIds": [
|
||||
"A1-U02-P01",
|
||||
"A1-U02-W01"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "看看身边的一样东西:用英语问它多少钱,再用 dollars 说一个价格。",
|
||||
"itemIds": [
|
||||
"A1-U02-P01",
|
||||
"A1-U02-P02"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-S2",
|
||||
"title": "买下和付款",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U02-P03",
|
||||
"A1-U02-P04",
|
||||
"A1-U02-W03",
|
||||
"A1-U02-P05",
|
||||
"A1-U02-W04",
|
||||
"A1-U02-P06"
|
||||
],
|
||||
"listening": {
|
||||
"text": "I'll take it. Cash or card? Can I pay by card? Sorry, cash only. Okay. Here you are. Thanks. Here's your change.",
|
||||
"question": "这家店收什么?",
|
||||
"options": [
|
||||
"只收现金",
|
||||
"只收卡",
|
||||
"现金和卡都收"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I'll take it.",
|
||||
"tip": "I'll 连读成一个音节"
|
||||
},
|
||||
{
|
||||
"text": "Can I pay by card?"
|
||||
},
|
||||
{
|
||||
"text": "Cash, please."
|
||||
},
|
||||
{
|
||||
"text": "Here you are."
|
||||
},
|
||||
{
|
||||
"text": "Can I have a receipt, please?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Anna: I want to buy this bottle of water. How much is it?\nTom: Two dollars.\nAnna: I'll take it. Can I pay by card?\nTom: Sorry, cash only.\nAnna: Okay. Here you are. Five dollars.\nTom: Thank you. Here's your change. Three dollars.\nAnna: Can I have a receipt, please?\nTom: Sure.",
|
||||
"question": "Anna 给了多少钱?",
|
||||
"options": [
|
||||
"five dollars",
|
||||
"two dollars",
|
||||
"three dollars"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "店员问你 Cash or card?,写下你的回答:先问能不能刷卡,不行就付现金并把钱递过去。",
|
||||
"example": "Can I pay by card? Okay, cash. Here you are.",
|
||||
"itemIds": [
|
||||
"A1-U02-P04",
|
||||
"A1-U02-W03",
|
||||
"A1-U02-P05"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "不看提示,说出买下东西、问能不能刷卡、要收据的三句话。",
|
||||
"itemIds": [
|
||||
"A1-U02-P03",
|
||||
"A1-U02-P04",
|
||||
"A1-U02-P06"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-S3",
|
||||
"title": "找取款机、看价格牌",
|
||||
"minutes": 10,
|
||||
"itemIds": [
|
||||
"A1-U02-P07"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. I need money. Is there an ATM near here? Yes. It's there, near the shop.",
|
||||
"question": "取款机在哪里?",
|
||||
"options": [
|
||||
"商店旁边",
|
||||
"酒店里面",
|
||||
"公交车上"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Is there an ATM near here?",
|
||||
"tip": "ATM 三个字母分开读"
|
||||
},
|
||||
{
|
||||
"text": "I only have cards."
|
||||
},
|
||||
{
|
||||
"text": "Is there a cheap shop near here?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "SALE! Good prices!\nHats: only ten dollars.\nUmbrellas: eighteen dollars.\nMaps: free.\nCoffee: ninety cents.\nWe take cash. We also take cards.\nThe ATM is near the shop.",
|
||||
"question": "伞多少钱?",
|
||||
"options": [
|
||||
"eighteen dollars",
|
||||
"ten dollars",
|
||||
"eighty dollars"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句话问附近有没有取款机。",
|
||||
"example": "Excuse me. Is there an ATM near here?",
|
||||
"itemIds": [
|
||||
"A1-U02-P07"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "想象你身上只有卡、没有现金:说明情况,再问附近有没有取款机。",
|
||||
"itemIds": [
|
||||
"A1-U02-P07"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U02-C1",
|
||||
"kind": "main",
|
||||
"title": "在纪念品店买帽子",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "店员",
|
||||
"setting": "你在一家纪念品店,想买一顶帽子。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello! Can I help you?",
|
||||
"goal": "问这顶帽子多少钱",
|
||||
"model": "Yes. How much is this hat?",
|
||||
"zh": "是的。这顶帽子多少钱?",
|
||||
"itemIds": [
|
||||
"A1-U02-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "It's forty dollars.",
|
||||
"goal": "说太贵了,问有没有便宜的",
|
||||
"model": "That's expensive. Is there a cheap one?",
|
||||
"zh": "太贵了。有便宜的吗?",
|
||||
"itemIds": [
|
||||
"A1-U02-W01",
|
||||
"A1-U02-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, another hat. This one is just twelve dollars. It's a good gift.",
|
||||
"goal": "说你买下",
|
||||
"model": "Great. I'll take it.",
|
||||
"zh": "太好了,我买了。",
|
||||
"itemIds": [
|
||||
"A1-U02-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Cash or card?",
|
||||
"goal": "问能不能刷卡",
|
||||
"model": "Can I pay by card?",
|
||||
"zh": "可以刷卡吗?",
|
||||
"itemIds": [
|
||||
"A1-U02-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Do you want anything else?",
|
||||
"goal": "说不要别的了,再要一张收据",
|
||||
"model": "No, thanks. Can I have a receipt, please?",
|
||||
"zh": "不用了,谢谢。可以给我收据吗?",
|
||||
"itemIds": [
|
||||
"A1-U02-P06"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-C2",
|
||||
"kind": "variant",
|
||||
"title": "只收现金的小摊",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "小摊摊主",
|
||||
"setting": "你在路边小摊买水,摊主只收现金。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! Do you want water? It's two dollars.",
|
||||
"goal": "说要一瓶,问能不能刷卡",
|
||||
"model": "Yes, please. Can I pay by card?",
|
||||
"zh": "要,谢谢。可以刷卡吗?",
|
||||
"itemIds": [
|
||||
"A1-U02-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, cash only.",
|
||||
"goal": "说你只有卡,问附近有没有取款机",
|
||||
"model": "I only have cards. Is there an ATM near here?",
|
||||
"zh": "我只有卡。这附近有取款机吗?",
|
||||
"itemIds": [
|
||||
"A1-U02-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, there. Near the ticket machine.",
|
||||
"goal": "发现自己有现金,把钱递给摊主",
|
||||
"model": "Oh, wait. I have cash. Here you are.",
|
||||
"zh": "哦,等等,我有现金。给你。",
|
||||
"itemIds": [
|
||||
"A1-U02-W03",
|
||||
"A1-U02-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thanks. Ten dollars? Here's your change. Eight dollars in coins.",
|
||||
"goal": "确认找零并道谢",
|
||||
"model": "Eight dollars change. Thank you!",
|
||||
"zh": "找我八美元。谢谢!",
|
||||
"itemIds": [
|
||||
"A1-U02-W04"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-C3",
|
||||
"kind": "variant",
|
||||
"title": "在售票处买车票",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "售票员",
|
||||
"setting": "你在公交车站的售票处,想买两张车票。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. Can I help you?",
|
||||
"goal": "问一张公交车票多少钱",
|
||||
"model": "Hi. How much is a bus ticket?",
|
||||
"zh": "你好。一张公交车票多少钱?",
|
||||
"itemIds": [
|
||||
"A1-U02-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "A bus ticket is three dollars fifty.",
|
||||
"goal": "说要两张,问能不能刷卡",
|
||||
"model": "Two tickets, please. Can I pay by card?",
|
||||
"zh": "两张,谢谢。可以刷卡吗?",
|
||||
"itemIds": [
|
||||
"A1-U02-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Two tickets, then. That's seven dollars.",
|
||||
"goal": "确认金额,把卡递过去",
|
||||
"model": "Seven dollars? Here you are.",
|
||||
"zh": "七美元?给你。",
|
||||
"itemIds": [
|
||||
"A1-U02-P02",
|
||||
"A1-U02-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thank you. Do you want a map? It's free.",
|
||||
"goal": "说要地图,再要一张收据",
|
||||
"model": "Yes, please. Can I have a receipt, please?",
|
||||
"zh": "要,谢谢。可以给我收据吗?",
|
||||
"itemIds": [
|
||||
"A1-U02-P06"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U02-T1",
|
||||
"sceneId": "A1-U02-C1",
|
||||
"goal": "问价、嫌贵换便宜的、买下并刷卡,最后要收据",
|
||||
"itemIds": [
|
||||
"A1-U02-P01",
|
||||
"A1-U02-W01",
|
||||
"A1-U02-W02",
|
||||
"A1-U02-P03",
|
||||
"A1-U02-P04",
|
||||
"A1-U02-P06"
|
||||
],
|
||||
"slots": [
|
||||
"商品",
|
||||
"价格"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-T2",
|
||||
"sceneId": "A1-U02-C2",
|
||||
"goal": "对方只收现金时问取款机,最后付现金并确认找零",
|
||||
"itemIds": [
|
||||
"A1-U02-P04",
|
||||
"A1-U02-P07",
|
||||
"A1-U02-W03",
|
||||
"A1-U02-P05",
|
||||
"A1-U02-W04"
|
||||
],
|
||||
"slots": [
|
||||
"付款方式",
|
||||
"找零金额"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-T3",
|
||||
"sceneId": "A1-U02-C3",
|
||||
"goal": "买两张车票:问价、确认总价、付款并要收据",
|
||||
"itemIds": [
|
||||
"A1-U02-P01",
|
||||
"A1-U02-P04",
|
||||
"A1-U02-P02",
|
||||
"A1-U02-P05",
|
||||
"A1-U02-P06"
|
||||
],
|
||||
"slots": [
|
||||
"票数",
|
||||
"总价"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U02-M1",
|
||||
"mode": "listening",
|
||||
"title": "商店广播:打折和付款",
|
||||
"text": "Welcome to our shop! Everything is on sale today. This hat is only ten dollars — very cheap! This umbrella is just fifteen dollars, a good price. Do you want a map? It's free today! We also have bottles of water. You can pay with cash, coins or a card. The card machine is here. Buy two, and wait for a free gift. Is there an ATM? Yes, near here. Have a nice day!",
|
||||
"glosses": {},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段广播主要说什么?",
|
||||
"options": [
|
||||
"店里在打折、卖东西",
|
||||
"商店今天关门",
|
||||
"这里没有地图"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "那顶帽子多少钱?",
|
||||
"options": [
|
||||
"十美元",
|
||||
"十五美元",
|
||||
"免费的"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U02-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:买包付款",
|
||||
"text": "Mia: How much is this bag? I want to buy a gift.\nShop: The price is twenty dollars.\nMia: That's expensive. This one?\nShop: That one is cheap, only eight dollars.\nMia: OK, I'll take it. Can I pay by card?\nShop: No card today. Cash only, please.\nMia: I have only eight dollars and fifty cents.\nShop: Here is your change: a coin and a receipt.\nMia: Do you have bus tickets?\nShop: No, but the ticket machine is here. Do you want anything else?\nMia: No, that's all. Thank you!",
|
||||
"glosses": {},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Mia 最后买了哪个包?",
|
||||
"options": [
|
||||
"便宜的八美元的包",
|
||||
"二十美元的包",
|
||||
"她没有买"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "她怎么付钱?",
|
||||
"options": [
|
||||
"用现金",
|
||||
"用银行卡",
|
||||
"用手机"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "How much does it cost?",
|
||||
"reason": "cost 为 A2,A1 先用 How much is it?"
|
||||
},
|
||||
{
|
||||
"en": "Do you take credit cards?",
|
||||
"reason": "credit card 为 A2,A1 先用 Can I pay by card?"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,908 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U03",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "在咖啡店点单",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "点饮料、说大小杯、礼貌确认",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "142",
|
||||
"use": "active",
|
||||
"reason": "I'd like… 作为点单的礼貌固定句块使用,不展开 would 的其他用法"
|
||||
},
|
||||
{
|
||||
"id": "239",
|
||||
"use": "receptive"
|
||||
},
|
||||
{
|
||||
"id": "150",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "21",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "16",
|
||||
"use": "active"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U03-P01",
|
||||
"type": "pattern",
|
||||
"en": "I'd like a coffee, please.",
|
||||
"zh": "我想要一杯咖啡。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"would like"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'd like a small orange juice, please.",
|
||||
"zh": "我想要一小杯橙汁。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员问你想喝什么,礼貌地点一杯咖啡。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-P02",
|
||||
"type": "pattern",
|
||||
"en": "Can I see the menu, please?",
|
||||
"zh": "可以看一下菜单吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "menu 在 CEFR-J 为 A2,但点单前看菜单是咖啡店和餐厅的第一步",
|
||||
"match": [
|
||||
[
|
||||
"menu"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, can I see the menu, please?",
|
||||
"zh": "抱歉,可以看一下菜单吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你还没想好点什么,请店员给你看菜单。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-W01",
|
||||
"type": "word",
|
||||
"en": "small",
|
||||
"zh": "小的;小杯",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"small"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "A small tea, please.",
|
||||
"zh": "一小杯茶,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员问你要多大的,说要小杯。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-W02",
|
||||
"type": "word",
|
||||
"en": "large",
|
||||
"zh": "大的;大杯",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"large"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Large, please.",
|
||||
"zh": "大杯,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员问你要多大的,说要大杯。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-W03",
|
||||
"type": "word",
|
||||
"en": "cup",
|
||||
"zh": "杯子;一杯",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"cup"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "A cup of tea for my friend.",
|
||||
"zh": "给我朋友一杯茶。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“一杯茶”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-W04",
|
||||
"type": "word",
|
||||
"en": "hot",
|
||||
"zh": "热的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"hot"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "A hot coffee, please.",
|
||||
"zh": "一杯热咖啡,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你想要热饮,告诉店员。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-W05",
|
||||
"type": "word",
|
||||
"en": "cold",
|
||||
"zh": "冷的;冰的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"cold"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Cold, please.",
|
||||
"zh": "要冰的,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "天很热,你想要冰饮,告诉店员。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-P03",
|
||||
"type": "phrase",
|
||||
"en": "with milk",
|
||||
"zh": "加奶",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"with milk"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Yes, with milk, please.",
|
||||
"zh": "要,加奶,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你的咖啡要加奶,告诉店员。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-P04",
|
||||
"type": "phrase",
|
||||
"en": "No sugar, please.",
|
||||
"zh": "不要加糖。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"no sugar"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "With milk. No sugar, please.",
|
||||
"zh": "加奶,不要糖。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你不想加糖,告诉店员。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-W06",
|
||||
"type": "word",
|
||||
"en": "juice",
|
||||
"zh": "果汁",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"juice"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "An apple juice for my sister.",
|
||||
"zh": "给我妹妹一杯苹果汁。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你不想喝咖啡,点一杯橙汁。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-W07",
|
||||
"type": "word",
|
||||
"en": "sandwich",
|
||||
"zh": "三明治",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"sandwich"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "And a sandwich, please.",
|
||||
"zh": "再要一个三明治。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "饮料点好了,再加一个三明治。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-P05",
|
||||
"type": "pattern",
|
||||
"en": "To go, please.",
|
||||
"zh": "带走,谢谢。(在店里吃说 For here.)",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"to go",
|
||||
"for here"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "For here or to go? To go, please.",
|
||||
"zh": "在这儿吃还是带走?带走,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员问你在店里喝还是带走,说带走。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-P06",
|
||||
"type": "phrase",
|
||||
"en": "That's all, thanks.",
|
||||
"zh": "就这些,谢谢。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"that is all",
|
||||
"that is it"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "No, that's all, thanks.",
|
||||
"zh": "不用了,就这些,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员问你还要别的吗,说就这些。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U03-R01",
|
||||
"en": "drink",
|
||||
"zh": "饮料;喝"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R02",
|
||||
"en": "eat",
|
||||
"zh": "吃"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R03",
|
||||
"en": "food",
|
||||
"zh": "食物"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R04",
|
||||
"en": "cake",
|
||||
"zh": "蛋糕"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R05",
|
||||
"en": "chocolate",
|
||||
"zh": "巧克力"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R06",
|
||||
"en": "bread",
|
||||
"zh": "面包"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R07",
|
||||
"en": "breakfast",
|
||||
"zh": "早餐"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R08",
|
||||
"en": "cookie",
|
||||
"zh": "饼干"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R09",
|
||||
"en": "apple",
|
||||
"zh": "苹果"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R10",
|
||||
"en": "orange",
|
||||
"zh": "橙子;橙色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R11",
|
||||
"en": "cheese",
|
||||
"zh": "奶酪"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R12",
|
||||
"en": "chicken",
|
||||
"zh": "鸡肉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R13",
|
||||
"en": "table",
|
||||
"zh": "桌子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R14",
|
||||
"en": "ready",
|
||||
"zh": "准备好的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R15",
|
||||
"en": "order",
|
||||
"zh": "点单"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R16",
|
||||
"en": "next",
|
||||
"zh": "下一个"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R17",
|
||||
"en": "minute",
|
||||
"zh": "分钟"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R18",
|
||||
"en": "sweet",
|
||||
"zh": "甜的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R19",
|
||||
"en": "delicious",
|
||||
"zh": "好吃的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R20",
|
||||
"en": "enjoy",
|
||||
"zh": "享用;喜欢"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R21",
|
||||
"en": "good morning",
|
||||
"zh": "早上好"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R22",
|
||||
"en": "friend",
|
||||
"zh": "朋友"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-R23",
|
||||
"en": "cafe",
|
||||
"zh": "咖啡店"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U03-S1",
|
||||
"title": "点一杯饮料",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U03-P02",
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W03",
|
||||
"A1-U03-W01",
|
||||
"A1-U03-W02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hi. Can I see the menu, please? Sure. Here you are. What would you like? I'd like a coffee, please. Small or large? Large, please.",
|
||||
"question": "顾客要了多大杯的咖啡?",
|
||||
"options": [
|
||||
"大杯",
|
||||
"小杯",
|
||||
"没说大小"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Can I see the menu, please?"
|
||||
},
|
||||
{
|
||||
"text": "I'd like a coffee, please.",
|
||||
"tip": "I'd 读作 /aɪd/,很轻很短"
|
||||
},
|
||||
{
|
||||
"text": "A small cup of tea, please.",
|
||||
"tip": "cup of 连读成 cuppa"
|
||||
},
|
||||
{
|
||||
"text": "Large, please."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "CAFE MENU\nCoffee: small three dollars, large four dollars\nTea: two dollars fifty\nHot chocolate: four dollars\nOrange juice: three dollars\nBread: two dollars\nCake: five dollars\nCookies: one dollar\nGood morning! Breakfast is ready!",
|
||||
"question": "大杯咖啡多少钱?",
|
||||
"options": [
|
||||
"four dollars",
|
||||
"three dollars",
|
||||
"five dollars"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句话,点一杯小杯咖啡。",
|
||||
"example": "I'd like a small coffee, please.",
|
||||
"itemIds": [
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W01"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "看着菜单,不看提示点一杯你想喝的饮料,并说要大杯还是小杯。",
|
||||
"itemIds": [
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W01",
|
||||
"A1-U03-W02"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-S2",
|
||||
"title": "冷热、加奶和糖",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U03-W04",
|
||||
"A1-U03-W05",
|
||||
"A1-U03-P03",
|
||||
"A1-U03-P04",
|
||||
"A1-U03-W06"
|
||||
],
|
||||
"listening": {
|
||||
"text": "I'd like a tea, please. Hot or cold? Hot, please. With milk? Yes, with milk. No sugar, please.",
|
||||
"question": "顾客的茶是怎么要的?",
|
||||
"options": [
|
||||
"热的,加奶,不加糖",
|
||||
"冰的,加奶,加糖",
|
||||
"热的,不加奶,加糖"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Hot, please."
|
||||
},
|
||||
{
|
||||
"text": "With milk, please."
|
||||
},
|
||||
{
|
||||
"text": "No sugar, please."
|
||||
},
|
||||
{
|
||||
"text": "A cold orange juice, please."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Anna: Hi. I'd like a coffee, please.\nTom: Hot or cold?\nAnna: Cold, please. With milk.\nTom: Sugar?\nAnna: No sugar, please. And an apple juice for my sister.\nTom: Okay. One cold coffee with milk and one apple juice.",
|
||||
"question": "Anna 给妹妹点了什么?",
|
||||
"options": [
|
||||
"apple juice",
|
||||
"cold coffee",
|
||||
"milk"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写下你想要的饮料:热的、加奶、不加糖。",
|
||||
"example": "A hot coffee with milk, please. No sugar.",
|
||||
"itemIds": [
|
||||
"A1-U03-W04",
|
||||
"A1-U03-P03",
|
||||
"A1-U03-P04"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "说说你平时怎么喝咖啡或茶:要热的还是冰的,加不加奶和糖。",
|
||||
"itemIds": [
|
||||
"A1-U03-W04",
|
||||
"A1-U03-W05",
|
||||
"A1-U03-P03",
|
||||
"A1-U03-P04"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-S3",
|
||||
"title": "加点吃的、带走和结束点单",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U03-W07",
|
||||
"A1-U03-P05",
|
||||
"A1-U03-P06"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Anything else? A sandwich, please. For here or to go? To go, please. Is that all? Yes, that's all, thanks.",
|
||||
"question": "顾客是在店里吃还是带走?",
|
||||
"options": [
|
||||
"带走",
|
||||
"在店里吃",
|
||||
"还没决定"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "A chicken sandwich, please."
|
||||
},
|
||||
{
|
||||
"text": "To go, please."
|
||||
},
|
||||
{
|
||||
"text": "For here, please."
|
||||
},
|
||||
{
|
||||
"text": "That's all, thanks."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Tom: Next, please! What would you like?\nMia: A cheese sandwich and a small tea, please.\nTom: For here or to go?\nMia: For here. Is there a free table?\nTom: Yes, there. Anything else? The cake is delicious.\nMia: No, that's all, thanks.\nTom: That's nine dollars. Your food is ready in five minutes.",
|
||||
"question": "Mia 点了什么?",
|
||||
"options": [
|
||||
"奶酪三明治和小杯茶",
|
||||
"蛋糕和小杯茶",
|
||||
"奶酪三明治和蛋糕"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说要带走,再说不要别的了。",
|
||||
"example": "To go, please. That's all, thanks.",
|
||||
"itemIds": [
|
||||
"A1-U03-P05",
|
||||
"A1-U03-P06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你点了一杯饮料和一个三明治:说带走还是在店里吃,最后结束点单。",
|
||||
"itemIds": [
|
||||
"A1-U03-W07",
|
||||
"A1-U03-P05",
|
||||
"A1-U03-P06"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U03-C1",
|
||||
"kind": "main",
|
||||
"title": "早上买咖啡",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "咖啡店店员",
|
||||
"setting": "早上你在咖啡店,想买一杯热咖啡和一个三明治带走。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Good morning! What would you like?",
|
||||
"goal": "点一杯咖啡",
|
||||
"model": "I'd like a coffee, please.",
|
||||
"zh": "我想要一杯咖啡。",
|
||||
"itemIds": [
|
||||
"A1-U03-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Small or large?",
|
||||
"goal": "要大杯、热的",
|
||||
"model": "Large, please. Hot.",
|
||||
"zh": "大杯,热的。",
|
||||
"itemIds": [
|
||||
"A1-U03-W02",
|
||||
"A1-U03-W04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Milk and sugar?",
|
||||
"goal": "加奶,不加糖",
|
||||
"model": "With milk, please. No sugar.",
|
||||
"zh": "加奶,不要糖。",
|
||||
"itemIds": [
|
||||
"A1-U03-P03",
|
||||
"A1-U03-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Okay. Anything else? The chocolate cake is very sweet.",
|
||||
"goal": "不要蛋糕,加一个三明治",
|
||||
"model": "No, thanks. A sandwich, please.",
|
||||
"zh": "不用了,谢谢。要一个三明治。",
|
||||
"itemIds": [
|
||||
"A1-U03-W07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "For here or to go?",
|
||||
"goal": "说带走,并结束点单",
|
||||
"model": "To go, please. That's all, thanks.",
|
||||
"zh": "带走。就这些,谢谢。",
|
||||
"itemIds": [
|
||||
"A1-U03-P05",
|
||||
"A1-U03-P06"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-C2",
|
||||
"kind": "variant",
|
||||
"title": "先看菜单,替朋友点单",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "咖啡店店员",
|
||||
"setting": "你和朋友在咖啡店,你还没看菜单,店员已经来问了。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! Are you ready to order?",
|
||||
"goal": "说抱歉,请店员给你看菜单",
|
||||
"model": "Sorry, can I see the menu, please?",
|
||||
"zh": "抱歉,可以看一下菜单吗?",
|
||||
"itemIds": [
|
||||
"A1-U03-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Here you are. Drinks: tea, coffee and juice.",
|
||||
"goal": "给自己点一小杯橙汁",
|
||||
"model": "I'd like a small orange juice, please.",
|
||||
"zh": "我要一小杯橙汁。",
|
||||
"itemIds": [
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W01",
|
||||
"A1-U03-W06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "A cold orange juice. It's two dollars.",
|
||||
"goal": "再给朋友点一杯茶",
|
||||
"model": "Okay. And a cup of tea for my friend.",
|
||||
"zh": "好的。再给我朋友一杯茶。",
|
||||
"itemIds": [
|
||||
"A1-U03-W03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Hot tea. With milk?",
|
||||
"goal": "说不加奶,也不加糖",
|
||||
"model": "No milk. No sugar, please.",
|
||||
"zh": "不加奶,也不要糖。",
|
||||
"itemIds": [
|
||||
"A1-U03-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Okay. Anything else?",
|
||||
"goal": "说就这些",
|
||||
"model": "That's all, thanks.",
|
||||
"zh": "就这些,谢谢。",
|
||||
"itemIds": [
|
||||
"A1-U03-P06"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-C3",
|
||||
"kind": "variant",
|
||||
"title": "没有冰咖啡了",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "早餐店店员",
|
||||
"setting": "你想要冰咖啡,但店里今天没有。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello! What would you like?",
|
||||
"goal": "点一个三明治和一杯冰咖啡",
|
||||
"model": "I'd like a sandwich and a cold coffee, please.",
|
||||
"zh": "我要一个三明治和一杯冰咖啡。",
|
||||
"itemIds": [
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W07",
|
||||
"A1-U03-W05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, no cold coffee today. Hot coffee?",
|
||||
"goal": "说热的也行,要小杯",
|
||||
"model": "Okay, a small hot coffee, please.",
|
||||
"zh": "好吧,一小杯热咖啡。",
|
||||
"itemIds": [
|
||||
"A1-U03-W01",
|
||||
"A1-U03-W04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "With milk?",
|
||||
"goal": "说要加奶",
|
||||
"model": "Yes, with milk, please.",
|
||||
"zh": "要,加奶。",
|
||||
"itemIds": [
|
||||
"A1-U03-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "For here or to go? You can eat at this table.",
|
||||
"goal": "说在店里吃",
|
||||
"model": "For here, please.",
|
||||
"zh": "在这里吃。",
|
||||
"itemIds": [
|
||||
"A1-U03-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "That's six dollars. Enjoy your breakfast!",
|
||||
"goal": "付钱并道谢",
|
||||
"model": "Here you are. Thank you!",
|
||||
"zh": "给你。谢谢!",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U03-T1",
|
||||
"sceneId": "A1-U03-C1",
|
||||
"goal": "点一杯大杯热咖啡,说清加奶不加糖,再加一个三明治带走",
|
||||
"itemIds": [
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W02",
|
||||
"A1-U03-W04",
|
||||
"A1-U03-P03",
|
||||
"A1-U03-P04",
|
||||
"A1-U03-W07",
|
||||
"A1-U03-P05",
|
||||
"A1-U03-P06"
|
||||
],
|
||||
"slots": [
|
||||
"饮料",
|
||||
"大小",
|
||||
"冷热",
|
||||
"奶和糖",
|
||||
"带走或堂食"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-T2",
|
||||
"sceneId": "A1-U03-C2",
|
||||
"goal": "先要菜单,再给自己和朋友各点一杯饮料",
|
||||
"itemIds": [
|
||||
"A1-U03-P02",
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W01",
|
||||
"A1-U03-W06",
|
||||
"A1-U03-W03",
|
||||
"A1-U03-P04",
|
||||
"A1-U03-P06"
|
||||
],
|
||||
"slots": [
|
||||
"自己的饮料",
|
||||
"朋友的饮料"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-T3",
|
||||
"sceneId": "A1-U03-C3",
|
||||
"goal": "想要的饮料没有时换一个,并说在店里吃",
|
||||
"itemIds": [
|
||||
"A1-U03-P01",
|
||||
"A1-U03-W07",
|
||||
"A1-U03-W05",
|
||||
"A1-U03-W01",
|
||||
"A1-U03-W04",
|
||||
"A1-U03-P03",
|
||||
"A1-U03-P05"
|
||||
],
|
||||
"slots": [
|
||||
"替代的饮料",
|
||||
"堂食或带走"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U03-M1",
|
||||
"mode": "listening",
|
||||
"title": "咖啡店广播:介绍吃的喝的",
|
||||
"text": "Good morning, and welcome to our cafe! Would you like a hot drink or a cold drink? Our coffee is very good. A small cup is three dollars, a large cup is four dollars. Do you want it with milk? We also have food: fresh bread, cake, chocolate cookies, cheese sandwiches and chicken. There is orange juice and sweet apple cake too. It's all delicious! Is it for here or to go? Sit at a table, please. Your order is ready in one minute. Enjoy your breakfast!",
|
||||
"glosses": {
|
||||
"fresh": "新鲜的",
|
||||
"too": "也",
|
||||
"sit": "坐"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话主要介绍什么?",
|
||||
"options": [
|
||||
"咖啡店的饮料和吃的",
|
||||
"怎么坐公交车",
|
||||
"酒店的房间"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "大杯咖啡多少钱?",
|
||||
"options": [
|
||||
"四美元",
|
||||
"三美元",
|
||||
"五美元"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U03-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:点咖啡和点心",
|
||||
"text": "Ben: Good morning! Can I see the menu, please? I want some breakfast.\nStaff: Sure. Here you are.\nBen: I'd like a large coffee, hot, with milk. No sugar, please. And some bread.\nStaff: We have fresh bread, apple cake and chocolate cookies. They are all sweet.\nBen: One chocolate cake, please. Is the chicken sandwich good?\nStaff: Yes, it's delicious. The cheese one is nice too.\nBen: OK, a chicken sandwich. That's all, thanks. To go, please.\nStaff: Sit at that table for one minute. Your order is ready. Enjoy!\nBen: Thank you, my friend!",
|
||||
"glosses": {
|
||||
"staff": "员工,服务员",
|
||||
"fresh": "新鲜的",
|
||||
"too": "也",
|
||||
"sit": "坐"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Ben 的咖啡怎么做?",
|
||||
"options": [
|
||||
"大杯、热的、加奶、不加糖",
|
||||
"小杯、冷的、加糖",
|
||||
"中杯、加奶和糖"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "他点了哪种三明治?",
|
||||
"options": [
|
||||
"鸡肉的",
|
||||
"奶酪的",
|
||||
"没有点三明治"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "medium",
|
||||
"reason": "B1;A1 只学 small 和 large"
|
||||
},
|
||||
{
|
||||
"en": "latte / cappuccino",
|
||||
"reason": "CEFR-J 未收录,作为菜单专名由应用临时提示"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,955 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U04",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "问路",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "问地点、听懂左右和距离",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "245",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "246",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "117",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "21",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "146",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U04-P01",
|
||||
"type": "pattern",
|
||||
"en": "Where is the station?",
|
||||
"zh": "车站在哪里?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"where is",
|
||||
"where are"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. Where is the toilet?",
|
||||
"zh": "打扰一下,洗手间在哪里?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你想找一个地方,问它在哪里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P02",
|
||||
"type": "pattern",
|
||||
"en": "How do I get to the station?",
|
||||
"zh": "去车站怎么走?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how do i get to",
|
||||
"how can i get to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Yes, I'm lost. How do I get to the museum?",
|
||||
"zh": "是的,我迷路了。去博物馆怎么走?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你想去博物馆,问路人怎么走。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-W01",
|
||||
"type": "word",
|
||||
"en": "station",
|
||||
"zh": "车站",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"station"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is there a subway station near here?",
|
||||
"zh": "这附近有地铁站吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问火车站在哪里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-W02",
|
||||
"type": "word",
|
||||
"en": "toilet",
|
||||
"zh": "洗手间;厕所",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"toilet",
|
||||
"toilets"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. Where is the toilet?",
|
||||
"zh": "打扰一下,洗手间在哪里?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在商场里,问洗手间在哪里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P03",
|
||||
"type": "phrase",
|
||||
"en": "I'm lost.",
|
||||
"zh": "我迷路了。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "lost 在 CEFR-J 为 A2,但说明自己迷路是问路时最先要说的话",
|
||||
"match": [
|
||||
[
|
||||
"lost"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, I'm lost. Where is the station?",
|
||||
"zh": "抱歉,我迷路了。车站在哪里?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉路人你迷路了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-W03",
|
||||
"type": "word",
|
||||
"en": "left",
|
||||
"zh": "左边",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"left"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The bank is on the left.",
|
||||
"zh": "银行在左边。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“左边”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P04",
|
||||
"type": "pattern",
|
||||
"en": "Turn left.",
|
||||
"zh": "左转。(右转是 Turn right.)",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"turn left",
|
||||
"turn right"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Turn right at the corner.",
|
||||
"zh": "在拐角处右转。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉别人在路口右转。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P05",
|
||||
"type": "phrase",
|
||||
"en": "Go straight.",
|
||||
"zh": "直走。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"go straight",
|
||||
"straight on"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Go straight and turn left.",
|
||||
"zh": "直走,然后左转。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉别人一直往前走。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P06",
|
||||
"type": "pattern",
|
||||
"en": "It's on the left.",
|
||||
"zh": "在左边。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"on the left",
|
||||
"on the right",
|
||||
"on your left",
|
||||
"on your right"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The station is on the right.",
|
||||
"zh": "车站在右边。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉别人银行在右边。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P07",
|
||||
"type": "pattern",
|
||||
"en": "It's next to the bank.",
|
||||
"zh": "在银行旁边。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"next to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The station is next to the church.",
|
||||
"zh": "车站在教堂旁边。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉别人车站在教堂旁边。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P08",
|
||||
"type": "pattern",
|
||||
"en": "Is it far?",
|
||||
"zh": "远吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "far 在 CEFR-J 为 A2,但问远不远决定走路还是坐车",
|
||||
"match": [
|
||||
[
|
||||
"far"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is the museum far?",
|
||||
"zh": "博物馆远吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问路人那个地方远不远。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P09",
|
||||
"type": "pattern",
|
||||
"en": "It's ten minutes from here.",
|
||||
"zh": "离这里十分钟。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"minute",
|
||||
"minutes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "No, it's not far. It's five minutes from here.",
|
||||
"zh": "不远,离这里五分钟。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉别人走过去要五分钟。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-P10",
|
||||
"type": "pattern",
|
||||
"en": "Can you show me on the map?",
|
||||
"zh": "你能在地图上指给我看吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"show me"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Five minutes. Can you show me on the map?",
|
||||
"zh": "五分钟。你能在地图上指给我看吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你听不懂路线,请对方在地图上指给你看。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U04-R01",
|
||||
"en": "street",
|
||||
"zh": "街道"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R02",
|
||||
"en": "corner",
|
||||
"zh": "拐角"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R03",
|
||||
"en": "walk",
|
||||
"zh": "走路"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R04",
|
||||
"en": "bank",
|
||||
"zh": "银行"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R05",
|
||||
"en": "park",
|
||||
"zh": "公园"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R06",
|
||||
"en": "museum",
|
||||
"zh": "博物馆"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R07",
|
||||
"en": "hospital",
|
||||
"zh": "医院"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R08",
|
||||
"en": "church",
|
||||
"zh": "教堂"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R09",
|
||||
"en": "supermarket",
|
||||
"zh": "超市"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R10",
|
||||
"en": "post office",
|
||||
"zh": "邮局"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R11",
|
||||
"en": "building",
|
||||
"zh": "大楼"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R12",
|
||||
"en": "bridge",
|
||||
"zh": "桥"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R13",
|
||||
"en": "city",
|
||||
"zh": "城市"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R14",
|
||||
"en": "town",
|
||||
"zh": "小镇;城镇"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R15",
|
||||
"en": "block",
|
||||
"zh": "街区"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R16",
|
||||
"en": "find",
|
||||
"zh": "找到"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R17",
|
||||
"en": "end",
|
||||
"zh": "尽头"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R18",
|
||||
"en": "second",
|
||||
"zh": "第二"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R19",
|
||||
"en": "traffic light",
|
||||
"zh": "红绿灯"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R20",
|
||||
"en": "sign",
|
||||
"zh": "标志;指示牌"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R21",
|
||||
"en": "restaurant",
|
||||
"zh": "餐厅"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R22",
|
||||
"en": "subway",
|
||||
"zh": "地铁"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R23",
|
||||
"en": "train",
|
||||
"zh": "火车"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R24",
|
||||
"en": "library",
|
||||
"zh": "图书馆"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R25",
|
||||
"en": "river",
|
||||
"zh": "河"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R26",
|
||||
"en": "garden",
|
||||
"zh": "花园"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R27",
|
||||
"en": "way",
|
||||
"zh": "路;方向"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R28",
|
||||
"en": "stop",
|
||||
"zh": "车站;停"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R29",
|
||||
"en": "come",
|
||||
"zh": "来"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-R30",
|
||||
"en": "cinema",
|
||||
"zh": "电影院"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U04-S1",
|
||||
"title": "问地方在哪里",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U04-P03",
|
||||
"A1-U04-P01",
|
||||
"A1-U04-W01",
|
||||
"A1-U04-W02",
|
||||
"A1-U04-P02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. I'm lost. Where is the station? The train station? It's on this street. Look, there is the sign.",
|
||||
"question": "说话的人要找什么地方?",
|
||||
"options": [
|
||||
"火车站",
|
||||
"洗手间",
|
||||
"银行"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Sorry, I'm lost."
|
||||
},
|
||||
{
|
||||
"text": "Excuse me. Where is the station?"
|
||||
},
|
||||
{
|
||||
"text": "Where is the toilet?"
|
||||
},
|
||||
{
|
||||
"text": "How do I get to the museum?",
|
||||
"tip": "get to 连读,to 读得很轻"
|
||||
},
|
||||
{
|
||||
"text": "Is this the way to the station?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "TOWN MAP\nThe train station is next to the bank.\nThe museum is behind the church.\nThe cinema is behind the museum.\nThe toilets are in the park, next to the garden.\nThe library is between the post office and the supermarket.",
|
||||
"question": "博物馆在哪里?",
|
||||
"options": [
|
||||
"behind the church",
|
||||
"next to the bank",
|
||||
"in the park"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "你迷路了,写两句话:说自己迷路了,再问车站在哪里。",
|
||||
"example": "Sorry, I'm lost. Where is the station?",
|
||||
"itemIds": [
|
||||
"A1-U04-P03",
|
||||
"A1-U04-P01",
|
||||
"A1-U04-W01"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "想一个你今天要去的地方,用英语问它在哪里、怎么去。",
|
||||
"itemIds": [
|
||||
"A1-U04-P01",
|
||||
"A1-U04-P02"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-S2",
|
||||
"title": "左转、右转、直走",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U04-W03",
|
||||
"A1-U04-P04",
|
||||
"A1-U04-P05",
|
||||
"A1-U04-P06",
|
||||
"A1-U04-P07"
|
||||
],
|
||||
"listening": {
|
||||
"text": "How do I get to the bank? Go straight. Turn left at the traffic lights. The bank is on the right, next to the hospital.",
|
||||
"question": "银行在哪里?",
|
||||
"options": [
|
||||
"右边,医院旁边",
|
||||
"左边,医院旁边",
|
||||
"右边,公园旁边"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Go straight."
|
||||
},
|
||||
{
|
||||
"text": "Turn left.",
|
||||
"tip": "left 结尾的 t 要轻轻带出来"
|
||||
},
|
||||
{
|
||||
"text": "Turn right at the corner."
|
||||
},
|
||||
{
|
||||
"text": "It's on the left."
|
||||
},
|
||||
{
|
||||
"text": "It's next to the bank."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mia: Excuse me. How do I get to the supermarket?\nTom: Go straight. Turn right at the second street. You can see a sign.\nMia: Turn right at the second street?\nTom: Yes. The supermarket is on the left, next to the restaurant.",
|
||||
"question": "超市在哪一边?",
|
||||
"options": [
|
||||
"on the left",
|
||||
"on the right",
|
||||
"at the second street"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写出两步指路:先直走,再在拐角左转。",
|
||||
"example": "Go straight. Turn left at the corner.",
|
||||
"itemIds": [
|
||||
"A1-U04-P05",
|
||||
"A1-U04-P04"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "从你家门口到最近的一家商店,用英语说两三步怎么走,最后说它在左边还是右边。",
|
||||
"itemIds": [
|
||||
"A1-U04-P05",
|
||||
"A1-U04-P04",
|
||||
"A1-U04-P06"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-S3",
|
||||
"title": "远不远、在地图上指给我看",
|
||||
"minutes": 11,
|
||||
"itemIds": [
|
||||
"A1-U04-P08",
|
||||
"A1-U04-P09",
|
||||
"A1-U04-P10"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Is it far? No, it's not far. It's ten minutes from here. You can walk.",
|
||||
"question": "走过去要多久?",
|
||||
"options": [
|
||||
"十分钟",
|
||||
"五分钟",
|
||||
"半小时"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Is it far?"
|
||||
},
|
||||
{
|
||||
"text": "It's five minutes from here."
|
||||
},
|
||||
{
|
||||
"text": "Can you show me on the map?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Anna: Excuse me. Is the museum far?\nTom: Yes, it's far. It's thirty minutes from here. Don't walk. Take the subway.\nAnna: Can you show me on the map?\nTom: Sure. Come and look. We are here. The subway stop is there, at the end of this street.",
|
||||
"question": "Tom 建议怎么去博物馆?",
|
||||
"options": [
|
||||
"take the subway",
|
||||
"walk",
|
||||
"take a bus"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写一句话问远不远,再请对方在地图上指给你看。",
|
||||
"example": "Is it far? Can you show me on the map?",
|
||||
"itemIds": [
|
||||
"A1-U04-P08",
|
||||
"A1-U04-P10"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你在地图上找不到酒店:问怎么去、远不远,再请对方指给你看。",
|
||||
"itemIds": [
|
||||
"A1-U04-P02",
|
||||
"A1-U04-P08",
|
||||
"A1-U04-P10"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U04-C1",
|
||||
"kind": "main",
|
||||
"title": "问去火车站的路",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "路人",
|
||||
"setting": "你拖着行李找不到火车站,向一位路人问路。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi. Can I help you?",
|
||||
"goal": "说迷路了,问火车站在哪里",
|
||||
"model": "Yes, please. I'm lost. Where is the train station?",
|
||||
"zh": "是的,谢谢。我迷路了。火车站在哪里?",
|
||||
"itemIds": [
|
||||
"A1-U04-P03",
|
||||
"A1-U04-P01",
|
||||
"A1-U04-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The station? Go straight and turn left at the bridge.",
|
||||
"goal": "复述确认:直走,然后左转",
|
||||
"model": "Go straight and turn left?",
|
||||
"zh": "直走,然后左转?",
|
||||
"itemIds": [
|
||||
"A1-U04-P05",
|
||||
"A1-U04-P04",
|
||||
"A1-U04-W03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. Then walk two blocks. The station is on the right, next to the church.",
|
||||
"goal": "确认位置,再问远不远",
|
||||
"model": "On the right, next to the church. Is it far?",
|
||||
"zh": "在右边,教堂旁边。远吗?",
|
||||
"itemIds": [
|
||||
"A1-U04-P06",
|
||||
"A1-U04-P07",
|
||||
"A1-U04-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "No, it's not far. It's five minutes from here.",
|
||||
"goal": "确认时间,请对方在地图上指给你看",
|
||||
"model": "Five minutes. Can you show me on the map?",
|
||||
"zh": "五分钟。你能在地图上指给我看吗?",
|
||||
"itemIds": [
|
||||
"A1-U04-P09",
|
||||
"A1-U04-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. We are here. The station is here.",
|
||||
"goal": "道谢",
|
||||
"model": "Great. Thank you very much!",
|
||||
"zh": "太好了,非常感谢!",
|
||||
"itemIds": []
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-C2",
|
||||
"kind": "variant",
|
||||
"title": "找洗手间",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "咖啡店店员",
|
||||
"setting": "你在一家咖啡店附近,急着找洗手间。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. Are you okay?",
|
||||
"goal": "问洗手间在哪里",
|
||||
"model": "Excuse me. Where is the toilet?",
|
||||
"zh": "打扰一下,洗手间在哪里?",
|
||||
"itemIds": [
|
||||
"A1-U04-P01",
|
||||
"A1-U04-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The toilets are in the building behind the cafe.",
|
||||
"goal": "问去那栋楼怎么走",
|
||||
"model": "How do I get to the building?",
|
||||
"zh": "那栋楼怎么走?",
|
||||
"itemIds": [
|
||||
"A1-U04-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Go straight. Then turn right at the corner. It's on the left.",
|
||||
"goal": "复述确认方向",
|
||||
"model": "Turn right, then on the left?",
|
||||
"zh": "右转,然后在左边?",
|
||||
"itemIds": [
|
||||
"A1-U04-P04",
|
||||
"A1-U04-P06",
|
||||
"A1-U04-W03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. It's the second building.",
|
||||
"goal": "问远不远",
|
||||
"model": "Is it far?",
|
||||
"zh": "远吗?",
|
||||
"itemIds": [
|
||||
"A1-U04-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "No. Two minutes.",
|
||||
"goal": "确认时间并道谢",
|
||||
"model": "Two minutes. Thanks!",
|
||||
"zh": "两分钟。谢谢!",
|
||||
"itemIds": [
|
||||
"A1-U04-P09"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-C3",
|
||||
"kind": "variant",
|
||||
"title": "去远处的博物馆",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "当地人",
|
||||
"setting": "你想去城市博物馆,但它离这里很远。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi there! You look lost. Can I help you find something?",
|
||||
"goal": "说是的,问去博物馆怎么走",
|
||||
"model": "Yes, I'm lost. How do I get to the museum?",
|
||||
"zh": "是的,我迷路了。去博物馆怎么走?",
|
||||
"itemIds": [
|
||||
"A1-U04-P03",
|
||||
"A1-U04-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The city museum? It's far. It's forty minutes from here.",
|
||||
"goal": "问附近有没有地铁站",
|
||||
"model": "Is there a subway station near here?",
|
||||
"zh": "这附近有地铁站吗?",
|
||||
"itemIds": [
|
||||
"A1-U04-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. Go straight. The subway station is next to the park.",
|
||||
"goal": "确认在公园旁边",
|
||||
"model": "Next to the park?",
|
||||
"zh": "在公园旁边?",
|
||||
"itemIds": [
|
||||
"A1-U04-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, on the left. Then take the subway. The museum stop is number six.",
|
||||
"goal": "请对方在地图上指出博物馆站",
|
||||
"model": "Can you show me on the map, please?",
|
||||
"zh": "你能在地图上指给我看吗?",
|
||||
"itemIds": [
|
||||
"A1-U04-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Look, it's here, near the river.",
|
||||
"goal": "道谢",
|
||||
"model": "Thank you very much!",
|
||||
"zh": "非常感谢!",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U04-T1",
|
||||
"sceneId": "A1-U04-C1",
|
||||
"goal": "说自己迷路,问到火车站的路线,确认左右、位置和距离",
|
||||
"itemIds": [
|
||||
"A1-U04-P03",
|
||||
"A1-U04-P01",
|
||||
"A1-U04-W01",
|
||||
"A1-U04-P05",
|
||||
"A1-U04-P04",
|
||||
"A1-U04-W03",
|
||||
"A1-U04-P06",
|
||||
"A1-U04-P07",
|
||||
"A1-U04-P08",
|
||||
"A1-U04-P09",
|
||||
"A1-U04-P10"
|
||||
],
|
||||
"slots": [
|
||||
"目的地",
|
||||
"路线",
|
||||
"距离"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-T2",
|
||||
"sceneId": "A1-U04-C2",
|
||||
"goal": "问洗手间在哪里并复述路线",
|
||||
"itemIds": [
|
||||
"A1-U04-P01",
|
||||
"A1-U04-W02",
|
||||
"A1-U04-P02",
|
||||
"A1-U04-P04",
|
||||
"A1-U04-P06",
|
||||
"A1-U04-W03",
|
||||
"A1-U04-P08",
|
||||
"A1-U04-P09"
|
||||
],
|
||||
"slots": [
|
||||
"地点",
|
||||
"左右"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-T3",
|
||||
"sceneId": "A1-U04-C3",
|
||||
"goal": "目的地很远时改问地铁站,并请对方在地图上指出",
|
||||
"itemIds": [
|
||||
"A1-U04-P03",
|
||||
"A1-U04-P02",
|
||||
"A1-U04-W01",
|
||||
"A1-U04-P07",
|
||||
"A1-U04-P10"
|
||||
],
|
||||
"slots": [
|
||||
"目的地",
|
||||
"交通方式"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U04-M1",
|
||||
"mode": "listening",
|
||||
"title": "路人给你指路",
|
||||
"text": "Excuse me. Do you need help? You look lost. The train station? OK, listen. Go straight down this street. Walk two blocks to the traffic light, then turn left at the bank. The station is at the end of the road, on the right, next to the church. It's not far — about ten minutes on foot. A big supermarket and a post office are near it too. You can see the sign for the subway there. Have a good trip!",
|
||||
"glosses": {
|
||||
"luck": "good luck 祝好运",
|
||||
"trip": "旅行",
|
||||
"foot": "on foot 步行",
|
||||
"road": "路",
|
||||
"big": "大的",
|
||||
"too": "也"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话在做什么?",
|
||||
"options": [
|
||||
"给人指去火车站的路",
|
||||
"介绍一家餐厅",
|
||||
"问多少钱"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "火车站在什么旁边?",
|
||||
"options": [
|
||||
"教堂旁边",
|
||||
"银行旁边",
|
||||
"公园旁边"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U04-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:问去博物馆的路",
|
||||
"text": "Anna: Excuse me, how do I get to the museum? I'm lost.\nMan: The museum? Go straight, cross the bridge, and turn right at the corner.\nAnna: Turn right at the corner. Is it far?\nMan: No, it's near — about five minutes. It's the second building on your left, next to the park.\nAnna: Is there a library near the museum too?\nMan: Yes, at the end of the street, near the river and the garden.\nAnna: Can you show me on the map?\nMan: Sure. We are here, in the town center. This is the way to the museum.\nAnna: Great. Is there a restaurant near there?\nMan: Yes, next to the cinema. Thank you, and have a nice day!",
|
||||
"glosses": {
|
||||
"cross": "穿过",
|
||||
"center": "中心",
|
||||
"man": "男人",
|
||||
"too": "也"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "去博物馆要在拐角怎么走?",
|
||||
"options": [
|
||||
"右转",
|
||||
"左转",
|
||||
"一直走"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "博物馆离这里远吗?",
|
||||
"options": [
|
||||
"不远,大约五分钟",
|
||||
"很远,要坐车",
|
||||
"不知道"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "opposite / across from",
|
||||
"reason": "A2 方位词,放到 A2 的描述地点单元"
|
||||
},
|
||||
{
|
||||
"en": "north / south / east / west",
|
||||
"reason": "A2;日常问路很少用方位"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,911 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U05",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "在餐厅点餐",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "点餐、问推荐、要账单",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "141",
|
||||
"use": "active",
|
||||
"reason": "只把 I'll have… 当作点菜的固定句块,不展开 will 的将来用法"
|
||||
},
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "239",
|
||||
"use": "receptive"
|
||||
},
|
||||
{
|
||||
"id": "142",
|
||||
"use": "receptive"
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
"use": "active"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U05-P01",
|
||||
"type": "phrase",
|
||||
"en": "A table for two, please.",
|
||||
"zh": "请给我们一张两人桌。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"table for"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "A table for four, please.",
|
||||
"zh": "请给我们一张四人桌。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你们两个人走进餐厅,告诉服务员要几人桌。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-P02",
|
||||
"type": "phrase",
|
||||
"en": "What do you recommend?",
|
||||
"zh": "你推荐什么?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "recommend 在 CEFR-J 为 B1,但在餐厅请人推荐是外出吃饭最常用的一句",
|
||||
"match": [
|
||||
[
|
||||
"recommend"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What do you recommend today?",
|
||||
"zh": "你今天推荐什么?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "你不知道点什么,请服务员推荐。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-P03",
|
||||
"type": "phrase",
|
||||
"en": "I'll have the fish.",
|
||||
"zh": "我要鱼。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"i will have"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'll have the soup, please.",
|
||||
"zh": "我要汤,谢谢。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉服务员你要牛肉。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-W01",
|
||||
"type": "word",
|
||||
"en": "soup",
|
||||
"zh": "汤",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"soup"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The soup is very hot.",
|
||||
"zh": "汤很烫。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“汤”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-W02",
|
||||
"type": "word",
|
||||
"en": "rice",
|
||||
"zh": "米饭",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"rice"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I have some rice, please?",
|
||||
"zh": "可以给我一些米饭吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "向服务员要一些米饭。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-W03",
|
||||
"type": "word",
|
||||
"en": "fish",
|
||||
"zh": "鱼",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"fish"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The fish is very good here.",
|
||||
"zh": "这里的鱼很好吃。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“鱼”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-W04",
|
||||
"type": "word",
|
||||
"en": "vegetables",
|
||||
"zh": "蔬菜",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"vegetable",
|
||||
"vegetables"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'd like some vegetables, please.",
|
||||
"zh": "我想要一些蔬菜。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你想要蔬菜。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-P04",
|
||||
"type": "phrase",
|
||||
"en": "Is it spicy?",
|
||||
"zh": "辣吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "spicy 在 CEFR-J 为 B1,但对习惯问辣度的中国游客点菜很关键",
|
||||
"match": [
|
||||
[
|
||||
"spicy"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is the chicken spicy?",
|
||||
"zh": "鸡肉辣吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "点菜前问这道菜辣不辣。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-P05",
|
||||
"type": "phrase",
|
||||
"en": "Some water, please.",
|
||||
"zh": "请给我一些水。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"water"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I have some water, please?",
|
||||
"zh": "可以给我一些水吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "向服务员要水。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-W05",
|
||||
"type": "word",
|
||||
"en": "hungry",
|
||||
"zh": "饿的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"hungry"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm very hungry.",
|
||||
"zh": "我很饿。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你很饿。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-P06",
|
||||
"type": "phrase",
|
||||
"en": "I'm full.",
|
||||
"zh": "我饱了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"i am full",
|
||||
"we are full"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "No dessert, thanks. I'm full.",
|
||||
"zh": "不要甜点了,谢谢。我饱了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "服务员问你要不要甜点,说你已经饱了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-P07",
|
||||
"type": "phrase",
|
||||
"en": "Can I have the bill, please?",
|
||||
"zh": "请给我账单。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "bill 在 CEFR-J 为 A2,但吃完结账必须会说",
|
||||
"match": [
|
||||
[
|
||||
"the bill",
|
||||
"the check"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. Can we have the bill, please?",
|
||||
"zh": "打扰一下,可以给我们账单吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "吃完了,向服务员要账单。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U05-R01",
|
||||
"en": "meal",
|
||||
"zh": "一顿饭"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R02",
|
||||
"en": "dinner",
|
||||
"zh": "晚饭"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R03",
|
||||
"en": "lunch",
|
||||
"zh": "午饭"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R04",
|
||||
"en": "waiter",
|
||||
"zh": "服务员"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R05",
|
||||
"en": "egg",
|
||||
"zh": "鸡蛋"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R06",
|
||||
"en": "meat",
|
||||
"zh": "肉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R07",
|
||||
"en": "beef",
|
||||
"zh": "牛肉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R08",
|
||||
"en": "potato",
|
||||
"zh": "土豆"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R09",
|
||||
"en": "tomato",
|
||||
"zh": "西红柿"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R10",
|
||||
"en": "pizza",
|
||||
"zh": "比萨饼"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R11",
|
||||
"en": "salad",
|
||||
"zh": "沙拉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R12",
|
||||
"en": "fruit",
|
||||
"zh": "水果"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R13",
|
||||
"en": "banana",
|
||||
"zh": "香蕉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R14",
|
||||
"en": "ice cream",
|
||||
"zh": "冰淇淋"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R15",
|
||||
"en": "glass",
|
||||
"zh": "玻璃杯"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R16",
|
||||
"en": "knife",
|
||||
"zh": "刀"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R17",
|
||||
"en": "fork",
|
||||
"zh": "叉子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R18",
|
||||
"en": "spoon",
|
||||
"zh": "勺子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R19",
|
||||
"en": "dessert",
|
||||
"zh": "甜点"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R20",
|
||||
"en": "special",
|
||||
"zh": "特别的;特色菜"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R21",
|
||||
"en": "try",
|
||||
"zh": "尝试"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R22",
|
||||
"en": "people",
|
||||
"zh": "人们"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R23",
|
||||
"en": "seat",
|
||||
"zh": "座位"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R24",
|
||||
"en": "dish",
|
||||
"zh": "一道菜"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R25",
|
||||
"en": "too",
|
||||
"zh": "太"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R26",
|
||||
"en": "smell",
|
||||
"zh": "闻起来"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R27",
|
||||
"en": "tip",
|
||||
"zh": "小费"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R28",
|
||||
"en": "hamburger",
|
||||
"zh": "汉堡包"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R29",
|
||||
"en": "butter",
|
||||
"zh": "黄油"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R30",
|
||||
"en": "ask",
|
||||
"zh": "问;请求"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-R31",
|
||||
"en": "sit",
|
||||
"zh": "坐"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U05-S1",
|
||||
"title": "入座与请人推荐",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U05-P01",
|
||||
"A1-U05-W05",
|
||||
"A1-U05-P02",
|
||||
"A1-U05-P03"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hello. How many people? A table for two, please. Sure. Sit here, please. Here is the menu, and some bread and butter.",
|
||||
"question": "他们要几人桌?",
|
||||
"options": [
|
||||
"两人桌",
|
||||
"三人桌",
|
||||
"四人桌"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "A table for two, please."
|
||||
},
|
||||
{
|
||||
"text": "I'm very hungry."
|
||||
},
|
||||
{
|
||||
"text": "What do you recommend?",
|
||||
"tip": "recommend 重音在最后的 mend"
|
||||
},
|
||||
{
|
||||
"text": "I'll have the fish, please.",
|
||||
"tip": "I'll 读作 /aɪl/,很短"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "TODAY'S SPECIAL\nFish with rice $15\nBeef with potatoes $18\nTomato soup $6\nEgg salad $7\nPizza $12\nDessert: banana ice cream $5",
|
||||
"question": "哪一道菜最便宜?",
|
||||
"options": [
|
||||
"tomato soup",
|
||||
"egg salad",
|
||||
"pizza"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:要一张两人桌,再说你要鱼。",
|
||||
"example": "A table for two, please. I'll have the fish.",
|
||||
"itemIds": [
|
||||
"A1-U05-P01",
|
||||
"A1-U05-P03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "想想你常去的一家餐厅,用英语请服务员推荐,然后点一道菜。",
|
||||
"itemIds": [
|
||||
"A1-U05-P02",
|
||||
"A1-U05-P03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-S2",
|
||||
"title": "点菜:汤、米饭、辣不辣",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U05-W01",
|
||||
"A1-U05-W02",
|
||||
"A1-U05-W03",
|
||||
"A1-U05-W04",
|
||||
"A1-U05-P04",
|
||||
"A1-U05-P05"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What would you like? I'll have the chicken soup and rice. Is the soup spicy? No, it isn't. It has meat and vegetables. And some water, please.",
|
||||
"question": "顾客点了什么?",
|
||||
"options": [
|
||||
"鸡汤和米饭",
|
||||
"鱼和米饭",
|
||||
"鸡汤和蔬菜"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "soup, rice, fish"
|
||||
},
|
||||
{
|
||||
"text": "I'd like some vegetables, please.",
|
||||
"tip": "vegetables 读作三个音节 /ˈvedʒtəblz/"
|
||||
},
|
||||
{
|
||||
"text": "Is it spicy?"
|
||||
},
|
||||
{
|
||||
"text": "Some water, please."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Waiter: Would you like to try the beef? It's very good.\nMia: Is it spicy?\nWaiter: Yes, very spicy.\nMia: Oh, no. I'll have the fish and some vegetables.\nWaiter: And to drink?\nMia: A glass of water, please.",
|
||||
"question": "Mia 点了什么主菜?",
|
||||
"options": [
|
||||
"the fish",
|
||||
"the beef",
|
||||
"a glass of water"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问鱼辣不辣,再要一些水。",
|
||||
"example": "Is the fish spicy? Some water, please.",
|
||||
"itemIds": [
|
||||
"A1-U05-P04",
|
||||
"A1-U05-P05",
|
||||
"A1-U05-W03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语点一份汤、米饭和蔬菜。",
|
||||
"itemIds": [
|
||||
"A1-U05-W01",
|
||||
"A1-U05-W02",
|
||||
"A1-U05-W04"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-S3",
|
||||
"title": "吃饱了,结账",
|
||||
"minutes": 10,
|
||||
"itemIds": [
|
||||
"A1-U05-P06",
|
||||
"A1-U05-P07"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Would you like some dessert? We have ice cream and fruit. No, thanks. I'm full. Can I have the bill, please?",
|
||||
"question": "顾客为什么不要甜点?",
|
||||
"options": [
|
||||
"已经吃饱了",
|
||||
"不喜欢冰淇淋",
|
||||
"甜点太贵"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I'm full."
|
||||
},
|
||||
{
|
||||
"text": "No dessert, thanks."
|
||||
},
|
||||
{
|
||||
"text": "Can I have the bill, please?",
|
||||
"tip": "bill 的 i 是短音,不要读成 beer"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Please wait here. The waiter will take you to a seat.\nLunch: 11 to 2. Dinner: 5 to 10.\nYou can pay by cash or card at your table. Please ask the waiter for the bill.\nNo tips, please.",
|
||||
"question": "怎么结账?",
|
||||
"options": [
|
||||
"在餐桌上向服务员要账单",
|
||||
"去门口付钱",
|
||||
"只能付现金"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你吃饱了,再要账单。",
|
||||
"example": "I'm full. Can I have the bill, please?",
|
||||
"itemIds": [
|
||||
"A1-U05-P06",
|
||||
"A1-U05-P07"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "回想上次在外面吃饭,用英语说你吃饱了并要账单。",
|
||||
"itemIds": [
|
||||
"A1-U05-P06",
|
||||
"A1-U05-P07"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U05-C1",
|
||||
"kind": "main",
|
||||
"title": "晚饭点菜",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "服务员",
|
||||
"setting": "你和朋友晚上走进一家餐厅吃饭。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello! How many people?",
|
||||
"goal": "说要两人桌",
|
||||
"model": "A table for two, please.",
|
||||
"zh": "两人桌,谢谢。",
|
||||
"itemIds": [
|
||||
"A1-U05-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Sit here, please. Here is the menu. Our special dish today is beef with potatoes.",
|
||||
"goal": "说你们很饿,请服务员推荐",
|
||||
"model": "Thank you. We're very hungry. What do you recommend?",
|
||||
"zh": "谢谢。我们很饿。你推荐什么?",
|
||||
"itemIds": [
|
||||
"A1-U05-W05",
|
||||
"A1-U05-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The fish is very good. It comes with rice and vegetables.",
|
||||
"goal": "问辣不辣",
|
||||
"model": "Is it spicy?",
|
||||
"zh": "辣吗?",
|
||||
"itemIds": [
|
||||
"A1-U05-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "No, it isn't.",
|
||||
"goal": "点鱼和一份汤",
|
||||
"model": "Great. I'll have the fish and a soup, please.",
|
||||
"zh": "好的,我要鱼和一份汤。",
|
||||
"itemIds": [
|
||||
"A1-U05-P03",
|
||||
"A1-U05-W03",
|
||||
"A1-U05-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "And to drink? Here is a spoon for the soup.",
|
||||
"goal": "要水",
|
||||
"model": "Some water, please.",
|
||||
"zh": "请给我一些水。",
|
||||
"itemIds": [
|
||||
"A1-U05-P05"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-C2",
|
||||
"kind": "variant",
|
||||
"title": "吃完结账",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "服务员",
|
||||
"setting": "你吃完午饭,想结账离开。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "How is your meal?",
|
||||
"goal": "说很好,你已经饱了",
|
||||
"model": "Very good, thank you. I'm full.",
|
||||
"zh": "很好,谢谢。我饱了。",
|
||||
"itemIds": [
|
||||
"A1-U05-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Would you like some dessert? We have ice cream, fruit and cake.",
|
||||
"goal": "拒绝并要账单",
|
||||
"model": "No, thanks. Can I have the bill, please?",
|
||||
"zh": "不用了,谢谢。可以给我账单吗?",
|
||||
"itemIds": [
|
||||
"A1-U05-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Here you are. Lunch for one, twelve dollars.",
|
||||
"goal": "问能不能刷卡",
|
||||
"model": "Can I pay by card?",
|
||||
"zh": "可以刷卡吗?",
|
||||
"itemIds": []
|
||||
},
|
||||
{
|
||||
"ai": "Yes, sure.",
|
||||
"goal": "道谢,说这里的汤和米饭很好",
|
||||
"model": "Thank you. The soup and rice are very good here.",
|
||||
"zh": "谢谢。这里的汤和米饭很好。",
|
||||
"itemIds": [
|
||||
"A1-U05-W01",
|
||||
"A1-U05-W02"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-C3",
|
||||
"kind": "variant",
|
||||
"title": "快餐店点汉堡",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "店员",
|
||||
"setting": "你在一家快餐店,很饿,但不太能吃辣。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! What would you like?",
|
||||
"goal": "说你很饿,要一个汉堡",
|
||||
"model": "I'm hungry. I'll have a hamburger, please.",
|
||||
"zh": "我饿了。我要一个汉堡。",
|
||||
"itemIds": [
|
||||
"A1-U05-W05",
|
||||
"A1-U05-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Beef or chicken? The chicken is spicy.",
|
||||
"goal": "说要牛肉的,鸡肉太辣",
|
||||
"model": "Beef, please. The chicken is too spicy for me.",
|
||||
"zh": "牛肉的。鸡肉对我来说太辣了。",
|
||||
"itemIds": [
|
||||
"A1-U05-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Would you like vegetables or potatoes with it?",
|
||||
"goal": "选蔬菜",
|
||||
"model": "Vegetables, please.",
|
||||
"zh": "蔬菜,谢谢。",
|
||||
"itemIds": [
|
||||
"A1-U05-W04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "And to drink?",
|
||||
"goal": "要水",
|
||||
"model": "Just some water, please.",
|
||||
"zh": "只要一些水,谢谢。",
|
||||
"itemIds": [
|
||||
"A1-U05-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. Here is your food, and a knife and fork.",
|
||||
"goal": "道谢",
|
||||
"model": "Thank you! It smells good.",
|
||||
"zh": "谢谢!闻起来很香。",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U05-T1",
|
||||
"sceneId": "A1-U05-C1",
|
||||
"goal": "要座位、请人推荐、问辣度并点主菜、汤和饮料",
|
||||
"itemIds": [
|
||||
"A1-U05-P01",
|
||||
"A1-U05-W05",
|
||||
"A1-U05-P02",
|
||||
"A1-U05-P04",
|
||||
"A1-U05-P03",
|
||||
"A1-U05-W03",
|
||||
"A1-U05-W01",
|
||||
"A1-U05-P05"
|
||||
],
|
||||
"slots": [
|
||||
"人数",
|
||||
"主菜",
|
||||
"饮料"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-T2",
|
||||
"sceneId": "A1-U05-C2",
|
||||
"goal": "吃完说饱了,拒绝甜点并结账",
|
||||
"itemIds": [
|
||||
"A1-U05-P06",
|
||||
"A1-U05-P07",
|
||||
"A1-U05-W01",
|
||||
"A1-U05-W02"
|
||||
],
|
||||
"slots": [
|
||||
"甜点",
|
||||
"账单"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-T3",
|
||||
"sceneId": "A1-U05-C3",
|
||||
"goal": "在快餐店按口味点餐",
|
||||
"itemIds": [
|
||||
"A1-U05-W05",
|
||||
"A1-U05-P03",
|
||||
"A1-U05-P04",
|
||||
"A1-U05-W04",
|
||||
"A1-U05-P05"
|
||||
],
|
||||
"slots": [
|
||||
"主食",
|
||||
"辣度",
|
||||
"配菜"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U05-M1",
|
||||
"mode": "listening",
|
||||
"title": "餐厅介绍今晚的菜",
|
||||
"text": "Good evening, and welcome to our restaurant. Please sit down; here is a seat for you. Tonight we have a special meal. First, a hot soup or a fresh salad with tomato. Then you can try beef with potato, chicken with rice, or a pizza. The fish is our best dish — it smells wonderful and is not spicy. For dessert, we have ice cream, fruit and banana cake. Here are your knife, fork and spoon, and a glass of water. Tell the waiter when you are ready. Enjoy your dinner!",
|
||||
"glosses": {
|
||||
"evening": "傍晚,晚上",
|
||||
"tell": "告诉",
|
||||
"tonight": "今晚",
|
||||
"best": "最好的",
|
||||
"wonderful": "很棒的",
|
||||
"fresh": "新鲜的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话主要说什么?",
|
||||
"options": [
|
||||
"餐厅今晚有什么菜",
|
||||
"怎么去餐厅",
|
||||
"餐厅几点关门"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "关于鱼,下面哪个对?",
|
||||
"options": [
|
||||
"很好吃,不辣",
|
||||
"很辣",
|
||||
"已经卖完了"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U05-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:在餐厅点餐",
|
||||
"text": "Waiter: Good evening. A table for two? Here are your seats.\nBen: Thank you. We're very hungry. What do you recommend?\nWaiter: The beef is our special dish today. The fish and the salad are good too.\nBen: I'll have the fish with rice and some vegetables. Is it spicy?\nWaiter: No, it isn't. And to drink?\nBen: A glass of water, please. Can I have a fork and a spoon?\nWaiter: Of course. Would you like a dessert? The ice cream is sweet and the fruit is fresh.\nBen: Ice cream, please. That was delicious. I'm full now. Can I have the bill?\nWaiter: Sure. Many people ask for the ice cream — it's the best!\nBen: Great. Here is a tip. Thank you!",
|
||||
"glosses": {
|
||||
"evening": "傍晚,晚上",
|
||||
"best": "最好的",
|
||||
"course": "of course 当然",
|
||||
"fresh": "新鲜的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "服务员今天推荐什么菜?",
|
||||
"options": [
|
||||
"牛肉",
|
||||
"汤",
|
||||
"香蕉"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "inference",
|
||||
"question": "Ben 为什么最后要账单?",
|
||||
"options": [
|
||||
"他吃完了,吃饱了",
|
||||
"菜太辣了",
|
||||
"他要换一张桌子"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "reservation / book a table",
|
||||
"reason": "B1;预订放到 A2 的预订单元"
|
||||
},
|
||||
{
|
||||
"en": "noodle",
|
||||
"reason": "CEFR-J 未收录,作为菜单专名由应用临时提示"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,990 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U06",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "家人与住处",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "介绍家人,说住在哪里和家里有什么",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "8",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "59",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "59-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "60",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "146",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "101",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U06-P01",
|
||||
"type": "phrase",
|
||||
"en": "This is my family.",
|
||||
"zh": "这是我的家人。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"my family"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "This is my family. These are my parents.",
|
||||
"zh": "这是我的家人。这两位是我的父母。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "给朋友看全家福,开头说一句介绍。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-W01",
|
||||
"type": "word",
|
||||
"en": "parents",
|
||||
"zh": "父母",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"parent",
|
||||
"parents"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My parents live in Beijing.",
|
||||
"zh": "我父母住在北京。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“我父母”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-P02",
|
||||
"type": "phrase",
|
||||
"en": "Do you have any brothers or sisters?",
|
||||
"zh": "你有兄弟姐妹吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"brothers or sisters",
|
||||
"brother or sister"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you have any brothers or sisters?",
|
||||
"zh": "你有兄弟姐妹吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问新朋友有没有兄弟姐妹。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-W02",
|
||||
"type": "word",
|
||||
"en": "husband",
|
||||
"zh": "丈夫",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"husband"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "This is my husband, David.",
|
||||
"zh": "这是我丈夫大卫。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "介绍你的丈夫。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-W03",
|
||||
"type": "word",
|
||||
"en": "wife",
|
||||
"zh": "妻子",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"wife"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My wife is a doctor.",
|
||||
"zh": "我妻子是医生。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "介绍你的妻子。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-W04",
|
||||
"type": "word",
|
||||
"en": "children",
|
||||
"zh": "孩子们",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"child",
|
||||
"children"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "We have two children.",
|
||||
"zh": "我们有两个孩子。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你有两个孩子。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-P03",
|
||||
"type": "phrase",
|
||||
"en": "What does he do?",
|
||||
"zh": "他是做什么工作的?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what does he do",
|
||||
"what does she do",
|
||||
"what do you do",
|
||||
"what does your"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What does your sister do?",
|
||||
"zh": "你姐姐是做什么工作的?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问朋友的妻子是做什么工作的。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-P04",
|
||||
"type": "phrase",
|
||||
"en": "I live in Shanghai.",
|
||||
"zh": "我住在上海。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"live in",
|
||||
"lives in"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My parents live in Beijing.",
|
||||
"zh": "我父母住在北京。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你住在哪个城市。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-P05",
|
||||
"type": "phrase",
|
||||
"en": "I live with my parents.",
|
||||
"zh": "我和父母一起住。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"live with",
|
||||
"lives with"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I live with my wife and children.",
|
||||
"zh": "我和妻子、孩子一起住。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你和谁一起住。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-W05",
|
||||
"type": "word",
|
||||
"en": "flat",
|
||||
"zh": "公寓",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"flat",
|
||||
"apartment"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I live in a small flat.",
|
||||
"zh": "我住在一个小公寓里。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你住在一个公寓里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-P06",
|
||||
"type": "phrase",
|
||||
"en": "There are two bedrooms.",
|
||||
"zh": "有两间卧室。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"there are",
|
||||
"there is"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "There is a big kitchen.",
|
||||
"zh": "有一个大厨房。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你家有三间卧室。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-W06",
|
||||
"type": "word",
|
||||
"en": "kitchen",
|
||||
"zh": "厨房",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"kitchen"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The kitchen is small.",
|
||||
"zh": "厨房很小。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“厨房”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-W07",
|
||||
"type": "word",
|
||||
"en": "big",
|
||||
"zh": "大的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"big"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Our house is very big.",
|
||||
"zh": "我们的房子很大。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你家的客厅很大。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U06-R01",
|
||||
"en": "grandmother",
|
||||
"zh": "祖母;外祖母"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R02",
|
||||
"en": "grandfather",
|
||||
"zh": "祖父;外祖父"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R03",
|
||||
"en": "aunt",
|
||||
"zh": "阿姨;姑姑"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R04",
|
||||
"en": "uncle",
|
||||
"zh": "叔叔;舅舅"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R05",
|
||||
"en": "cousin",
|
||||
"zh": "堂/表兄弟姐妹"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R06",
|
||||
"en": "baby",
|
||||
"zh": "婴儿"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R07",
|
||||
"en": "son",
|
||||
"zh": "儿子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R08",
|
||||
"en": "daughter",
|
||||
"zh": "女儿"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R09",
|
||||
"en": "house",
|
||||
"zh": "房子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R10",
|
||||
"en": "bedroom",
|
||||
"zh": "卧室"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R11",
|
||||
"en": "bathroom",
|
||||
"zh": "浴室;卫生间"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R12",
|
||||
"en": "living room",
|
||||
"zh": "客厅"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R13",
|
||||
"en": "window",
|
||||
"zh": "窗户"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R14",
|
||||
"en": "door",
|
||||
"zh": "门"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R15",
|
||||
"en": "sofa",
|
||||
"zh": "沙发"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R16",
|
||||
"en": "new",
|
||||
"zh": "新的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R17",
|
||||
"en": "bed",
|
||||
"zh": "床"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R18",
|
||||
"en": "floor",
|
||||
"zh": "楼层;地板"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R19",
|
||||
"en": "neighbor",
|
||||
"zh": "邻居"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R20",
|
||||
"en": "pet",
|
||||
"zh": "宠物"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R21",
|
||||
"en": "dog",
|
||||
"zh": "狗"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R22",
|
||||
"en": "cat",
|
||||
"zh": "猫"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R23",
|
||||
"en": "old",
|
||||
"zh": "老的;旧的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R24",
|
||||
"en": "young",
|
||||
"zh": "年轻的;年幼的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R25",
|
||||
"en": "little",
|
||||
"zh": "小的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R26",
|
||||
"en": "job",
|
||||
"zh": "工作"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R27",
|
||||
"en": "work",
|
||||
"zh": "工作;上班"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R28",
|
||||
"en": "doctor",
|
||||
"zh": "医生"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R29",
|
||||
"en": "teacher",
|
||||
"zh": "老师"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R30",
|
||||
"en": "student",
|
||||
"zh": "学生"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R31",
|
||||
"en": "hometown",
|
||||
"zh": "家乡"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R32",
|
||||
"en": "quiet",
|
||||
"zh": "安静的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R33",
|
||||
"en": "TV",
|
||||
"zh": "电视"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R34",
|
||||
"en": "home",
|
||||
"zh": "家"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-R35",
|
||||
"en": "married",
|
||||
"zh": "已婚的"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U06-S1",
|
||||
"title": "介绍家人",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U06-P01",
|
||||
"A1-U06-W01",
|
||||
"A1-U06-P02",
|
||||
"A1-U06-W02",
|
||||
"A1-U06-W03",
|
||||
"A1-U06-W04"
|
||||
],
|
||||
"listening": {
|
||||
"text": "This is my family. These are my parents. This is my wife, Anna, and these are our children, Ben and Lily. Ben is eight. Lily is a baby.",
|
||||
"question": "Ben 几岁?",
|
||||
"options": [
|
||||
"八岁",
|
||||
"六岁",
|
||||
"三岁"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "This is my family."
|
||||
},
|
||||
{
|
||||
"text": "These are my parents."
|
||||
},
|
||||
{
|
||||
"text": "This is my husband, David."
|
||||
},
|
||||
{
|
||||
"text": "This is my son, and this is my daughter."
|
||||
},
|
||||
{
|
||||
"text": "My wife and I have two children.",
|
||||
"tip": "children 的 ch 读 /tʃ/,结尾是 /ən/"
|
||||
},
|
||||
{
|
||||
"text": "Do you have any brothers or sisters?",
|
||||
"tip": "brothers or sisters 连读,or 很轻"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Hi, I'm Tom. This is my family.\nMy grandfather and grandmother are on the left. My grandfather is 80. He's very old.\nMy parents are next to them.\nMy aunt and uncle are on the right. Their son, my cousin Leo, is only two.\nI don't have any brothers or sisters.",
|
||||
"question": "Tom 有兄弟姐妹吗?",
|
||||
"options": [
|
||||
"没有",
|
||||
"有一个哥哥",
|
||||
"有一个妹妹"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:介绍你的父母,再问对方有没有兄弟姐妹。",
|
||||
"example": "These are my parents. Do you have any brothers or sisters?",
|
||||
"itemIds": [
|
||||
"A1-U06-W01",
|
||||
"A1-U06-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "拿出手机里一张家人的照片,用英语一个一个介绍。",
|
||||
"itemIds": [
|
||||
"A1-U06-P01",
|
||||
"A1-U06-W01",
|
||||
"A1-U06-W04"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-S2",
|
||||
"title": "做什么工作、住在哪里",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U06-P03",
|
||||
"A1-U06-P04",
|
||||
"A1-U06-P05"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Are you married? Yes. What does your wife do? She's a doctor. And you? I'm a teacher. We live in Shanghai with our two children.",
|
||||
"question": "说话人做什么工作?",
|
||||
"options": [
|
||||
"老师",
|
||||
"医生",
|
||||
"学生"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What does he do?",
|
||||
"tip": "does he 连读,he 的 h 可以很弱"
|
||||
},
|
||||
{
|
||||
"text": "She's a teacher."
|
||||
},
|
||||
{
|
||||
"text": "I live in Beijing."
|
||||
},
|
||||
{
|
||||
"text": "I live with my parents."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Nina: Where do you live, Sam?\nSam: I live in London, but my hometown is Sydney.\nNina: Do you live with your family?\nSam: No. My parents live in Sydney. I live with a friend. He's a student.\nNina: What's your job?\nSam: I work in a hospital. I'm a doctor.",
|
||||
"question": "Sam 和谁住在一起?",
|
||||
"options": [
|
||||
"with a friend",
|
||||
"with his parents",
|
||||
"in Sydney"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你住在哪个城市、和谁住。",
|
||||
"example": "I live in Shanghai. I live with my wife and children.",
|
||||
"itemIds": [
|
||||
"A1-U06-P04",
|
||||
"A1-U06-P05"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你家里的一个人:他或她做什么工作、住在哪里。",
|
||||
"itemIds": [
|
||||
"A1-U06-P03",
|
||||
"A1-U06-P04"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-S3",
|
||||
"title": "我住的地方",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U06-W05",
|
||||
"A1-U06-P06",
|
||||
"A1-U06-W06",
|
||||
"A1-U06-W07"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Is your flat big? No, it's small. It's on the second floor. There are two bedrooms, a kitchen, a bathroom and a small living room. It's quiet, and my neighbors are very nice.",
|
||||
"question": "这套公寓有几间卧室?",
|
||||
"options": [
|
||||
"两间",
|
||||
"三间",
|
||||
"一间"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I live in a small flat."
|
||||
},
|
||||
{
|
||||
"text": "There are two bedrooms.",
|
||||
"tip": "There are 连读成 /ðeərə/"
|
||||
},
|
||||
{
|
||||
"text": "The kitchen is very big."
|
||||
},
|
||||
{
|
||||
"text": "Is your house big?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Big family house\nThere are three bedrooms with big beds, and two bathrooms.\nThe kitchen is big. There is a sofa and a TV in the living room.\nThere is a small garden. Pets are OK: dogs and cats.\nIt's near a park and the subway.",
|
||||
"question": "关于宠物,这里怎么说?",
|
||||
"options": [
|
||||
"允许养宠物",
|
||||
"禁止养宠物",
|
||||
"没有提到宠物"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你住在一个小公寓里,里面有两间卧室。",
|
||||
"example": "I live in a small flat. There are two bedrooms.",
|
||||
"itemIds": [
|
||||
"A1-U06-W05",
|
||||
"A1-U06-P06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语介绍你现在住的地方:大不大、有几间卧室、厨房怎么样。",
|
||||
"itemIds": [
|
||||
"A1-U06-W05",
|
||||
"A1-U06-P06",
|
||||
"A1-U06-W06",
|
||||
"A1-U06-W07"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U06-C1",
|
||||
"kind": "main",
|
||||
"title": "和旅途中认识的朋友聊家人",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "刚认识的朋友",
|
||||
"setting": "你在火车上和刚认识的朋友聊天,对方拿出手机给你看家人的照片。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Look, this is my family. These are my parents, and this is my little sister. And this is our dog.",
|
||||
"goal": "也介绍你的家人",
|
||||
"model": "This is my family. This is my wife, and these are our children.",
|
||||
"zh": "这是我的家人。这是我妻子,这是我们的孩子。",
|
||||
"itemIds": [
|
||||
"A1-U06-P01",
|
||||
"A1-U06-W03",
|
||||
"A1-U06-W04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Your children are so young! What does your wife do?",
|
||||
"goal": "说妻子的工作,再问对方的妹妹做什么",
|
||||
"model": "She's a doctor. What does your sister do?",
|
||||
"zh": "她是医生。你妹妹是做什么的?",
|
||||
"itemIds": [
|
||||
"A1-U06-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "She's a student. Do you have any brothers or sisters?",
|
||||
"goal": "说你有一个哥哥,他和父母住在一起",
|
||||
"model": "Yes, a brother. He lives with my parents.",
|
||||
"zh": "有,一个哥哥。他和我父母住在一起。",
|
||||
"itemIds": [
|
||||
"A1-U06-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Where do you live?",
|
||||
"goal": "说你住在上海,家里是个大公寓",
|
||||
"model": "I live in Shanghai. We have a big flat.",
|
||||
"zh": "我住在上海。我们有一个大公寓。",
|
||||
"itemIds": [
|
||||
"A1-U06-P04",
|
||||
"A1-U06-W05",
|
||||
"A1-U06-W07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Nice. Is it quiet?",
|
||||
"goal": "说很安静,有三间卧室,厨房很大",
|
||||
"model": "Yes, it's quiet. There are three bedrooms, and the kitchen is big.",
|
||||
"zh": "是的,很安静。有三间卧室,厨房很大。",
|
||||
"itemIds": [
|
||||
"A1-U06-P06",
|
||||
"A1-U06-W06",
|
||||
"A1-U06-W07"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-C2",
|
||||
"kind": "variant",
|
||||
"title": "和民宿主人聊天",
|
||||
"learnerRole": "住客",
|
||||
"aiRole": "民宿主人",
|
||||
"setting": "你带家人住进一家小民宿,主人很爱聊天。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Welcome! Are you here with your family?",
|
||||
"goal": "说和丈夫、孩子一起来的",
|
||||
"model": "Yes, I'm here with my husband and children.",
|
||||
"zh": "是的,我和丈夫、孩子一起来的。",
|
||||
"itemIds": [
|
||||
"A1-U06-W02",
|
||||
"A1-U06-W04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "How nice. I live here with my parents and my grandmother.",
|
||||
"goal": "问主人有没有兄弟姐妹",
|
||||
"model": "Do you have any brothers or sisters?",
|
||||
"zh": "你有兄弟姐妹吗?",
|
||||
"itemIds": [
|
||||
"A1-U06-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, a brother. He lives in Tokyo. He's a doctor. And your husband?",
|
||||
"goal": "说丈夫是老师",
|
||||
"model": "He's a teacher.",
|
||||
"zh": "他是老师。",
|
||||
"itemIds": []
|
||||
},
|
||||
{
|
||||
"ai": "Where do you live?",
|
||||
"goal": "说你们住在北京,和父母住在一起",
|
||||
"model": "We live in Beijing, with my parents.",
|
||||
"zh": "我们住在北京,和我父母一起。",
|
||||
"itemIds": [
|
||||
"A1-U06-P04",
|
||||
"A1-U06-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Is your home big?",
|
||||
"goal": "说是个小公寓,有两间卧室",
|
||||
"model": "No, it's a small flat. There are two bedrooms.",
|
||||
"zh": "不大,是个小公寓,有两间卧室。",
|
||||
"itemIds": [
|
||||
"A1-U06-W05",
|
||||
"A1-U06-P06"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-C3",
|
||||
"kind": "variant",
|
||||
"title": "视频通话看新家",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "老朋友",
|
||||
"setting": "你刚搬进新家,和老朋友视频通话,带对方看房子。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! Is this your new home?",
|
||||
"goal": "说是新公寓,不大但很安静",
|
||||
"model": "Yes, this is my new flat. It's not big, but it's quiet.",
|
||||
"zh": "是的,这是我的新公寓。不大,但很安静。",
|
||||
"itemIds": [
|
||||
"A1-U06-W05",
|
||||
"A1-U06-W07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Show me the kitchen!",
|
||||
"goal": "说厨房很小,但有一扇大窗户和一扇新门",
|
||||
"model": "Here is the kitchen. It's small, but it has a big window and a new door.",
|
||||
"zh": "这是厨房。很小,但有一扇大窗户和一扇新门。",
|
||||
"itemIds": [
|
||||
"A1-U06-W06",
|
||||
"A1-U06-W07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Nice! Do you live with your family?",
|
||||
"goal": "说和妻子住,父母住在家乡",
|
||||
"model": "No, I live with my wife. My parents live in my hometown.",
|
||||
"zh": "不,我和妻子住。我父母住在我的家乡。",
|
||||
"itemIds": [
|
||||
"A1-U06-P05",
|
||||
"A1-U06-W03",
|
||||
"A1-U06-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "How are your parents?",
|
||||
"goal": "说他们很好,再问对方的哥哥现在做什么",
|
||||
"model": "They're fine. What does your brother do now?",
|
||||
"zh": "他们很好。你哥哥现在做什么工作?",
|
||||
"itemIds": [
|
||||
"A1-U06-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "He's a student. He lives with our aunt and uncle in London.",
|
||||
"goal": "回应一句",
|
||||
"model": "That's great.",
|
||||
"zh": "那太好了。",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U06-T1",
|
||||
"sceneId": "A1-U06-C1",
|
||||
"goal": "看照片介绍家人,互相问工作、兄弟姐妹、住在哪里和房子",
|
||||
"itemIds": [
|
||||
"A1-U06-P01",
|
||||
"A1-U06-W03",
|
||||
"A1-U06-W04",
|
||||
"A1-U06-P03",
|
||||
"A1-U06-P05",
|
||||
"A1-U06-P04",
|
||||
"A1-U06-W05",
|
||||
"A1-U06-W07",
|
||||
"A1-U06-P06",
|
||||
"A1-U06-W06"
|
||||
],
|
||||
"slots": [
|
||||
"家人",
|
||||
"工作",
|
||||
"住处"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-T2",
|
||||
"sceneId": "A1-U06-C2",
|
||||
"goal": "回答民宿主人关于家人和住处的问题,并反问一句",
|
||||
"itemIds": [
|
||||
"A1-U06-W02",
|
||||
"A1-U06-W04",
|
||||
"A1-U06-P02",
|
||||
"A1-U06-P04",
|
||||
"A1-U06-W01",
|
||||
"A1-U06-W05",
|
||||
"A1-U06-P06"
|
||||
],
|
||||
"slots": [
|
||||
"同行的人",
|
||||
"兄弟姐妹",
|
||||
"住处"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-T3",
|
||||
"sceneId": "A1-U06-C3",
|
||||
"goal": "带朋友看新家,说房子情况和与谁同住",
|
||||
"itemIds": [
|
||||
"A1-U06-W05",
|
||||
"A1-U06-W07",
|
||||
"A1-U06-W06",
|
||||
"A1-U06-P05",
|
||||
"A1-U06-W03",
|
||||
"A1-U06-W01",
|
||||
"A1-U06-P03"
|
||||
],
|
||||
"slots": [
|
||||
"房间",
|
||||
"同住的人"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U06-M1",
|
||||
"mode": "listening",
|
||||
"title": "朋友介绍全家照",
|
||||
"text": "Look, this is a photo of my family at our new house. These are my parents. My father is a doctor, and my mother is a teacher. This is my little sister; she is a student. Here are my grandmother and grandfather. My uncle, my aunt and my cousin live near us too. I have one brother. He is married and lives with his wife and their baby son and daughter. Our house has three bedrooms, a big living room with a sofa and a TV, a kitchen and two bathrooms. It's quiet, and our neighbor is very kind. I love my home!",
|
||||
"glosses": {
|
||||
"love": "很喜欢,爱",
|
||||
"photo": "照片",
|
||||
"kind": "善良的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话主要在做什么?",
|
||||
"options": [
|
||||
"介绍自己的家人和家",
|
||||
"说怎么去上海",
|
||||
"问房子多少钱"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "说话人的爸爸做什么工作?",
|
||||
"options": [
|
||||
"医生",
|
||||
"老师",
|
||||
"学生"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U06-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:聊各自的家",
|
||||
"text": "New friend: Do you have any brothers or sisters?\nYou: Yes, one brother. He lives with our grandmother and our parents.\nNew friend: Where do you live?\nYou: In a small flat in the city. There are two bedrooms, a living room and a little kitchen.\nNew friend: Is it new?\nYou: No, it's old, but I love it. There is a big window in my bedroom. Do you have a pet?\nYou: Yes, a dog and a cat. What about you?\nNew friend: I live with my husband and our young daughter. Our house is near the park.\nNew friend: What does your uncle do?\nYou: He has a new job. He works in a hospital. My aunt is a teacher.\nNew friend: That's nice.",
|
||||
"glosses": {
|
||||
"love": "很喜欢,爱",
|
||||
"week": "周,星期"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "“你”养了什么宠物?",
|
||||
"options": [
|
||||
"一只狗和一只猫",
|
||||
"一只鸟",
|
||||
"没有宠物"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "新朋友和谁住在一起?",
|
||||
"options": [
|
||||
"丈夫和小女儿",
|
||||
"父母",
|
||||
"一个人住"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "apartment",
|
||||
"reason": "A2;美式说法先在 match 里接受,A2 再正式教"
|
||||
},
|
||||
{
|
||||
"en": "rent / landlord",
|
||||
"reason": "A2–B1;租房放到 B1 的租住单元"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,930 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U07",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "公交与地铁",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "买票、问站点、确认下车",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "245",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "246",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "59-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "60",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "101",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "117",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U07-P01",
|
||||
"type": "phrase",
|
||||
"en": "A ticket to the airport, please.",
|
||||
"zh": "一张去机场的票。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"ticket to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "A ticket to the museum, please.",
|
||||
"zh": "一张去博物馆的票。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在售票处买一张去机场的票。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P02",
|
||||
"type": "phrase",
|
||||
"en": "Which bus goes to the museum?",
|
||||
"zh": "哪路车去博物馆?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"which bus",
|
||||
"which line",
|
||||
"which train"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Which train goes to the airport?",
|
||||
"zh": "哪趟车去机场?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问哪路公交车去火车站。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P03",
|
||||
"type": "phrase",
|
||||
"en": "Does this bus go to the station?",
|
||||
"zh": "这辆车去车站吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"does this bus go to",
|
||||
"does this train go to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Does this train go to the airport?",
|
||||
"zh": "这趟车去机场吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "上车前问司机这辆车去不去博物馆。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-W01",
|
||||
"type": "word",
|
||||
"en": "line",
|
||||
"zh": "(地铁)线路",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"line"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Take Line 2.",
|
||||
"zh": "坐 2 号线。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“坐 3 号线”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P04",
|
||||
"type": "phrase",
|
||||
"en": "Where do I change?",
|
||||
"zh": "我在哪里换乘?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"where do i change",
|
||||
"change at",
|
||||
"change to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Change to Line 5 at City Park.",
|
||||
"zh": "在城市公园站换乘 5 号线。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问在哪一站换乘。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P05",
|
||||
"type": "phrase",
|
||||
"en": "How many stops?",
|
||||
"zh": "坐几站?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how many stops"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "How many stops to the zoo?",
|
||||
"zh": "到动物园坐几站?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问到博物馆要坐几站。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P06",
|
||||
"type": "phrase",
|
||||
"en": "Where do I get off?",
|
||||
"zh": "我在哪里下车?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"get off"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Get off at the next stop.",
|
||||
"zh": "在下一站下车。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问去医院应该在哪里下车。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P07",
|
||||
"type": "phrase",
|
||||
"en": "Is this my stop?",
|
||||
"zh": "这是我要下的站吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"my stop"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me, is this my stop?",
|
||||
"zh": "打扰一下,这是我要下的站吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "车停了,问旁边的人是不是你要下的站。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P08",
|
||||
"type": "phrase",
|
||||
"en": "When is the last train?",
|
||||
"zh": "末班车是几点?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "last 作限定词在 CEFR-J 为 A2,但晚上出行必须会问末班车",
|
||||
"match": [
|
||||
[
|
||||
"last train",
|
||||
"last bus",
|
||||
"next train",
|
||||
"next bus"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "When is the next bus?",
|
||||
"zh": "下一班公交车是几点?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "晚上问末班地铁几点。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P09",
|
||||
"type": "phrase",
|
||||
"en": "It comes every ten minutes.",
|
||||
"zh": "每十分钟一班。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"every"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The train comes every five minutes.",
|
||||
"zh": "火车每五分钟一班。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说公交车每十五分钟一班。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-P10",
|
||||
"type": "phrase",
|
||||
"en": "Is this seat free?",
|
||||
"zh": "这个座位有人吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"seat free"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me, is this seat free?",
|
||||
"zh": "打扰一下,这个座位有人吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在车上问旁边的座位有没有人。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-W02",
|
||||
"type": "word",
|
||||
"en": "platform",
|
||||
"zh": "站台",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "platform 在 CEFR-J 为 B1,但坐火车找站台离不开这个词",
|
||||
"match": [
|
||||
[
|
||||
"platform"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The train is on Platform 4.",
|
||||
"zh": "火车在 4 号站台。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问火车在几号站台。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U07-R01",
|
||||
"en": "bike",
|
||||
"zh": "自行车"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R02",
|
||||
"en": "bicycle",
|
||||
"zh": "自行车"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R03",
|
||||
"en": "boat",
|
||||
"zh": "小船"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R04",
|
||||
"en": "ship",
|
||||
"zh": "船"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R05",
|
||||
"en": "trip",
|
||||
"zh": "旅程"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R06",
|
||||
"en": "travel",
|
||||
"zh": "出行;旅行"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R07",
|
||||
"en": "arrive",
|
||||
"zh": "到达"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R08",
|
||||
"en": "early",
|
||||
"zh": "早"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R09",
|
||||
"en": "late",
|
||||
"zh": "晚;迟到"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R10",
|
||||
"en": "hour",
|
||||
"zh": "小时"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R11",
|
||||
"en": "half",
|
||||
"zh": "一半"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R12",
|
||||
"en": "quarter",
|
||||
"zh": "一刻钟;四分之一"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R13",
|
||||
"en": "often",
|
||||
"zh": "经常"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R14",
|
||||
"en": "ride",
|
||||
"zh": "乘坐;骑"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R15",
|
||||
"en": "fast",
|
||||
"zh": "快的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R16",
|
||||
"en": "slow",
|
||||
"zh": "慢的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R17",
|
||||
"en": "driver",
|
||||
"zh": "司机"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R18",
|
||||
"en": "passenger",
|
||||
"zh": "乘客"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R19",
|
||||
"en": "tram",
|
||||
"zh": "有轨电车"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R20",
|
||||
"en": "fare",
|
||||
"zh": "车费"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R21",
|
||||
"en": "side",
|
||||
"zh": "边"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R22",
|
||||
"en": "miss",
|
||||
"zh": "错过"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R23",
|
||||
"en": "catch",
|
||||
"zh": "赶上"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R24",
|
||||
"en": "road",
|
||||
"zh": "路"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R25",
|
||||
"en": "direction",
|
||||
"zh": "方向"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R26",
|
||||
"en": "single",
|
||||
"zh": "单程的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R27",
|
||||
"en": "return",
|
||||
"zh": "往返的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R28",
|
||||
"en": "car",
|
||||
"zh": "汽车"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-R29",
|
||||
"en": "zoo",
|
||||
"zh": "动物园"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U07-S1",
|
||||
"title": "买票、问坐哪路车",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U07-P01",
|
||||
"A1-U07-P02",
|
||||
"A1-U07-P03",
|
||||
"A1-U07-W01"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. Which bus goes to the museum? Bus 15. Does this bus go to the museum? No, it doesn't. Take the other bus, on the other side of the street.",
|
||||
"question": "去博物馆应该怎么做?",
|
||||
"options": [
|
||||
"到街对面坐车",
|
||||
"就坐这辆车",
|
||||
"改坐地铁"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "A ticket to the airport, please."
|
||||
},
|
||||
{
|
||||
"text": "A single ticket, please."
|
||||
},
|
||||
{
|
||||
"text": "Which bus goes to the station?"
|
||||
},
|
||||
{
|
||||
"text": "Does this bus go to the museum?",
|
||||
"tip": "Does this 连读,重音在 museum"
|
||||
},
|
||||
{
|
||||
"text": "Take Line 2."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Travel in the city\nSubway: fast. Trains come every five minutes.\nBus: slow, but cheap. Pay the driver. The fare is $2.\nTram: Line 7 goes to the river.\nBoat: a small ship on the river, every hour.\nBike: you can ride a bicycle in the park.\nCar: not easy, and very expensive.",
|
||||
"question": "哪种交通方式要把车费付给司机?",
|
||||
"options": [
|
||||
"bus",
|
||||
"tram",
|
||||
"boat"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:买一张去机场的票,再问哪路车去机场。",
|
||||
"example": "A ticket to the airport, please. Which bus goes to the airport?",
|
||||
"itemIds": [
|
||||
"A1-U07-P01",
|
||||
"A1-U07-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "想一个你常去的地方,用英语问哪路车去那里、这辆车去不去。",
|
||||
"itemIds": [
|
||||
"A1-U07-P02",
|
||||
"A1-U07-P03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-S2",
|
||||
"title": "换乘、坐几站、几号站台",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U07-P04",
|
||||
"A1-U07-P05",
|
||||
"A1-U07-P09",
|
||||
"A1-U07-W02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. How do I get to the zoo? Take Line 2. Change to Line 5 at City Park. How many stops? Five stops. Trains come every four minutes.",
|
||||
"question": "在哪一站换乘?",
|
||||
"options": [
|
||||
"城市公园站",
|
||||
"动物园站",
|
||||
"第五站"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Where do I change?"
|
||||
},
|
||||
{
|
||||
"text": "How many stops?"
|
||||
},
|
||||
{
|
||||
"text": "Change to Line 3."
|
||||
},
|
||||
{
|
||||
"text": "The train comes every five minutes.",
|
||||
"tip": "every 读两个音节 /ˈevri/"
|
||||
},
|
||||
{
|
||||
"text": "Which platform?",
|
||||
"tip": "platform 重音在前"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Lily: Which train goes to the airport?\nMark: The airport train. It's on Platform 4.\nLily: How often does it come?\nMark: Every fifteen minutes. It's fast. It takes half an hour.\nLily: Do I change?\nMark: No, you don't. It goes to the airport.",
|
||||
"question": "坐火车到机场要多久?",
|
||||
"options": [
|
||||
"half an hour",
|
||||
"fifteen minutes",
|
||||
"four minutes"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问在哪里换乘,再问坐几站。",
|
||||
"example": "Where do I change? How many stops?",
|
||||
"itemIds": [
|
||||
"A1-U07-P04",
|
||||
"A1-U07-P05"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你家附近的一条地铁或公交线路:几号线、多久一班。",
|
||||
"itemIds": [
|
||||
"A1-U07-W01",
|
||||
"A1-U07-P09"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-S3",
|
||||
"title": "找座位、下车、末班车",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U07-P10",
|
||||
"A1-U07-P06",
|
||||
"A1-U07-P07",
|
||||
"A1-U07-P08"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me, is this seat free? Yes, it is. Thanks. Where do I get off for the hospital? Get off at the next stop. Is this my stop? No, it's the next one.",
|
||||
"question": "去医院在哪里下车?",
|
||||
"options": [
|
||||
"下一站",
|
||||
"这一站",
|
||||
"终点站"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Is this seat free?"
|
||||
},
|
||||
{
|
||||
"text": "Where do I get off?",
|
||||
"tip": "get off 连读成 /ɡe tɒf/"
|
||||
},
|
||||
{
|
||||
"text": "Is this my stop?"
|
||||
},
|
||||
{
|
||||
"text": "When is the last train?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Last trains\nLine 1: 11:30\nLine 2: 11:45\nPlease arrive early. Do not be late!\nPassengers: this train goes in one direction only. After the last train, take a bus.",
|
||||
"question": "2 号线的末班车是几点?",
|
||||
"options": [
|
||||
"十一点四十五",
|
||||
"十一点半",
|
||||
"十二点"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问末班车几点,再问在哪里下车。",
|
||||
"example": "When is the last train? Where do I get off?",
|
||||
"itemIds": [
|
||||
"A1-U07-P08",
|
||||
"A1-U07-P06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你坐车去一个没去过的地方,用英语问座位、在哪里下车、是不是这一站。",
|
||||
"itemIds": [
|
||||
"A1-U07-P10",
|
||||
"A1-U07-P06",
|
||||
"A1-U07-P07"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U07-C1",
|
||||
"kind": "main",
|
||||
"title": "坐地铁去机场",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "地铁站工作人员",
|
||||
"setting": "你在地铁站,要坐地铁去机场。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. Can I help you?",
|
||||
"goal": "买一张去机场的票",
|
||||
"model": "Yes. A ticket to the airport, please.",
|
||||
"zh": "是的。一张去机场的票。",
|
||||
"itemIds": [
|
||||
"A1-U07-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Single or return? A single is three dollars.",
|
||||
"goal": "付钱,问哪条线去机场",
|
||||
"model": "Here you are. Which line goes to the airport?",
|
||||
"zh": "给你。哪条线去机场?",
|
||||
"itemIds": [
|
||||
"A1-U07-P02",
|
||||
"A1-U07-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Take Line 3 and change to the airport train.",
|
||||
"goal": "问在哪里换乘",
|
||||
"model": "Where do I change?",
|
||||
"zh": "我在哪里换乘?",
|
||||
"itemIds": [
|
||||
"A1-U07-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "At Bridge Street. Then take the airport train.",
|
||||
"goal": "问坐几站,是不是每十分钟一班",
|
||||
"model": "How many stops? Does it come every ten minutes?",
|
||||
"zh": "坐几站?每十分钟一班吗?",
|
||||
"itemIds": [
|
||||
"A1-U07-P05",
|
||||
"A1-U07-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Four stops. It comes every fifteen minutes. The last train leaves at a quarter past eleven.",
|
||||
"goal": "问在几号站台",
|
||||
"model": "OK. Which platform is it?",
|
||||
"zh": "好的。在几号站台?",
|
||||
"itemIds": [
|
||||
"A1-U07-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Platform 2. Have a good trip!",
|
||||
"goal": "道谢",
|
||||
"model": "Thanks!",
|
||||
"zh": "谢谢!",
|
||||
"itemIds": []
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-C2",
|
||||
"kind": "variant",
|
||||
"title": "公交车上确认下车",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "公交车司机",
|
||||
"setting": "你第一次坐这路公交车去博物馆,担心坐过站。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. Where are you going?",
|
||||
"goal": "问这辆车去不去博物馆",
|
||||
"model": "Does this bus go to the museum?",
|
||||
"zh": "这辆车去博物馆吗?",
|
||||
"itemIds": [
|
||||
"A1-U07-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, it does. Two dollars, please.",
|
||||
"goal": "付钱,问坐几站",
|
||||
"model": "Here you are. How many stops?",
|
||||
"zh": "给你。坐几站?",
|
||||
"itemIds": [
|
||||
"A1-U07-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Seven stops.",
|
||||
"goal": "问去博物馆在哪里下车",
|
||||
"model": "Where do I get off for the museum?",
|
||||
"zh": "去博物馆在哪里下车?",
|
||||
"itemIds": [
|
||||
"A1-U07-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Get off at Park Road. It's after the bridge. The bus is slow today, sorry.",
|
||||
"goal": "过了一会儿,问是不是这一站",
|
||||
"model": "Excuse me, is this my stop?",
|
||||
"zh": "打扰一下,这是我要下的站吗?",
|
||||
"itemIds": [
|
||||
"A1-U07-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "No, the next stop. Don't miss it!",
|
||||
"goal": "道谢",
|
||||
"model": "OK, thank you!",
|
||||
"zh": "好的,谢谢!",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-C3",
|
||||
"kind": "variant",
|
||||
"title": "深夜赶末班车",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "另一位乘客",
|
||||
"setting": "深夜你在火车站,担心赶不上回酒店的末班车。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Are you OK? You look lost.",
|
||||
"goal": "问去城市公园的末班车几点",
|
||||
"model": "Yes, thanks. When is the last train to City Park?",
|
||||
"zh": "是的,谢谢。去城市公园的末班车几点?",
|
||||
"itemIds": [
|
||||
"A1-U07-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "It leaves at half past eleven, from Platform 6. You can catch it.",
|
||||
"goal": "确认站台,问在哪里换乘",
|
||||
"model": "Platform 6? Where do I change?",
|
||||
"zh": "6 号站台?我在哪里换乘?",
|
||||
"itemIds": [
|
||||
"A1-U07-W02",
|
||||
"A1-U07-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "You don't change. It's Line 1. Just ride it for eight stops.",
|
||||
"goal": "确认线路和站数",
|
||||
"model": "Line 1, eight stops. Thank you.",
|
||||
"zh": "1 号线,八站。谢谢。",
|
||||
"itemIds": [
|
||||
"A1-U07-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Here is the train. Oh, it's very full.",
|
||||
"goal": "问旁边的人座位有没有人",
|
||||
"model": "Excuse me, is this seat free?",
|
||||
"zh": "打扰一下,这个座位有人吗?",
|
||||
"itemIds": [
|
||||
"A1-U07-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, it is. Where do you get off?",
|
||||
"goal": "说在城市公园下车,问远不远",
|
||||
"model": "At City Park. Is it far?",
|
||||
"zh": "在城市公园。远吗?",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U07-T1",
|
||||
"sceneId": "A1-U07-C1",
|
||||
"goal": "买票,问线路、换乘、站数、班次和站台",
|
||||
"itemIds": [
|
||||
"A1-U07-P01",
|
||||
"A1-U07-P02",
|
||||
"A1-U07-W01",
|
||||
"A1-U07-P04",
|
||||
"A1-U07-P05",
|
||||
"A1-U07-P09",
|
||||
"A1-U07-W02"
|
||||
],
|
||||
"slots": [
|
||||
"目的地",
|
||||
"线路",
|
||||
"换乘站",
|
||||
"站台"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-T2",
|
||||
"sceneId": "A1-U07-C2",
|
||||
"goal": "上车确认方向,问坐几站和在哪里下车",
|
||||
"itemIds": [
|
||||
"A1-U07-P03",
|
||||
"A1-U07-P05",
|
||||
"A1-U07-P06",
|
||||
"A1-U07-P07"
|
||||
],
|
||||
"slots": [
|
||||
"目的地",
|
||||
"下车站"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-T3",
|
||||
"sceneId": "A1-U07-C3",
|
||||
"goal": "深夜问末班车、站台和换乘,上车找座位",
|
||||
"itemIds": [
|
||||
"A1-U07-P08",
|
||||
"A1-U07-W02",
|
||||
"A1-U07-P04",
|
||||
"A1-U07-W01",
|
||||
"A1-U07-P10"
|
||||
],
|
||||
"slots": [
|
||||
"末班车时间",
|
||||
"站台",
|
||||
"座位"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U07-M1",
|
||||
"mode": "listening",
|
||||
"title": "公交车上的广播",
|
||||
"text": "Good afternoon, passengers. This is the airport bus. A single ticket is ten dollars; a return ticket is fifteen. The fare is the same all day. The bus comes every fifteen minutes, so you don't need to wait long. Please find a free seat and hold on. We ride fast on the big road, but there is a slow tram on the other side. The next stop is the train station. If you go to the zoo, change here. The last bus tonight is late, at eleven. Don't miss it! Thank you for taking our bus.",
|
||||
"glosses": {
|
||||
"tonight": "今晚",
|
||||
"afternoon": "下午",
|
||||
"same": "一样的",
|
||||
"long": "(时间)久",
|
||||
"hold": "抓牢;hold on 抓稳"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这是什么广播?",
|
||||
"options": [
|
||||
"机场公交车上的广播",
|
||||
"餐厅的广播",
|
||||
"商店的广播"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "这趟车多久来一班?",
|
||||
"options": [
|
||||
"每十五分钟",
|
||||
"每十分钟",
|
||||
"每小时"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U07-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:坐公交去机场",
|
||||
"text": "Anna: Excuse me, does this bus go to the station?\nDriver: No, this one goes to the airport. For the station you need the tram on the other side of the road.\nAnna: I don't want to be late. How many stops, and is it fast?\nDriver: Four stops. It's fast, about half an hour. We arrive early today.\nAnna: Good, I don't want to miss my plane. Where do I get off?\nDriver: It's the last stop. Is this seat free?\nAnna: Yes. How much is a single ticket?\nDriver: Ten dollars. A return is fifteen.\nAnna: A single, please. I often travel by bus; it's cheaper than a car or a taxi.\nDriver: Here you are. Have a good trip!\nAnna: Thank you!",
|
||||
"glosses": {
|
||||
"tell": "告诉",
|
||||
"half": "一半;half an hour 半小时",
|
||||
"than": "比",
|
||||
"cheaper": "更便宜",
|
||||
"plane": "飞机",
|
||||
"taxi": "出租车"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "去机场要坐几站?",
|
||||
"options": [
|
||||
"四站",
|
||||
"五站",
|
||||
"两站"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Anna 在哪一站下车?",
|
||||
"options": [
|
||||
"最后一站",
|
||||
"第一站",
|
||||
"火车站"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "transfer",
|
||||
"reason": "B1;A1 用 change 表达换乘"
|
||||
},
|
||||
{
|
||||
"en": "one-way / round trip",
|
||||
"reason": "CEFR-J 未收录;A1 用 single / return,应用可提示美式说法"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,924 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U08",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "超市与便利店",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "找商品、说数量",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "61",
|
||||
"use": "active",
|
||||
"reason": "只用 I'm looking for… 这个固定句块"
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "59-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "16",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U08-P01",
|
||||
"type": "phrase",
|
||||
"en": "Where can I find the milk?",
|
||||
"zh": "哪里能找到牛奶?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"where can i find"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. Where can I find the rice?",
|
||||
"zh": "打扰一下,哪里能找到大米?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在超市问哪里能找到糖。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P02",
|
||||
"type": "phrase",
|
||||
"en": "Do you have any yogurt?",
|
||||
"zh": "你们有酸奶吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"do you have"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you have any fresh bread?",
|
||||
"zh": "你们有新鲜面包吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问店员有没有冰块。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-W01",
|
||||
"type": "word",
|
||||
"en": "aisle",
|
||||
"zh": "(超市的)通道",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "aisle 在 CEFR-J 为 A2,但在超市找东西时店员几乎都会这样指路",
|
||||
"match": [
|
||||
[
|
||||
"aisle"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The rice is in aisle 4.",
|
||||
"zh": "大米在 4 号通道。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“在 3 号通道”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P03",
|
||||
"type": "phrase",
|
||||
"en": "It's on the top shelf.",
|
||||
"zh": "在最上层的架子上。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"top shelf",
|
||||
"bottom shelf"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The sugar is on the bottom shelf.",
|
||||
"zh": "糖在最下层的架子上。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉朋友牛奶在最下层。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P04",
|
||||
"type": "phrase",
|
||||
"en": "a bottle of water",
|
||||
"zh": "一瓶水",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"a bottle of",
|
||||
"bottles of",
|
||||
"a box of",
|
||||
"a bag of",
|
||||
"a piece of"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Two bottles of water, please.",
|
||||
"zh": "请给我两瓶水。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说要一盒鸡蛋。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P05",
|
||||
"type": "phrase",
|
||||
"en": "a kilo of apples",
|
||||
"zh": "一公斤苹果",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "kilo 在 CEFR-J 为 A2,但在市场买水果、蔬菜时按公斤说数量",
|
||||
"match": [
|
||||
[
|
||||
"kilo",
|
||||
"kilos"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Half a kilo of grapes, please.",
|
||||
"zh": "请给我半公斤葡萄。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在水果摊说要两公斤苹果。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P06",
|
||||
"type": "phrase",
|
||||
"en": "I need two bags.",
|
||||
"zh": "我需要两个袋子。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"i need",
|
||||
"we need"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I need a box of eggs.",
|
||||
"zh": "我需要一盒鸡蛋。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你需要一个袋子。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-W02",
|
||||
"type": "word",
|
||||
"en": "bag",
|
||||
"zh": "袋子",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"bag",
|
||||
"bags"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you need a bag?",
|
||||
"zh": "你需要袋子吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "结账时说你要一个袋子。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-W03",
|
||||
"type": "word",
|
||||
"en": "fresh",
|
||||
"zh": "新鲜的",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "fresh 在 CEFR-J 为 A2,但买水果、面包时常要问",
|
||||
"match": [
|
||||
[
|
||||
"fresh"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is the fish fresh?",
|
||||
"zh": "这鱼新鲜吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问这些水果新不新鲜。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P07",
|
||||
"type": "phrase",
|
||||
"en": "Where do I pay?",
|
||||
"zh": "在哪里付钱?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"where do i pay",
|
||||
"where can i pay"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Excuse me. Where do I pay?",
|
||||
"zh": "打扰一下,在哪里付钱?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "拿好东西后问在哪里付钱。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P08",
|
||||
"type": "phrase",
|
||||
"en": "What time do you close?",
|
||||
"zh": "你们几点关门?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what time do you close",
|
||||
"what time do you open"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What time do you open?",
|
||||
"zh": "你们几点开门?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问便利店几点开门。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-P09",
|
||||
"type": "phrase",
|
||||
"en": "I'm looking for eggs.",
|
||||
"zh": "我在找电池。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"looking for"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm looking for the bread.",
|
||||
"zh": "我在找面包。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉店员你在找牛奶。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U08-R01",
|
||||
"en": "box",
|
||||
"zh": "盒子;箱子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R02",
|
||||
"en": "piece",
|
||||
"zh": "一块;一片"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R03",
|
||||
"en": "list",
|
||||
"zh": "清单"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R04",
|
||||
"en": "pick",
|
||||
"zh": "挑选"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R05",
|
||||
"en": "put",
|
||||
"zh": "放"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R06",
|
||||
"en": "carry",
|
||||
"zh": "拿;提"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R07",
|
||||
"en": "heavy",
|
||||
"zh": "重的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R08",
|
||||
"en": "store",
|
||||
"zh": "商店"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R09",
|
||||
"en": "candy",
|
||||
"zh": "糖果"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R10",
|
||||
"en": "biscuit",
|
||||
"zh": "饼干"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R11",
|
||||
"en": "grape",
|
||||
"zh": "葡萄"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R12",
|
||||
"en": "corn",
|
||||
"zh": "玉米"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R13",
|
||||
"en": "bean",
|
||||
"zh": "豆子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R14",
|
||||
"en": "basket",
|
||||
"zh": "篮子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R15",
|
||||
"en": "item",
|
||||
"zh": "物品;件"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R16",
|
||||
"en": "sell",
|
||||
"zh": "卖"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R17",
|
||||
"en": "open",
|
||||
"zh": "开着的;营业"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R18",
|
||||
"en": "closed",
|
||||
"zh": "关着的;停止营业"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R19",
|
||||
"en": "bottom",
|
||||
"zh": "底部"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R20",
|
||||
"en": "year",
|
||||
"zh": "年"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R21",
|
||||
"en": "night",
|
||||
"zh": "夜晚"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R22",
|
||||
"en": "snack",
|
||||
"zh": "零食"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R23",
|
||||
"en": "soap",
|
||||
"zh": "肥皂"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R24",
|
||||
"en": "salt",
|
||||
"zh": "盐"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R25",
|
||||
"en": "battery",
|
||||
"zh": "电池"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R26",
|
||||
"en": "empty",
|
||||
"zh": "空的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R27",
|
||||
"en": "keep",
|
||||
"zh": "保留"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R28",
|
||||
"en": "plastic",
|
||||
"zh": "塑料的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-R29",
|
||||
"en": "whole",
|
||||
"zh": "整个的"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U08-S1",
|
||||
"title": "找东西",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U08-P01",
|
||||
"A1-U08-P02",
|
||||
"A1-U08-W01",
|
||||
"A1-U08-P03",
|
||||
"A1-U08-P09"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me. Where can I find the sugar? It's in aisle 4, on the bottom shelf. And do you have any candy? Yes, in aisle 7.",
|
||||
"question": "糖在哪里?",
|
||||
"options": [
|
||||
"4 号通道最下层",
|
||||
"7 号通道最下层",
|
||||
"4 号通道最上层"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Excuse me. Where can I find the milk?"
|
||||
},
|
||||
{
|
||||
"text": "I'm looking for the bread.",
|
||||
"tip": "looking for 连读,for 很轻"
|
||||
},
|
||||
{
|
||||
"text": "Do you have any yogurt?"
|
||||
},
|
||||
{
|
||||
"text": "It's in aisle 3.",
|
||||
"tip": "aisle 里的 s 不发音,读作 /aɪl/"
|
||||
},
|
||||
{
|
||||
"text": "It's on the top shelf."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Welcome to City Store\nAisle 1: fruit and vegetables\nAisle 2: bread and cake\nAisle 3: milk, cheese, yogurt and cream\nAisle 4: rice, beans, corn, sugar and salt\nAisle 5: candy, biscuits and snacks\nAisle 6: soap and batteries\nIce is in the box next to the door.",
|
||||
"question": "酸奶在哪个通道?",
|
||||
"options": [
|
||||
"aisle 3",
|
||||
"aisle 5",
|
||||
"aisle 6"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问哪里能找到牛奶,再问有没有酸奶。",
|
||||
"example": "Where can I find the milk? Do you have any yogurt?",
|
||||
"itemIds": [
|
||||
"A1-U08-P01",
|
||||
"A1-U08-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "想想你家附近的超市,用英语问三样东西在哪里。",
|
||||
"itemIds": [
|
||||
"A1-U08-P01",
|
||||
"A1-U08-P09",
|
||||
"A1-U08-W01"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-S2",
|
||||
"title": "说数量",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U08-P04",
|
||||
"A1-U08-P05",
|
||||
"A1-U08-P06",
|
||||
"A1-U08-W02",
|
||||
"A1-U08-W03"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Can I have a kilo of apples, please? Sure. Anything else? Yes, two bottles of water. Do you need a bag? Yes, please. I need two bags. They're heavy.",
|
||||
"question": "顾客买了几瓶水?",
|
||||
"options": [
|
||||
"两瓶",
|
||||
"一瓶",
|
||||
"三瓶"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "a bottle of water"
|
||||
},
|
||||
{
|
||||
"text": "a box of eggs"
|
||||
},
|
||||
{
|
||||
"text": "a kilo of apples",
|
||||
"tip": "kilo 读作 /ˈkiːləʊ/"
|
||||
},
|
||||
{
|
||||
"text": "Half a kilo, please."
|
||||
},
|
||||
{
|
||||
"text": "I need two bags."
|
||||
},
|
||||
{
|
||||
"text": "Is the bread fresh?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Shopping list\n- a kilo of apples\n- half a kilo of grapes\n- a box of eggs\n- two bottles of water\n- a piece of cheese\n- a whole chicken\nMia: Please keep the list, and don't buy candy!",
|
||||
"question": "清单上要买多少葡萄?",
|
||||
"options": [
|
||||
"half a kilo",
|
||||
"a piece of cheese",
|
||||
"a box of eggs"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写购物清单里的两行:一瓶水、一公斤苹果。",
|
||||
"example": "a bottle of water, a kilo of apples",
|
||||
"itemIds": [
|
||||
"A1-U08-P04",
|
||||
"A1-U08-P05"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你这周要买的三样东西和数量。",
|
||||
"itemIds": [
|
||||
"A1-U08-P04",
|
||||
"A1-U08-P05",
|
||||
"A1-U08-P06"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-S3",
|
||||
"title": "付钱和营业时间",
|
||||
"minutes": 10,
|
||||
"itemIds": [
|
||||
"A1-U08-P07",
|
||||
"A1-U08-P08",
|
||||
"A1-U08-W02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Where do I pay? Over there. What time do you close? We close at ten. We're open every day.",
|
||||
"question": "这家店几点关门?",
|
||||
"options": [
|
||||
"十点",
|
||||
"九点",
|
||||
"十一点"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Where do I pay?"
|
||||
},
|
||||
{
|
||||
"text": "What time do you close?",
|
||||
"tip": "do you 连读成 /djuː/"
|
||||
},
|
||||
{
|
||||
"text": "What time do you open?"
|
||||
},
|
||||
{
|
||||
"text": "Can I have a plastic bag?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Open every day: 7 to 11\nClosed on the first day of the new year\nPlease put your items in a basket. Please pay here.\nPlastic bags are 10 cents.",
|
||||
"question": "塑料袋多少钱?",
|
||||
"options": [
|
||||
"十美分一个",
|
||||
"免费",
|
||||
"一美元"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问在哪里付钱,再问几点关门。",
|
||||
"example": "Where do I pay? What time do you close?",
|
||||
"itemIds": [
|
||||
"A1-U08-P07",
|
||||
"A1-U08-P08"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "在便利店结账时,用英语要一个袋子,并问几点关门。",
|
||||
"itemIds": [
|
||||
"A1-U08-W02",
|
||||
"A1-U08-P08"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U08-C1",
|
||||
"kind": "main",
|
||||
"title": "在超市找东西",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "店员",
|
||||
"setting": "你在一家大超市,想买牛奶、酸奶和鸡蛋,但找不到。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi. Can I help you?",
|
||||
"goal": "问哪里能找到牛奶",
|
||||
"model": "Yes, please. Where can I find the milk?",
|
||||
"zh": "是的,谢谢。哪里能找到牛奶?",
|
||||
"itemIds": [
|
||||
"A1-U08-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The milk is in aisle 3, on the bottom shelf.",
|
||||
"goal": "确认通道,再问有没有新鲜酸奶",
|
||||
"model": "Aisle 3? And do you have any fresh yogurt?",
|
||||
"zh": "3 号通道?你们有新鲜酸奶吗?",
|
||||
"itemIds": [
|
||||
"A1-U08-W01",
|
||||
"A1-U08-P02",
|
||||
"A1-U08-W03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, next to the milk, on the top shelf.",
|
||||
"goal": "确认位置,再说你还在找鸡蛋",
|
||||
"model": "On the top shelf. Thanks. I'm also looking for eggs.",
|
||||
"zh": "最上层。谢谢。我还在找鸡蛋。",
|
||||
"itemIds": [
|
||||
"A1-U08-P03",
|
||||
"A1-U08-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Eggs are in aisle 5. Do you need a basket? You can't carry it all.",
|
||||
"goal": "说不用篮子,你有袋子",
|
||||
"model": "No, thanks. I have a bag.",
|
||||
"zh": "不用了,谢谢。我有袋子。",
|
||||
"itemIds": [
|
||||
"A1-U08-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. Anything else?",
|
||||
"goal": "问在哪里付钱",
|
||||
"model": "Where do I pay?",
|
||||
"zh": "在哪里付钱?",
|
||||
"itemIds": [
|
||||
"A1-U08-P07"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-C2",
|
||||
"kind": "variant",
|
||||
"title": "在水果摊买水果",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "摊主",
|
||||
"setting": "你在市场的水果摊买水果。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello! Fresh fruit today!",
|
||||
"goal": "问苹果新不新鲜",
|
||||
"model": "Are the apples fresh?",
|
||||
"zh": "苹果新鲜吗?",
|
||||
"itemIds": [
|
||||
"A1-U08-W03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Very fresh. Pick your apples. Two dollars a kilo.",
|
||||
"goal": "要两公斤苹果",
|
||||
"model": "Two kilos of apples, please.",
|
||||
"zh": "请给我两公斤苹果。",
|
||||
"itemIds": [
|
||||
"A1-U08-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Anything else? The grapes are very sweet.",
|
||||
"goal": "要半公斤葡萄",
|
||||
"model": "OK, half a kilo of grapes.",
|
||||
"zh": "好,半公斤葡萄。",
|
||||
"itemIds": [
|
||||
"A1-U08-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Here you are. It's heavy. Do you need a bag?",
|
||||
"goal": "说需要两个袋子",
|
||||
"model": "Yes, I need two bags, please.",
|
||||
"zh": "要,我需要两个袋子。",
|
||||
"itemIds": [
|
||||
"A1-U08-P06",
|
||||
"A1-U08-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Seven dollars, please.",
|
||||
"goal": "付钱",
|
||||
"model": "Here you are.",
|
||||
"zh": "给你。",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-C3",
|
||||
"kind": "variant",
|
||||
"title": "晚上去便利店",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "店员",
|
||||
"setting": "晚上你在酒店旁边的便利店买东西。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi there.",
|
||||
"goal": "说你在找电池",
|
||||
"model": "Hi. I'm looking for batteries.",
|
||||
"zh": "你好。我在找电池。",
|
||||
"itemIds": [
|
||||
"A1-U08-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, we don't sell batteries. We have snacks, drinks and soap.",
|
||||
"goal": "改要一瓶水",
|
||||
"model": "OK. A bottle of water, please.",
|
||||
"zh": "好吧。请给我一瓶水。",
|
||||
"itemIds": [
|
||||
"A1-U08-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The water is over there. Anything else?",
|
||||
"goal": "问几点关门",
|
||||
"model": "No, thanks. What time do you close?",
|
||||
"zh": "不用了,谢谢。你们几点关门?",
|
||||
"itemIds": [
|
||||
"A1-U08-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "We're open all night.",
|
||||
"goal": "问有没有冰块",
|
||||
"model": "Oh, good. Do you have any ice?",
|
||||
"zh": "哦,太好了。你们有冰块吗?",
|
||||
"itemIds": [
|
||||
"A1-U08-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, the ice box is empty now.",
|
||||
"goal": "回应",
|
||||
"model": "That's OK. Thank you.",
|
||||
"zh": "没关系,谢谢。",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U08-T1",
|
||||
"sceneId": "A1-U08-C1",
|
||||
"goal": "在超市问三样东西在哪里,确认通道和货架,最后问在哪里付钱",
|
||||
"itemIds": [
|
||||
"A1-U08-P01",
|
||||
"A1-U08-W01",
|
||||
"A1-U08-P02",
|
||||
"A1-U08-W03",
|
||||
"A1-U08-P03",
|
||||
"A1-U08-P09",
|
||||
"A1-U08-W02",
|
||||
"A1-U08-P07"
|
||||
],
|
||||
"slots": [
|
||||
"商品",
|
||||
"位置"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-T2",
|
||||
"sceneId": "A1-U08-C2",
|
||||
"goal": "在水果摊问新鲜程度,按公斤买水果并要袋子",
|
||||
"itemIds": [
|
||||
"A1-U08-W03",
|
||||
"A1-U08-P05",
|
||||
"A1-U08-P06",
|
||||
"A1-U08-W02"
|
||||
],
|
||||
"slots": [
|
||||
"水果",
|
||||
"数量",
|
||||
"袋子"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-T3",
|
||||
"sceneId": "A1-U08-C3",
|
||||
"goal": "在便利店找东西,找不到时换一样,并问营业时间",
|
||||
"itemIds": [
|
||||
"A1-U08-P09",
|
||||
"A1-U08-P04",
|
||||
"A1-U08-P08",
|
||||
"A1-U08-P02"
|
||||
],
|
||||
"slots": [
|
||||
"商品",
|
||||
"营业时间"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U08-M1",
|
||||
"mode": "listening",
|
||||
"title": "超市广播",
|
||||
"text": "Welcome to our store! We are open now, from morning to ten at night. Today we sell fresh fruit and vegetables. Grapes and apples are cheap. The candy, biscuits and snacks are on the top shelf; corn, beans and salt are at the bottom. Milk and yogurt are next to the eggs. Soap and batteries are near the door. Please take a basket and put your items in it. Some boxes are heavy, so carry them with care. Please keep your plastic bags. Pay at the front. Thank you, and have a nice day!",
|
||||
"glosses": {
|
||||
"forget": "忘记",
|
||||
"tonight": "今晚",
|
||||
"front": "前面;front desk 前台",
|
||||
"care": "with care 小心地",
|
||||
"shelf": "架子"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段广播主要说什么?",
|
||||
"options": [
|
||||
"超市里的商品和信息",
|
||||
"怎么坐地铁",
|
||||
"怎么点菜"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "糖果和零食放在哪里?",
|
||||
"options": [
|
||||
"最上层的架子",
|
||||
"门口",
|
||||
"最下面"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U08-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:在超市找东西",
|
||||
"text": "Mia: Excuse me, where can I find the milk?\nStaff: It's on the top shelf, next to the yogurt.\nMia: Thank you. I have a list. I need a box of eggs, a piece of cheese and some grapes.\nStaff: The eggs are in aisle three. The grapes are fresh today. Please pick the good ones.\nMia: OK. Where are the corn and the beans?\nStaff: At the bottom, near the salt. Do you need soap or batteries?\nMia: Yes, one soap. This basket is heavy now! Can you carry it?\nStaff: Sure. Do you want a plastic bag or a box?\nMia: A box, please. What time do you close? Are you open on Sunday?\nStaff: We close at ten, and yes, we're open every day of the year.\nMia: Great, thank you very much!",
|
||||
"glosses": {
|
||||
"staff": "员工,服务员",
|
||||
"front": "前面;front desk 前台",
|
||||
"shelf": "架子"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Mia 的清单上有什么?",
|
||||
"options": [
|
||||
"一盒鸡蛋、一块奶酪、一些葡萄",
|
||||
"面包和牛奶",
|
||||
"糖和零食"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "超市几点关门?",
|
||||
"options": [
|
||||
"十点",
|
||||
"九点",
|
||||
"十一点"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "checkout / cashier",
|
||||
"reason": "B1 或未收录;A1 用 Where do I pay? 表达"
|
||||
},
|
||||
{
|
||||
"en": "dozen",
|
||||
"reason": "B1;A1 用 a box of twelve eggs"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,974 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U09",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "描述人和感受",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说一个人的外貌和性格,说自己的心情",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "60",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "101",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "59-1",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "117",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U09-P01",
|
||||
"type": "phrase",
|
||||
"en": "What does she look like?",
|
||||
"zh": "她长什么样?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"look like"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What does your brother look like?",
|
||||
"zh": "你哥哥长什么样?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "朋友让你去接一个人,问那个人长什么样。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-W01",
|
||||
"type": "word",
|
||||
"en": "tall",
|
||||
"zh": "高的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"tall"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My father is very tall.",
|
||||
"zh": "我爸爸很高。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的朋友很高。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-W02",
|
||||
"type": "word",
|
||||
"en": "short",
|
||||
"zh": "矮的;短的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"short"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "She's short, and she has short hair.",
|
||||
"zh": "她个子矮,留着短发。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的孩子个子矮。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-P02",
|
||||
"type": "phrase",
|
||||
"en": "She has long hair.",
|
||||
"zh": "她留着长发。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"has long",
|
||||
"has short",
|
||||
"have long",
|
||||
"have short"
|
||||
],
|
||||
[
|
||||
"hair"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "He has short black hair.",
|
||||
"zh": "他留着黑色短发。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "描述一个留着长发的朋友。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-P03",
|
||||
"type": "phrase",
|
||||
"en": "He wears glasses.",
|
||||
"zh": "他戴眼镜。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"wears glasses",
|
||||
"wear glasses"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My mother wears glasses.",
|
||||
"zh": "我妈妈戴眼镜。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的老师戴眼镜。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-P04",
|
||||
"type": "phrase",
|
||||
"en": "What is he like?",
|
||||
"zh": "他(性格)怎么样?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what is he like",
|
||||
"what is she like",
|
||||
"what are they like",
|
||||
"what is your"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What is your new teacher like?",
|
||||
"zh": "你的新老师怎么样?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问朋友的新邻居性格怎么样。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-W03",
|
||||
"type": "word",
|
||||
"en": "kind",
|
||||
"zh": "友善的;好心的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"kind"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The man is very kind.",
|
||||
"zh": "那个男人很友善。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的邻居很友善。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-W04",
|
||||
"type": "word",
|
||||
"en": "funny",
|
||||
"zh": "有趣的;好笑的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"funny"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My son is very funny.",
|
||||
"zh": "我儿子很有趣。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的朋友很有趣。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-W05",
|
||||
"type": "word",
|
||||
"en": "shy",
|
||||
"zh": "害羞的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"shy"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "She's a little shy.",
|
||||
"zh": "她有点害羞。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的女儿有点害羞。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-P05",
|
||||
"type": "phrase",
|
||||
"en": "I'm tired.",
|
||||
"zh": "我累了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"tired"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm very tired today.",
|
||||
"zh": "我今天很累。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉朋友你今天很累。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-W06",
|
||||
"type": "word",
|
||||
"en": "happy",
|
||||
"zh": "开心的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"happy"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm very happy today.",
|
||||
"zh": "我今天很开心。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你今天很开心。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-P06",
|
||||
"type": "phrase",
|
||||
"en": "I feel sad.",
|
||||
"zh": "我觉得难过。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"feel",
|
||||
"feels"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "He feels sad today.",
|
||||
"zh": "他今天很难过。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉朋友你觉得难过。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-P07",
|
||||
"type": "phrase",
|
||||
"en": "I'm worried.",
|
||||
"zh": "我很担心。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "worried 在 CEFR-J 为 A2,但遇到麻烦时说明自己担心很常用",
|
||||
"match": [
|
||||
[
|
||||
"worried"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm worried about my son.",
|
||||
"zh": "我很担心我儿子。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "孩子走丢了,告诉工作人员你很担心。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U09-R01",
|
||||
"en": "thin",
|
||||
"zh": "瘦的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R02",
|
||||
"en": "handsome",
|
||||
"zh": "英俊的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R03",
|
||||
"en": "pretty",
|
||||
"zh": "漂亮的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R04",
|
||||
"en": "beautiful",
|
||||
"zh": "美丽的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R05",
|
||||
"en": "eye",
|
||||
"zh": "眼睛"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R06",
|
||||
"en": "face",
|
||||
"zh": "脸"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R07",
|
||||
"en": "clever",
|
||||
"zh": "聪明的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R08",
|
||||
"en": "smart",
|
||||
"zh": "聪明的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R09",
|
||||
"en": "lazy",
|
||||
"zh": "懒的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R10",
|
||||
"en": "angry",
|
||||
"zh": "生气的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R11",
|
||||
"en": "lonely",
|
||||
"zh": "孤单的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R12",
|
||||
"en": "afraid",
|
||||
"zh": "害怕的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R13",
|
||||
"en": "glad",
|
||||
"zh": "高兴的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R14",
|
||||
"en": "strong",
|
||||
"zh": "强壮的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R15",
|
||||
"en": "cute",
|
||||
"zh": "可爱的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R16",
|
||||
"en": "feeling",
|
||||
"zh": "感觉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R17",
|
||||
"en": "smile",
|
||||
"zh": "微笑"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R18",
|
||||
"en": "laugh",
|
||||
"zh": "笑"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R19",
|
||||
"en": "cry",
|
||||
"zh": "哭"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R20",
|
||||
"en": "love",
|
||||
"zh": "爱;喜欢"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R21",
|
||||
"en": "dark",
|
||||
"zh": "深色的;黑暗的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R22",
|
||||
"en": "black",
|
||||
"zh": "黑色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R23",
|
||||
"en": "brown",
|
||||
"zh": "棕色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R24",
|
||||
"en": "excited",
|
||||
"zh": "兴奋的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R25",
|
||||
"en": "boring",
|
||||
"zh": "无聊的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R26",
|
||||
"en": "nervous",
|
||||
"zh": "紧张的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R27",
|
||||
"en": "woman",
|
||||
"zh": "女人"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R28",
|
||||
"en": "man",
|
||||
"zh": "男人"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R29",
|
||||
"en": "boy",
|
||||
"zh": "男孩"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R30",
|
||||
"en": "girl",
|
||||
"zh": "女孩"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R31",
|
||||
"en": "guy",
|
||||
"zh": "家伙;人"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R32",
|
||||
"en": "surprised",
|
||||
"zh": "惊讶的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R33",
|
||||
"en": "interesting",
|
||||
"zh": "有意思的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-R34",
|
||||
"en": "let",
|
||||
"zh": "让(let's 一起……吧)"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U09-S1",
|
||||
"title": "长什么样",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U09-P01",
|
||||
"A1-U09-W01",
|
||||
"A1-U09-W02",
|
||||
"A1-U09-P02",
|
||||
"A1-U09-P03"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What does your brother look like? He's tall and thin. He has short black hair, and he wears glasses.",
|
||||
"question": "哥哥长什么样?",
|
||||
"options": [
|
||||
"高瘦,短黑发,戴眼镜",
|
||||
"矮个子,长黑发,戴眼镜",
|
||||
"高瘦,短黑发,不戴眼镜"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What does she look like?",
|
||||
"tip": "look like 连读,重音在 look"
|
||||
},
|
||||
{
|
||||
"text": "She's tall."
|
||||
},
|
||||
{
|
||||
"text": "He's short."
|
||||
},
|
||||
{
|
||||
"text": "She has long brown hair."
|
||||
},
|
||||
{
|
||||
"text": "He wears glasses."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Please help! We are looking for a little girl. Her name is Lucy. She is five.\nShe has a small face, long brown hair and beautiful big brown eyes. She's short.\nLucy is shy, but she has a big smile.",
|
||||
"question": "Lucy 长什么样?",
|
||||
"options": [
|
||||
"长棕发,棕色大眼睛",
|
||||
"短黑发,戴眼镜",
|
||||
"长黑发,个子很高"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话描述你的一位朋友:高还是矮、什么头发。",
|
||||
"example": "My friend is tall. She has long hair.",
|
||||
"itemIds": [
|
||||
"A1-U09-W01",
|
||||
"A1-U09-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语描述一个你家里的人:他或她长什么样。",
|
||||
"itemIds": [
|
||||
"A1-U09-P01",
|
||||
"A1-U09-W01",
|
||||
"A1-U09-W02",
|
||||
"A1-U09-P02",
|
||||
"A1-U09-P03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-S2",
|
||||
"title": "性格怎么样",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U09-P04",
|
||||
"A1-U09-W03",
|
||||
"A1-U09-W04",
|
||||
"A1-U09-W05"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What is your new teacher like? She's very kind and funny. The students love her. And your teacher? He's clever, but he's a little shy.",
|
||||
"question": "说话人的新老师怎么样?",
|
||||
"options": [
|
||||
"友善又有趣",
|
||||
"聪明但害羞",
|
||||
"严厉又无聊"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What is he like?",
|
||||
"tip": "不要和 What does he like? 混淆"
|
||||
},
|
||||
{
|
||||
"text": "She's very kind."
|
||||
},
|
||||
{
|
||||
"text": "He's funny."
|
||||
},
|
||||
{
|
||||
"text": "My son is a little shy."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Tom: What are your new neighbors like?\nMia: The man is very kind. He's strong, and he can carry my bags!\nTom: And the woman?\nMia: She's clever and funny. Her two boys are cute, but they are a little lazy.\nTom: Lazy?\nMia: Yes. They don't help their mother!",
|
||||
"question": "那个男人怎么样?",
|
||||
"options": [
|
||||
"very kind",
|
||||
"clever and funny",
|
||||
"a little lazy"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问朋友的新朋友怎么样,再说他很有趣。",
|
||||
"example": "What is your new friend like? He's very funny.",
|
||||
"itemIds": [
|
||||
"A1-U09-P04",
|
||||
"A1-U09-W04"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你最好的朋友的性格,至少用两个词。",
|
||||
"itemIds": [
|
||||
"A1-U09-P04",
|
||||
"A1-U09-W03",
|
||||
"A1-U09-W04",
|
||||
"A1-U09-W05"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-S3",
|
||||
"title": "说感受",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U09-P05",
|
||||
"A1-U09-W06",
|
||||
"A1-U09-P06",
|
||||
"A1-U09-P07"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hi, Ben. How are you? I'm tired. I have a lot of work today. And I feel sad. My cat is lost. Oh, no. I'm sorry.",
|
||||
"question": "Ben 为什么难过?",
|
||||
"options": [
|
||||
"他的猫走丢了",
|
||||
"他工作太多",
|
||||
"他生病了"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I'm tired."
|
||||
},
|
||||
{
|
||||
"text": "I'm very happy today."
|
||||
},
|
||||
{
|
||||
"text": "I feel sad."
|
||||
},
|
||||
{
|
||||
"text": "I'm worried.",
|
||||
"tip": "worried 读作 /ˈwʌrid/,不要读成 /ˈwɔːrid/"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Emma: How are you today?\nLily: I'm happy and excited! I have a new job. My old job is very boring.\nEmma: That's great! I'm surprised.\nLily: And you? How are you feeling?\nEmma: I'm a little nervous. I'm in a new city, and I feel lonely.\nLily: Don't be afraid. Come and have lunch with me!",
|
||||
"question": "Emma 为什么紧张?",
|
||||
"options": [
|
||||
"在新城市,觉得孤单",
|
||||
"找到了新工作",
|
||||
"午饭时间太晚"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话说你现在的感受:很累,但是开心。",
|
||||
"example": "I'm tired, but I'm happy.",
|
||||
"itemIds": [
|
||||
"A1-U09-P05",
|
||||
"A1-U09-W06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你今天的感受。",
|
||||
"itemIds": [
|
||||
"A1-U09-P06",
|
||||
"A1-U09-P05",
|
||||
"A1-U09-W06",
|
||||
"A1-U09-P07"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U09-C1",
|
||||
"kind": "main",
|
||||
"title": "帮朋友接人",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "朋友",
|
||||
"setting": "朋友请你去机场帮忙接她哥哥,你没见过他,打电话问清楚。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Can you get my brother at the airport today?",
|
||||
"goal": "答应,问他长什么样",
|
||||
"model": "Sure. What does he look like?",
|
||||
"zh": "没问题。他长什么样?",
|
||||
"itemIds": [
|
||||
"A1-U09-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "He's tall and handsome, and he has short dark hair.",
|
||||
"goal": "确认,再问他戴不戴眼镜",
|
||||
"model": "Tall, with short dark hair. Does he wear glasses?",
|
||||
"zh": "高个子,黑色短发。他戴眼镜吗?",
|
||||
"itemIds": [
|
||||
"A1-U09-W01",
|
||||
"A1-U09-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, he does.",
|
||||
"goal": "问他性格怎么样",
|
||||
"model": "OK. What is he like?",
|
||||
"zh": "好的。他人怎么样?",
|
||||
"itemIds": [
|
||||
"A1-U09-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "He's kind, but a little shy. He's a nice guy.",
|
||||
"goal": "说没关系,你也有点害羞",
|
||||
"model": "That's OK. I'm shy too.",
|
||||
"zh": "没关系,我也有点害羞。",
|
||||
"itemIds": [
|
||||
"A1-U09-W05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thank you! Are you OK? You look tired.",
|
||||
"goal": "说你有点累,但很乐意帮忙",
|
||||
"model": "I'm a little tired, but I'm happy to help.",
|
||||
"zh": "我有点累,但很乐意帮忙。",
|
||||
"itemIds": [
|
||||
"A1-U09-P05",
|
||||
"A1-U09-W06"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-C2",
|
||||
"kind": "variant",
|
||||
"title": "商场里孩子走丢了",
|
||||
"learnerRole": "家长",
|
||||
"aiRole": "商场服务台工作人员",
|
||||
"setting": "你在商场里和孩子走散了,去服务台求助。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. Are you OK?",
|
||||
"goal": "说你很担心,女儿走丢了",
|
||||
"model": "No. I'm worried. My daughter is lost.",
|
||||
"zh": "不好。我很担心,我女儿走丢了。",
|
||||
"itemIds": [
|
||||
"A1-U09-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Don't worry. What does she look like?",
|
||||
"goal": "描述:个子矮,长头发",
|
||||
"model": "She's five. She's short, and she has long hair.",
|
||||
"zh": "她五岁,个子矮,留着长发。",
|
||||
"itemIds": [
|
||||
"A1-U09-W02",
|
||||
"A1-U09-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Is her hair black or brown?",
|
||||
"goal": "说是棕色,眼睛也是棕色的",
|
||||
"model": "It's brown. And she has big brown eyes.",
|
||||
"zh": "棕色的。她还有一双棕色的大眼睛。",
|
||||
"itemIds": []
|
||||
},
|
||||
{
|
||||
"ai": "Is she shy?",
|
||||
"goal": "说她不害羞,很有趣,爱笑",
|
||||
"model": "No, she isn't shy. She's very funny. She loves to laugh.",
|
||||
"zh": "不,她不害羞,很有趣,很爱笑。",
|
||||
"itemIds": [
|
||||
"A1-U09-W05",
|
||||
"A1-U09-W04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Look! Is that her, the pretty little girl over there? She's crying.",
|
||||
"goal": "看到女儿,很开心",
|
||||
"model": "Yes! Oh, I'm so happy! Thank you!",
|
||||
"zh": "是她!太开心了!谢谢你!",
|
||||
"itemIds": [
|
||||
"A1-U09-W06"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-C3",
|
||||
"kind": "variant",
|
||||
"title": "朋友关心你的心情",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "朋友",
|
||||
"setting": "朋友发现你今天不太开心,关心你。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! Are you OK? You don't look happy.",
|
||||
"goal": "说你今天觉得难过",
|
||||
"model": "I feel sad today.",
|
||||
"zh": "我今天觉得难过。",
|
||||
"itemIds": [
|
||||
"A1-U09-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh, no. Is it your work?",
|
||||
"goal": "说是,你很累,工作很多",
|
||||
"model": "Yes. I'm very tired. I have a lot of work.",
|
||||
"zh": "是的。我很累,工作很多。",
|
||||
"itemIds": [
|
||||
"A1-U09-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I'm sorry. Are the people at work angry?",
|
||||
"goal": "说同事们很友善,但你担心新工作",
|
||||
"model": "No, they're kind, but I'm worried about my new job.",
|
||||
"zh": "不,他们很友善,但我担心我的新工作。",
|
||||
"itemIds": [
|
||||
"A1-U09-W03",
|
||||
"A1-U09-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Don't be afraid. You're smart, and your work is interesting.",
|
||||
"goal": "说谢谢,你现在觉得开心了",
|
||||
"model": "Thank you. I feel happy now.",
|
||||
"zh": "谢谢你。我现在觉得开心了。",
|
||||
"itemIds": [
|
||||
"A1-U09-P06",
|
||||
"A1-U09-W06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good! I'm glad. Let's have a cup of coffee.",
|
||||
"goal": "答应",
|
||||
"model": "OK, great!",
|
||||
"zh": "好啊!",
|
||||
"itemIds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U09-T1",
|
||||
"sceneId": "A1-U09-C1",
|
||||
"goal": "帮朋友接人:问清外貌和性格,回应对方的关心",
|
||||
"itemIds": [
|
||||
"A1-U09-P01",
|
||||
"A1-U09-W01",
|
||||
"A1-U09-P03",
|
||||
"A1-U09-P04",
|
||||
"A1-U09-W05",
|
||||
"A1-U09-P05",
|
||||
"A1-U09-W06"
|
||||
],
|
||||
"slots": [
|
||||
"外貌",
|
||||
"性格"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-T2",
|
||||
"sceneId": "A1-U09-C2",
|
||||
"goal": "孩子走丢时说明担心,描述孩子的外貌和性格",
|
||||
"itemIds": [
|
||||
"A1-U09-P07",
|
||||
"A1-U09-W02",
|
||||
"A1-U09-P02",
|
||||
"A1-U09-W05",
|
||||
"A1-U09-W04",
|
||||
"A1-U09-W06"
|
||||
],
|
||||
"slots": [
|
||||
"年龄",
|
||||
"外貌",
|
||||
"性格"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-T3",
|
||||
"sceneId": "A1-U09-C3",
|
||||
"goal": "朋友关心时说出自己的感受和原因",
|
||||
"itemIds": [
|
||||
"A1-U09-P06",
|
||||
"A1-U09-P05",
|
||||
"A1-U09-W03",
|
||||
"A1-U09-P07",
|
||||
"A1-U09-W06"
|
||||
],
|
||||
"slots": [
|
||||
"感受",
|
||||
"原因"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U09-M1",
|
||||
"mode": "listening",
|
||||
"title": "朋友让你去接人",
|
||||
"text": "Can you get my friend at the station? You don't know her, so listen. She is a young woman, tall and thin, with a pretty face and big brown eyes. Her hair is long and black. She wears glasses and always has a nice smile. She is very kind, clever and funny, but a little shy, so don't worry if she is quiet. Today she has a big red bag. She is a little tired after the trip, and maybe nervous in a new city, so please be kind. When you find her, give her a smile. Call me then. Thank you!",
|
||||
"glosses": {
|
||||
"call": "打电话",
|
||||
"know": "知道",
|
||||
"red": "红色",
|
||||
"always": "总是",
|
||||
"maybe": "也许",
|
||||
"give": "给"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话在做什么?",
|
||||
"options": [
|
||||
"描述一个人,好去接她",
|
||||
"介绍今天的天气",
|
||||
"点一份晚餐"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "这个朋友长什么样?",
|
||||
"options": [
|
||||
"高、瘦、长黑发、戴眼镜",
|
||||
"矮、短发、不戴眼镜",
|
||||
"高、卷发、戴帽子"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U09-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:聊聚会上的人",
|
||||
"text": "Tom: How was the party? You look happy and excited!\nMia: It was great, not boring at all! But I feel a little tired now.\nTom: Who is that handsome man, tall and strong, with the big smile?\nMia: That's my brother. He looks serious, but he is very funny and kind. Everyone loves him.\nTom: Is that clever girl his friend? She has beautiful eyes.\nMia: Yes. She is smart and cute. Are you OK? You look nervous.\nTom: I'm just shy at parties. Sometimes I feel lonely and a little afraid here.\nMia: Don't be afraid! Come and say hello. Everyone is glad to see new people.\nTom: OK. This party is interesting. Let's go and laugh together!\nMia: Great, that's the way!",
|
||||
"glosses": {
|
||||
"bit": "一点;a bit 有点",
|
||||
"party": "聚会",
|
||||
"parties": "聚会",
|
||||
"serious": "严肃的",
|
||||
"loves": "喜欢",
|
||||
"everyone": "每个人",
|
||||
"sometimes": "有时",
|
||||
"together": "一起"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "那个英俊的男人是谁?",
|
||||
"options": [
|
||||
"Mia 的哥哥",
|
||||
"Mia 的爸爸",
|
||||
"Tom 的朋友"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Tom 为什么显得紧张?",
|
||||
"options": [
|
||||
"他在聚会上有点害羞",
|
||||
"他很累",
|
||||
"他找不到 Mia"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "friendly",
|
||||
"reason": "B2;A1 用 kind / nice 表达"
|
||||
},
|
||||
{
|
||||
"en": "curly / blond / beard",
|
||||
"reason": "B1 或未收录;外貌先用 long / short / dark 描述"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,913 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U10",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "打车",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说目的地、确认价格或路线",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "117",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "246",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "146",
|
||||
"use": "receptive"
|
||||
},
|
||||
{
|
||||
"id": "120",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U10-P01",
|
||||
"type": "phrase",
|
||||
"en": "Take me to the airport, please.",
|
||||
"zh": "请带我去机场。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"take me to",
|
||||
"to the airport",
|
||||
"to the train station",
|
||||
"to the station",
|
||||
"to the hotel",
|
||||
"to this address"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "To the train station, please.",
|
||||
"zh": "请去火车站。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "上车后告诉司机去这个地址。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-W01",
|
||||
"type": "word",
|
||||
"en": "taxi",
|
||||
"zh": "出租车",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"taxi"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Let's take a taxi.",
|
||||
"zh": "我们打车吧。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说“一辆出租车”。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P02",
|
||||
"type": "phrase",
|
||||
"en": "How much is it to the airport?",
|
||||
"zh": "去机场多少钱?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how much is it to",
|
||||
"how much to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "How much is it to the station?",
|
||||
"zh": "去车站多少钱?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "上车前问去市中心要多少钱。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P03",
|
||||
"type": "phrase",
|
||||
"en": "How long does it take?",
|
||||
"zh": "要多长时间?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how long"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "How long does it take to the airport?",
|
||||
"zh": "去机场要多长时间?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问司机到酒店要多久。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P04",
|
||||
"type": "phrase",
|
||||
"en": "Can you stop here, please?",
|
||||
"zh": "请在这里停车。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"stop here",
|
||||
"stop at",
|
||||
"stop in front of"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Stop at the corner, please.",
|
||||
"zh": "请在拐角停车。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "到了,请司机在这里停车。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P05",
|
||||
"type": "phrase",
|
||||
"en": "I'm in a hurry.",
|
||||
"zh": "我赶时间。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"in a hurry"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, I'm in a hurry.",
|
||||
"zh": "抱歉,我赶时间。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉司机你赶时间。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P06",
|
||||
"type": "phrase",
|
||||
"en": "Please use the meter.",
|
||||
"zh": "请打表。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "meter 在 CEFR-J 为 A2,但在国外打车要求打表很重要",
|
||||
"match": [
|
||||
[
|
||||
"meter"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can you use the meter, please?",
|
||||
"zh": "可以打表吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "司机不打表,请他打表。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P07",
|
||||
"type": "phrase",
|
||||
"en": "Can you help me with my suitcase?",
|
||||
"zh": "能帮我拿一下行李箱吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "suitcase 在 CEFR-J 为 A2,但坐出租车去机场、酒店时常常用到",
|
||||
"match": [
|
||||
[
|
||||
"suitcase"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My suitcase is very heavy.",
|
||||
"zh": "我的行李箱很重。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请司机帮你拿行李箱。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P08",
|
||||
"type": "phrase",
|
||||
"en": "Is this the right way?",
|
||||
"zh": "这条路对吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"right way",
|
||||
"wrong way"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is this the right way to the museum?",
|
||||
"zh": "这是去博物馆的路吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "担心司机走错路,问这条路对不对。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P09",
|
||||
"type": "phrase",
|
||||
"en": "Keep the change.",
|
||||
"zh": "不用找了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"keep the change"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Here's twenty dollars. Keep the change.",
|
||||
"zh": "这是二十美元,不用找了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "付钱时告诉司机不用找零。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-W02",
|
||||
"type": "word",
|
||||
"en": "traffic",
|
||||
"zh": "路上的车辆;交通",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "traffic 在 CEFR-J 为 A2,但打车时经常要听懂“堵车”",
|
||||
"match": [
|
||||
[
|
||||
"traffic"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "There's a lot of traffic today.",
|
||||
"zh": "今天路上车很多。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说路上车很多。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-P10",
|
||||
"type": "phrase",
|
||||
"en": "Please drive carefully.",
|
||||
"zh": "请小心开车。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"drive carefully",
|
||||
"drive slowly"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Please drive slowly.",
|
||||
"zh": "请开慢一点。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "司机开得太快,请他小心开车。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U10-R01",
|
||||
"en": "careful",
|
||||
"zh": "小心的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R02",
|
||||
"en": "quickly",
|
||||
"zh": "快速地"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R03",
|
||||
"en": "front",
|
||||
"zh": "前面的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R04",
|
||||
"en": "back",
|
||||
"zh": "后面"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R05",
|
||||
"en": "pocket",
|
||||
"zh": "口袋"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R06",
|
||||
"en": "busy",
|
||||
"zh": "忙的;繁忙的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R07",
|
||||
"en": "highway",
|
||||
"zh": "公路;高速公路"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R08",
|
||||
"en": "radio",
|
||||
"zh": "收音机;电台"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R09",
|
||||
"en": "accident",
|
||||
"zh": "事故"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R10",
|
||||
"en": "jam",
|
||||
"zh": "堵塞"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R11",
|
||||
"en": "correct",
|
||||
"zh": "正确的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R12",
|
||||
"en": "different",
|
||||
"zh": "不同的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R13",
|
||||
"en": "place",
|
||||
"zh": "地方"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R14",
|
||||
"en": "already",
|
||||
"zh": "已经"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R15",
|
||||
"en": "almost",
|
||||
"zh": "几乎;快要"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R16",
|
||||
"en": "soon",
|
||||
"zh": "很快"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R17",
|
||||
"en": "yet",
|
||||
"zh": "还(没)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R18",
|
||||
"en": "outside",
|
||||
"zh": "外面"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R19",
|
||||
"en": "entrance",
|
||||
"zh": "入口"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R20",
|
||||
"en": "gate",
|
||||
"zh": "大门"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R21",
|
||||
"en": "belt",
|
||||
"zh": "带子(seat belt 安全带)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R22",
|
||||
"en": "center",
|
||||
"zh": "中心"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R23",
|
||||
"en": "famous",
|
||||
"zh": "著名的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-R24",
|
||||
"en": "tonight",
|
||||
"zh": "今晚"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U10-S1",
|
||||
"title": "说目的地、问价钱和时间",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U10-P01",
|
||||
"A1-U10-W01",
|
||||
"A1-U10-P02",
|
||||
"A1-U10-P03",
|
||||
"A1-U10-P05"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hi. To the airport, please. Sure. How much is it to the airport? About forty dollars. How long does it take? Thirty minutes. But there is a lot of traffic now.",
|
||||
"question": "去机场大约多少钱?",
|
||||
"options": [
|
||||
"四十美元左右",
|
||||
"三十美元左右",
|
||||
"十四美元左右"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Take me to the airport, please."
|
||||
},
|
||||
{
|
||||
"text": "Take me to this address, please."
|
||||
},
|
||||
{
|
||||
"text": "How much is it to the station?"
|
||||
},
|
||||
{
|
||||
"text": "How long does it take?",
|
||||
"tip": "does it 连读,take 重读"
|
||||
},
|
||||
{
|
||||
"text": "I'm in a hurry."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "TAXI\nPlease wait here for a taxi.\nTo the city center: about $45, 40 minutes.\nTo the train station: about $30, 25 minutes.\nAll taxis use the meter. Pay by cash or card.\nPlease use your seat belt.",
|
||||
"question": "从这里到火车站大约要多久?",
|
||||
"options": [
|
||||
"二十五分钟",
|
||||
"四十分钟",
|
||||
"三十分钟"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:告诉司机去机场,再问要多久。",
|
||||
"example": "To the airport, please. How long does it take?",
|
||||
"itemIds": [
|
||||
"A1-U10-P01",
|
||||
"A1-U10-P03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你要打车回酒店,用英语说目的地、问价格和时间。",
|
||||
"itemIds": [
|
||||
"A1-U10-P01",
|
||||
"A1-U10-P02",
|
||||
"A1-U10-P03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-S2",
|
||||
"title": "打表、确认路线",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U10-P06",
|
||||
"A1-U10-P08",
|
||||
"A1-U10-W02",
|
||||
"A1-U10-P10"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Please use the meter. OK. Is this the right way to the museum? Yes, but there is a lot of traffic on the bridge. We can take the highway. It's fast.",
|
||||
"question": "司机为什么要走公路?",
|
||||
"options": [
|
||||
"桥上车很多",
|
||||
"桥上有事故",
|
||||
"公路更便宜"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Please use the meter.",
|
||||
"tip": "meter 读作 /ˈmiːtə/"
|
||||
},
|
||||
{
|
||||
"text": "Is this the right way?"
|
||||
},
|
||||
{
|
||||
"text": "There's a lot of traffic."
|
||||
},
|
||||
{
|
||||
"text": "Please drive carefully."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Taxi tips\nTake a taxi from the taxi stop at the gate.\nSay, \"Please use the meter.\"\nHave the address on your phone or in your pocket.\nIs it a different way? Ask the driver, \"Is this the right way?\"",
|
||||
"question": "应该在哪里坐出租车?",
|
||||
"options": [
|
||||
"大门口的出租车站",
|
||||
"路边随便拦车",
|
||||
"酒店里面"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:请司机打表,再问路对不对。",
|
||||
"example": "Please use the meter. Is this the right way?",
|
||||
"itemIds": [
|
||||
"A1-U10-P06",
|
||||
"A1-U10-P08"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说路上车很多,请司机小心开车。",
|
||||
"itemIds": [
|
||||
"A1-U10-P10",
|
||||
"A1-U10-W02"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-S3",
|
||||
"title": "停车、付钱、拿行李",
|
||||
"minutes": 11,
|
||||
"itemIds": [
|
||||
"A1-U10-P04",
|
||||
"A1-U10-P07",
|
||||
"A1-U10-P09"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Can you stop here, please? Sure. That's twenty dollars. Here you are, thirty. Keep the change. Thank you. Can you help me with my suitcase? Sure.",
|
||||
"question": "乘客给了司机多少钱?",
|
||||
"options": [
|
||||
"三十美元",
|
||||
"二十美元",
|
||||
"五十美元"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Can you stop here, please?"
|
||||
},
|
||||
{
|
||||
"text": "Stop at the corner, please."
|
||||
},
|
||||
{
|
||||
"text": "Keep the change."
|
||||
},
|
||||
{
|
||||
"text": "Can you help me with my suitcase?",
|
||||
"tip": "suitcase 读作 /ˈsuːtkeɪs/"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Anna: Where are you?\nBen: I'm in a taxi. We are almost at the hotel.\nAnna: Great. The hotel entrance is on the other side. Ask the driver to stop in front of the hotel.\nBen: OK. See you soon.",
|
||||
"question": "应该让司机停在哪里?",
|
||||
"options": [
|
||||
"in front of the hotel",
|
||||
"on the other side",
|
||||
"at the corner"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:请司机在这里停车,再说不用找零。",
|
||||
"example": "Can you stop here, please? Keep the change.",
|
||||
"itemIds": [
|
||||
"A1-U10-P04",
|
||||
"A1-U10-P09"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "到了酒店,用英语请司机停车、帮你拿行李箱,再付钱。",
|
||||
"itemIds": [
|
||||
"A1-U10-P04",
|
||||
"A1-U10-P07",
|
||||
"A1-U10-P09"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U10-C1",
|
||||
"kind": "main",
|
||||
"title": "从机场打车去酒店",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "出租车司机",
|
||||
"setting": "你在机场坐上一辆出租车,要去市中心的酒店。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello! Where to? Please put your bag in the back.",
|
||||
"goal": "告诉司机去城市酒店",
|
||||
"model": "Take me to City Hotel, please.",
|
||||
"zh": "请带我去城市酒店。",
|
||||
"itemIds": [
|
||||
"A1-U10-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "City Hotel. OK.",
|
||||
"goal": "问多少钱、要多久",
|
||||
"model": "How much is it to the hotel? How long does it take?",
|
||||
"zh": "去酒店多少钱?要多久?",
|
||||
"itemIds": [
|
||||
"A1-U10-P02",
|
||||
"A1-U10-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "About forty dollars. Forty minutes, but there is a lot of traffic today.",
|
||||
"goal": "说你赶时间,请司机打表",
|
||||
"model": "I'm in a hurry. Please use the meter.",
|
||||
"zh": "我赶时间。请打表。",
|
||||
"itemIds": [
|
||||
"A1-U10-P05",
|
||||
"A1-U10-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. There is an accident on the bridge, and a traffic jam. Let's take the highway. It's fast.",
|
||||
"goal": "过了一会儿,问路对不对",
|
||||
"model": "Is this the right way?",
|
||||
"zh": "这条路对吗?",
|
||||
"itemIds": [
|
||||
"A1-U10-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, it's correct. Your hotel is in a famous place, near the river. It's almost here.",
|
||||
"goal": "请司机开慢一点、小心开",
|
||||
"model": "OK. Please drive carefully.",
|
||||
"zh": "好的。请小心开车。",
|
||||
"itemIds": [
|
||||
"A1-U10-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Here is City Hotel.",
|
||||
"goal": "请司机在这里停车,并帮你拿行李箱",
|
||||
"model": "Can you stop here and help me with my suitcase?",
|
||||
"zh": "能在这里停车,帮我拿一下行李箱吗?",
|
||||
"itemIds": [
|
||||
"A1-U10-P04",
|
||||
"A1-U10-P07"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-C2",
|
||||
"kind": "variant",
|
||||
"title": "打车赶火车",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "出租车司机",
|
||||
"setting": "你快赶不上火车了,在街上拦了一辆出租车。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi. It's cold outside! Where are you going?",
|
||||
"goal": "说去火车站,你赶时间",
|
||||
"model": "To the train station, please. I'm in a hurry.",
|
||||
"zh": "请去火车站。我赶时间。",
|
||||
"itemIds": [
|
||||
"A1-U10-P01",
|
||||
"A1-U10-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. Be careful with the door. What time is your train?",
|
||||
"goal": "说十点,问要多久",
|
||||
"model": "At ten. How long does it take?",
|
||||
"zh": "十点。要多久?",
|
||||
"itemIds": [
|
||||
"A1-U10-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "About fifteen minutes. There is a lot of traffic on the bridge.",
|
||||
"goal": "问有没有别的路",
|
||||
"model": "A lot of traffic? Is there another way?",
|
||||
"zh": "车很多?有别的路吗?",
|
||||
"itemIds": [
|
||||
"A1-U10-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. We can go through the park. I can drive quickly.",
|
||||
"goal": "请司机小心开车",
|
||||
"model": "Great, but please drive carefully.",
|
||||
"zh": "太好了,不过请小心开车。",
|
||||
"itemIds": [
|
||||
"A1-U10-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Here is the station, and you're not late yet. That's twelve dollars.",
|
||||
"goal": "付十五美元,不用找零",
|
||||
"model": "Here's fifteen. Keep the change.",
|
||||
"zh": "这是十五美元。不用找了。",
|
||||
"itemIds": [
|
||||
"A1-U10-P09"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-C3",
|
||||
"kind": "variant",
|
||||
"title": "司机不打表",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "出租车司机",
|
||||
"setting": "晚上你在酒店门口打车去一家餐厅,但司机不想打表。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi. Taxi? It's busy tonight.",
|
||||
"goal": "说要打车,给地址",
|
||||
"model": "Yes, a taxi, please. Take me to this address.",
|
||||
"zh": "是的,要打车。请带我去这个地址。",
|
||||
"itemIds": [
|
||||
"A1-U10-W01",
|
||||
"A1-U10-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "That restaurant? Twenty dollars.",
|
||||
"goal": "请司机打表",
|
||||
"model": "Please use the meter.",
|
||||
"zh": "请打表。",
|
||||
"itemIds": [
|
||||
"A1-U10-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The meter doesn't work today. It's the same price.",
|
||||
"goal": "问要多久",
|
||||
"model": "How long does it take?",
|
||||
"zh": "要多长时间?",
|
||||
"itemIds": [
|
||||
"A1-U10-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Ten minutes. Do you want the radio or some music?",
|
||||
"goal": "说太贵了,请司机在这里停车,你换一辆车",
|
||||
"model": "No, thanks. Can you stop here, please? I want a different taxi.",
|
||||
"zh": "不用了,谢谢。请在这里停车,我换一辆车。",
|
||||
"itemIds": [
|
||||
"A1-U10-P04",
|
||||
"A1-U10-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK, OK. The meter works now. It's already on.",
|
||||
"goal": "回应,再问这条路对不对",
|
||||
"model": "Thank you. Is this the right way to the restaurant?",
|
||||
"zh": "谢谢。这是去餐厅的路吗?",
|
||||
"itemIds": [
|
||||
"A1-U10-P08"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U10-T1",
|
||||
"sceneId": "A1-U10-C1",
|
||||
"goal": "从机场打车去酒店:说目的地、问价格和时间、要求打表、确认路线、下车拿行李",
|
||||
"itemIds": [
|
||||
"A1-U10-P01",
|
||||
"A1-U10-P02",
|
||||
"A1-U10-P03",
|
||||
"A1-U10-P05",
|
||||
"A1-U10-P06",
|
||||
"A1-U10-P08",
|
||||
"A1-U10-P10",
|
||||
"A1-U10-P04",
|
||||
"A1-U10-P07"
|
||||
],
|
||||
"slots": [
|
||||
"目的地",
|
||||
"价格",
|
||||
"路线"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-T2",
|
||||
"sceneId": "A1-U10-C2",
|
||||
"goal": "赶火车时说明赶时间,问时间和路线,付钱下车",
|
||||
"itemIds": [
|
||||
"A1-U10-P01",
|
||||
"A1-U10-P05",
|
||||
"A1-U10-P03",
|
||||
"A1-U10-W02",
|
||||
"A1-U10-P10",
|
||||
"A1-U10-P09"
|
||||
],
|
||||
"slots": [
|
||||
"目的地",
|
||||
"时间",
|
||||
"付款"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-T3",
|
||||
"sceneId": "A1-U10-C3",
|
||||
"goal": "司机不打表时坚持打表,必要时换车",
|
||||
"itemIds": [
|
||||
"A1-U10-W01",
|
||||
"A1-U10-P01",
|
||||
"A1-U10-P06",
|
||||
"A1-U10-P03",
|
||||
"A1-U10-P04",
|
||||
"A1-U10-P08"
|
||||
],
|
||||
"slots": [
|
||||
"目的地",
|
||||
"打表"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U10-M1",
|
||||
"mode": "listening",
|
||||
"title": "打车去机场",
|
||||
"text": "Hello, taxi? Good. Please take me to the airport quickly; I'm in a hurry. Is this the right way? Please use the meter. My bag is in the back; my phone is in my pocket. How much is it, and how long does it take? About thirty minutes? The traffic is busy tonight. The radio says there is an accident on the highway, so maybe there is a jam. Please drive carefully. Are we almost there? We are near the airport gate now. You can stop here at the entrance. Keep the change. Thank you!",
|
||||
"glosses": {
|
||||
"bad": "糟糕的",
|
||||
"meter": "计价器",
|
||||
"maybe": "也许"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "说话人要去哪里?",
|
||||
"options": [
|
||||
"机场",
|
||||
"火车站",
|
||||
"酒店"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "收音机里说高速路上怎么了?",
|
||||
"options": [
|
||||
"出了事故,可能堵车",
|
||||
"天气很好",
|
||||
"没有车"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U10-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:打车去酒店",
|
||||
"text": "Ben: Take me to this hotel, please. Can you use the meter?\nDriver: Sure. It's about twenty minutes, but the road is busy.\nBen: I'm in a hurry. Is this the right way? Is it the correct road?\nDriver: Yes, but there is a traffic jam near the city center. Let's take a different way.\nBen: OK. Please drive carefully. Are we almost there?\nDriver: Almost. The hotel is just in front of us. We arrive soon.\nBen: Great. Can you stop at the entrance? Please put my suitcase on the ground; don't drop it.\nDriver: Of course. That's fifteen dollars.\nBen: Here is twenty. Keep the change. Is the airport far from here for tonight?\nDriver: Not far. Call me anytime. Be careful with your bag!\nBen: Thank you!",
|
||||
"glosses": {
|
||||
"tomorrow": "明天",
|
||||
"course": "of course 当然",
|
||||
"different": "不同的",
|
||||
"ground": "地面",
|
||||
"anytime": "随时",
|
||||
"call": "打电话",
|
||||
"drop": "掉落"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "司机说路上情况怎么样?",
|
||||
"options": [
|
||||
"很堵,要换条路",
|
||||
"路很空",
|
||||
"下雨了"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "车费多少钱?",
|
||||
"options": [
|
||||
"十五美元",
|
||||
"二十美元",
|
||||
"三十美元"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "trunk / boot / luggage",
|
||||
"reason": "B1–B2;A1 用 suitcase 表达行李"
|
||||
},
|
||||
{
|
||||
"en": "route",
|
||||
"reason": "B2;A1 用 the right way 确认路线"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,967 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U11",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "我的一天",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说作息和常做的事",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "59",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "60",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "32",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "101",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "55",
|
||||
"use": "active",
|
||||
"reason": "只用 get up、go to bed 等固定搭配"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U11-P01",
|
||||
"type": "phrase",
|
||||
"en": "I get up at seven.",
|
||||
"zh": "我七点起床。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"get up",
|
||||
"gets up"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "He gets up at six thirty.",
|
||||
"zh": "他六点半起床。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你几点起床。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P02",
|
||||
"type": "phrase",
|
||||
"en": "What time do you get up?",
|
||||
"zh": "你几点起床?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what time do you",
|
||||
"what time does"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What time do you go to work?",
|
||||
"zh": "你几点去上班?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问新室友几点起床。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P03",
|
||||
"type": "phrase",
|
||||
"en": "I have breakfast at home.",
|
||||
"zh": "我在家吃早饭。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"have breakfast",
|
||||
"has breakfast",
|
||||
"have lunch",
|
||||
"has lunch",
|
||||
"have dinner",
|
||||
"has dinner"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I have lunch at twelve.",
|
||||
"zh": "我十二点吃午饭。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你在家吃早饭。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P04",
|
||||
"type": "phrase",
|
||||
"en": "I go to work at eight.",
|
||||
"zh": "我八点去上班。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"go to work",
|
||||
"goes to work",
|
||||
"go to school",
|
||||
"goes to school"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My son goes to school at eight.",
|
||||
"zh": "我儿子八点去上学。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你几点去上班。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-W01",
|
||||
"type": "word",
|
||||
"en": "usually",
|
||||
"zh": "通常",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"usually"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I usually go to work by bus.",
|
||||
"zh": "我通常坐公交车上班。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你通常晚上看电视。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-W02",
|
||||
"type": "word",
|
||||
"en": "always",
|
||||
"zh": "总是",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"always"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I always have breakfast.",
|
||||
"zh": "我总是吃早饭。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你总是早上洗澡。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-W03",
|
||||
"type": "word",
|
||||
"en": "never",
|
||||
"zh": "从不",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"never"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I never watch TV in the morning.",
|
||||
"zh": "我早上从不看电视。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你从不吃早饭。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-W04",
|
||||
"type": "word",
|
||||
"en": "sometimes",
|
||||
"zh": "有时候",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "sometimes 在 CEFR-J 为 B1,但描述习惯时和 always、usually、never 一样常用",
|
||||
"match": [
|
||||
[
|
||||
"sometimes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I sometimes cook dinner.",
|
||||
"zh": "我有时候做晚饭。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你有时候早上跑步。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P05",
|
||||
"type": "phrase",
|
||||
"en": "I watch TV in the evening.",
|
||||
"zh": "我晚上看电视。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"in the evening",
|
||||
"in the morning",
|
||||
"in the afternoon",
|
||||
"at night"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I run in the morning.",
|
||||
"zh": "我早上跑步。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你晚上常做的一件事。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P06",
|
||||
"type": "phrase",
|
||||
"en": "every day",
|
||||
"zh": "每天",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"every day"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I read the newspaper every day.",
|
||||
"zh": "我每天看报纸。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你每天都喝咖啡。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P07",
|
||||
"type": "phrase",
|
||||
"en": "I take a shower.",
|
||||
"zh": "我洗个澡。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"take a shower",
|
||||
"takes a shower"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I always take a shower in the morning.",
|
||||
"zh": "我总是早上洗澡。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你早上先洗澡。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P08",
|
||||
"type": "phrase",
|
||||
"en": "I go to bed at eleven.",
|
||||
"zh": "我十一点睡觉。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"go to bed",
|
||||
"goes to bed"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What time do you go to bed?",
|
||||
"zh": "你几点睡觉?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你通常十二点睡觉。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-P09",
|
||||
"type": "phrase",
|
||||
"en": "In my free time, I read books.",
|
||||
"zh": "空闲时我看书。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"free time"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "In my free time, I watch videos.",
|
||||
"zh": "空闲时我玩电子游戏。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你空闲时喜欢做什么。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U11-R01",
|
||||
"en": "wake",
|
||||
"zh": "醒来(wake up)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R02",
|
||||
"en": "sleep",
|
||||
"zh": "睡觉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R03",
|
||||
"en": "wash",
|
||||
"zh": "洗"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R04",
|
||||
"en": "clean",
|
||||
"zh": "打扫;干净的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R05",
|
||||
"en": "brush",
|
||||
"zh": "刷"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R06",
|
||||
"en": "tooth",
|
||||
"zh": "牙齿(复数 teeth)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R07",
|
||||
"en": "cook",
|
||||
"zh": "做饭"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R08",
|
||||
"en": "school",
|
||||
"zh": "学校"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R09",
|
||||
"en": "study",
|
||||
"zh": "学习"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R10",
|
||||
"en": "teach",
|
||||
"zh": "教"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R11",
|
||||
"en": "learn",
|
||||
"zh": "学"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R12",
|
||||
"en": "newspaper",
|
||||
"zh": "报纸"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R13",
|
||||
"en": "afternoon",
|
||||
"zh": "下午"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R14",
|
||||
"en": "start",
|
||||
"zh": "开始"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R15",
|
||||
"en": "finish",
|
||||
"zh": "完成;结束"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R16",
|
||||
"en": "exercise",
|
||||
"zh": "锻炼"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R17",
|
||||
"en": "run",
|
||||
"zh": "跑步"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R18",
|
||||
"en": "homework",
|
||||
"zh": "作业"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R19",
|
||||
"en": "news",
|
||||
"zh": "新闻"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R20",
|
||||
"en": "internet",
|
||||
"zh": "互联网"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R21",
|
||||
"en": "computer",
|
||||
"zh": "电脑"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R22",
|
||||
"en": "video",
|
||||
"zh": "视频"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R23",
|
||||
"en": "song",
|
||||
"zh": "歌曲"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R24",
|
||||
"en": "sing",
|
||||
"zh": "唱歌"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R25",
|
||||
"en": "dance",
|
||||
"zh": "跳舞"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R26",
|
||||
"en": "draw",
|
||||
"zh": "画画"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R27",
|
||||
"en": "swim",
|
||||
"zh": "游泳"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R28",
|
||||
"en": "weekend",
|
||||
"zh": "周末"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R29",
|
||||
"en": "week",
|
||||
"zh": "星期;周"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-R30",
|
||||
"en": "life",
|
||||
"zh": "生活"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U11-S1",
|
||||
"title": "早上的安排",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U11-P01",
|
||||
"A1-U11-P02",
|
||||
"A1-U11-P03",
|
||||
"A1-U11-P07",
|
||||
"A1-U11-P04"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What time do you get up? I get up at six thirty. I take a shower and have breakfast at home. I go to work at seven thirty.",
|
||||
"question": "说话人几点去上班?",
|
||||
"options": [
|
||||
"七点半",
|
||||
"六点半",
|
||||
"八点"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I get up at seven.",
|
||||
"tip": "get up 连读成 /ɡe tʌp/"
|
||||
},
|
||||
{
|
||||
"text": "What time do you get up?"
|
||||
},
|
||||
{
|
||||
"text": "I take a shower."
|
||||
},
|
||||
{
|
||||
"text": "I have breakfast at home."
|
||||
},
|
||||
{
|
||||
"text": "I go to work at eight."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Mark's morning\nMark wakes up at 6:00. He washes his face and brushes his teeth. He takes a shower. Then he cooks breakfast: eggs and bread.\nAt 7:30, he goes to work. His office is near his home.",
|
||||
"question": "Mark 早饭吃什么?",
|
||||
"options": [
|
||||
"鸡蛋和面包",
|
||||
"米饭和汤",
|
||||
"咖啡和蛋糕"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:你几点起床、几点去上班。",
|
||||
"example": "I get up at seven. I go to work at eight.",
|
||||
"itemIds": [
|
||||
"A1-U11-P01",
|
||||
"A1-U11-P04"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语按顺序说说你平时早上做的几件事。",
|
||||
"itemIds": [
|
||||
"A1-U11-P01",
|
||||
"A1-U11-P07",
|
||||
"A1-U11-P03",
|
||||
"A1-U11-P04"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-S2",
|
||||
"title": "总是、通常、有时、从不",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U11-W01",
|
||||
"A1-U11-W02",
|
||||
"A1-U11-W03",
|
||||
"A1-U11-W04",
|
||||
"A1-U11-P06"
|
||||
],
|
||||
"listening": {
|
||||
"text": "I always have breakfast. I usually go to work by bus. I sometimes run in the morning. I never watch TV in the morning.",
|
||||
"question": "说话人早上从不做什么?",
|
||||
"options": [
|
||||
"看电视",
|
||||
"跑步",
|
||||
"吃早饭"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I always have breakfast."
|
||||
},
|
||||
{
|
||||
"text": "I usually go to work by bus.",
|
||||
"tip": "usually 读作 /ˈjuːʒuəli/"
|
||||
},
|
||||
{
|
||||
"text": "I sometimes cook dinner."
|
||||
},
|
||||
{
|
||||
"text": "I never watch TV in the morning."
|
||||
},
|
||||
{
|
||||
"text": "I read the newspaper every day."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "How often do you exercise?\nAnna: I run every day. I always go before work.\nTom: I usually swim on the weekend.\nLily: I sometimes dance at home.\nBen: I never exercise. I'm always busy!",
|
||||
"question": "谁每天跑步?",
|
||||
"options": [
|
||||
"Anna",
|
||||
"Tom",
|
||||
"Ben"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说一件你总是做的事和一件你从不做的事。",
|
||||
"example": "I always have breakfast. I never watch TV in the morning.",
|
||||
"itemIds": [
|
||||
"A1-U11-W02",
|
||||
"A1-U11-W03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用 usually、sometimes、never 各说一件你的习惯。",
|
||||
"itemIds": [
|
||||
"A1-U11-W01",
|
||||
"A1-U11-W04",
|
||||
"A1-U11-W03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-S3",
|
||||
"title": "晚上和空闲时间",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U11-P05",
|
||||
"A1-U11-P08",
|
||||
"A1-U11-P09"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What do you do in the evening? I usually cook dinner and watch TV. In my free time, I read books or watch videos. I go to bed at eleven.",
|
||||
"question": "说话人几点睡觉?",
|
||||
"options": [
|
||||
"十一点",
|
||||
"十点",
|
||||
"十二点"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I watch TV in the evening."
|
||||
},
|
||||
{
|
||||
"text": "In my free time, I read books."
|
||||
},
|
||||
{
|
||||
"text": "I go to bed at eleven."
|
||||
},
|
||||
{
|
||||
"text": "I study English at night."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "My week\nI'm a teacher. I teach at a school five days a week. After school, I finish my work and learn English on the computer.\nIn the evening, I listen to the news and songs on the internet. I sometimes sing and draw.\nMy life is busy, but I love it. On the weekend, I sleep late.",
|
||||
"question": "这位老师晚上做什么?",
|
||||
"options": [
|
||||
"在网上听新闻和歌",
|
||||
"去学校上课",
|
||||
"很早睡觉"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:你晚上常做什么、几点睡觉。",
|
||||
"example": "I watch TV in the evening. I go to bed at eleven.",
|
||||
"itemIds": [
|
||||
"A1-U11-P05",
|
||||
"A1-U11-P08"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你空闲时喜欢做的两件事。",
|
||||
"itemIds": [
|
||||
"A1-U11-P09",
|
||||
"A1-U11-P05"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U11-C1",
|
||||
"kind": "main",
|
||||
"title": "和语言伙伴聊作息",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "语言交换伙伴",
|
||||
"setting": "你和语言交换伙伴视频聊天,互相问对方平时一天怎么过。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "So, what time do you get up?",
|
||||
"goal": "说你七点起床",
|
||||
"model": "I get up at seven.",
|
||||
"zh": "我七点起床。",
|
||||
"itemIds": [
|
||||
"A1-U11-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Seven? That's early! What do you do in the morning?",
|
||||
"goal": "说你洗澡、在家吃早饭",
|
||||
"model": "I take a shower and have breakfast at home.",
|
||||
"zh": "我洗个澡,在家吃早饭。",
|
||||
"itemIds": [
|
||||
"A1-U11-P07",
|
||||
"A1-U11-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Nice. I never have breakfast. I only have a coffee.",
|
||||
"goal": "说你八点去上班,再问对方几点起床",
|
||||
"model": "I go to work at eight. What time do you get up?",
|
||||
"zh": "我八点去上班。你几点起床?",
|
||||
"itemIds": [
|
||||
"A1-U11-P04",
|
||||
"A1-U11-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "At nine! I work at home on the computer. In the afternoon, I clean the house. What do you do in the evening?",
|
||||
"goal": "说你通常晚上看电视",
|
||||
"model": "I usually watch TV in the evening.",
|
||||
"zh": "我通常晚上看电视。",
|
||||
"itemIds": [
|
||||
"A1-U11-W01",
|
||||
"A1-U11-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Me too. And what time do you go to bed?",
|
||||
"goal": "说十一点睡觉,从不太晚睡",
|
||||
"model": "I go to bed at eleven. I never go to bed late.",
|
||||
"zh": "我十一点睡觉,从不晚睡。",
|
||||
"itemIds": [
|
||||
"A1-U11-P08",
|
||||
"A1-U11-W03"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-C2",
|
||||
"kind": "variant",
|
||||
"title": "体检时回答生活习惯",
|
||||
"learnerRole": "病人",
|
||||
"aiRole": "护士",
|
||||
"setting": "体检时护士问你平时的生活习惯。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. I have some questions about your day. Do you exercise?",
|
||||
"goal": "说你有时候早上跑步",
|
||||
"model": "Yes, I sometimes run in the morning.",
|
||||
"zh": "有,我有时候早上跑步。",
|
||||
"itemIds": [
|
||||
"A1-U11-W04",
|
||||
"A1-U11-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good. Do you have breakfast every day?",
|
||||
"goal": "说你总是吃早饭",
|
||||
"model": "Yes, I always have breakfast.",
|
||||
"zh": "是的,我总是吃早饭。",
|
||||
"itemIds": [
|
||||
"A1-U11-W02",
|
||||
"A1-U11-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "What time do you go to bed?",
|
||||
"goal": "说通常十二点睡觉",
|
||||
"model": "I usually go to bed at twelve.",
|
||||
"zh": "我通常十二点睡觉。",
|
||||
"itemIds": [
|
||||
"A1-U11-W01",
|
||||
"A1-U11-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "That's late. How many hours do you sleep?",
|
||||
"goal": "说每天睡七个小时",
|
||||
"model": "I sleep seven hours every day.",
|
||||
"zh": "我每天睡七个小时。",
|
||||
"itemIds": [
|
||||
"A1-U11-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. And in your free time?",
|
||||
"goal": "说空闲时你看书",
|
||||
"model": "In my free time, I read books.",
|
||||
"zh": "空闲时我看书。",
|
||||
"itemIds": [
|
||||
"A1-U11-P09"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-C3",
|
||||
"kind": "variant",
|
||||
"title": "和新室友商量早上的时间",
|
||||
"learnerRole": "室友",
|
||||
"aiRole": "新室友",
|
||||
"setting": "你和新室友住在一起,商量早上谁先用浴室。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! What time do you get up?",
|
||||
"goal": "说你六点半起床",
|
||||
"model": "I get up at six thirty.",
|
||||
"zh": "我六点半起床。",
|
||||
"itemIds": [
|
||||
"A1-U11-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh, I get up at seven. Do you take a shower in the morning?",
|
||||
"goal": "说你总是早上洗澡",
|
||||
"model": "Yes, I always take a shower in the morning.",
|
||||
"zh": "是的,我总是早上洗澡。",
|
||||
"itemIds": [
|
||||
"A1-U11-W02",
|
||||
"A1-U11-P07",
|
||||
"A1-U11-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. You can use the bathroom first. What time do you go to work?",
|
||||
"goal": "说你七点半去上班",
|
||||
"model": "I go to work at seven thirty.",
|
||||
"zh": "我七点半去上班。",
|
||||
"itemIds": [
|
||||
"A1-U11-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I start school at nine. I'm a student. Do you have breakfast at home?",
|
||||
"goal": "说有时候在家吃,再问对方几点睡觉",
|
||||
"model": "Sometimes. What time do you go to bed?",
|
||||
"zh": "有时候。你几点睡觉?",
|
||||
"itemIds": [
|
||||
"A1-U11-W04",
|
||||
"A1-U11-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Usually at twelve. I do my homework late.",
|
||||
"goal": "说你十一点睡觉,请对方晚上安静一点",
|
||||
"model": "I go to bed at eleven. Please be quiet at night.",
|
||||
"zh": "我十一点睡觉。晚上请安静一点。",
|
||||
"itemIds": [
|
||||
"A1-U11-P08",
|
||||
"A1-U11-P05"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U11-T1",
|
||||
"sceneId": "A1-U11-C1",
|
||||
"goal": "和语言伙伴互相介绍一天的作息",
|
||||
"itemIds": [
|
||||
"A1-U11-P01",
|
||||
"A1-U11-P07",
|
||||
"A1-U11-P03",
|
||||
"A1-U11-P04",
|
||||
"A1-U11-P02",
|
||||
"A1-U11-W01",
|
||||
"A1-U11-P05",
|
||||
"A1-U11-P08",
|
||||
"A1-U11-W03"
|
||||
],
|
||||
"slots": [
|
||||
"起床",
|
||||
"上班",
|
||||
"晚上",
|
||||
"睡觉"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-T2",
|
||||
"sceneId": "A1-U11-C2",
|
||||
"goal": "回答护士关于运动、早饭、睡眠和空闲时间的问题",
|
||||
"itemIds": [
|
||||
"A1-U11-W04",
|
||||
"A1-U11-P05",
|
||||
"A1-U11-W02",
|
||||
"A1-U11-P03",
|
||||
"A1-U11-W01",
|
||||
"A1-U11-P08",
|
||||
"A1-U11-P06",
|
||||
"A1-U11-P09"
|
||||
],
|
||||
"slots": [
|
||||
"运动",
|
||||
"早饭",
|
||||
"睡眠"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-T3",
|
||||
"sceneId": "A1-U11-C3",
|
||||
"goal": "和室友商量早上的时间安排",
|
||||
"itemIds": [
|
||||
"A1-U11-P01",
|
||||
"A1-U11-W02",
|
||||
"A1-U11-P07",
|
||||
"A1-U11-P05",
|
||||
"A1-U11-P04",
|
||||
"A1-U11-W04",
|
||||
"A1-U11-P08"
|
||||
],
|
||||
"slots": [
|
||||
"起床时间",
|
||||
"洗澡",
|
||||
"睡觉时间"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U11-M1",
|
||||
"mode": "listening",
|
||||
"title": "介绍我的一天",
|
||||
"text": "This is my normal day. I wake up at seven and get up. I wash my face and brush my teeth, then I take a shower. I have breakfast and clean the kitchen. I go to school at eight to study and learn. I usually eat lunch with friends. In the afternoon my classes start again, and I do my homework. I finish at five. In the evening I sometimes cook, read the newspaper, or watch the news on TV. In my free time I run, swim, sing, dance or draw. On the weekend I use the internet and watch videos. I never sleep late; I go to bed at eleven.",
|
||||
"glosses": {
|
||||
"normal": "平常的",
|
||||
"teeth": "牙齿",
|
||||
"classes": "(复数)课"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话主要讲什么?",
|
||||
"options": [
|
||||
"说话人每天的作息和常做的事",
|
||||
"一次旅行",
|
||||
"怎么买衣服"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "说话人早上先做什么?",
|
||||
"options": [
|
||||
"洗脸、刷牙",
|
||||
"去上学",
|
||||
"做作业"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U11-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:聊日常和爱好",
|
||||
"text": "Mia: What time do you wake up?\nBen: I usually wake up at six thirty and go for a run. And you?\nMia: I never get up early! I wash and brush my teeth at eight.\nBen: Do you go to school?\nMia: Yes, I study music at college. I teach a small class on the weekend too.\nBen: What do you do in your free time?\nMia: I sing, dance and draw. Sometimes I swim. I like this life! What about you?\nBen: After work I clean the house, cook, and read the news on the internet.\nMia: Do you watch TV?\nBen: No, I never watch TV. But I like videos and songs.\nMia: Me too. Let's swim together this weekend!",
|
||||
"glosses": {
|
||||
"together": "一起",
|
||||
"college": "大学",
|
||||
"class": "课"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Mia 空闲时喜欢做什么?",
|
||||
"options": [
|
||||
"唱歌、跳舞、画画",
|
||||
"跑步",
|
||||
"看电视"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "关于看电视,Ben 怎么说?",
|
||||
"options": [
|
||||
"他从不看电视",
|
||||
"他每天看电视",
|
||||
"他晚上看电视"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "routine",
|
||||
"reason": "B1;A1 用 my day 表达"
|
||||
},
|
||||
{
|
||||
"en": "gym",
|
||||
"reason": "B1;A1 用 exercise / run / swim 表达运动"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,972 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U12",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "买衣服",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "问尺码和颜色、试穿",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "38",
|
||||
"use": "active",
|
||||
"reason": "只用 bigger、smaller 这类单音节比较级"
|
||||
},
|
||||
{
|
||||
"id": "16",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U12-P01",
|
||||
"type": "phrase",
|
||||
"en": "Just looking, thanks.",
|
||||
"zh": "我只是看看,谢谢。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"just looking"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "No, thanks. I'm just looking.",
|
||||
"zh": "不用了,谢谢,我只是看看。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "店员问你要不要帮忙,说你只是看看。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-W01",
|
||||
"type": "word",
|
||||
"en": "clothes",
|
||||
"zh": "衣服",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"clothes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I need some new clothes.",
|
||||
"zh": "我需要几件新衣服。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你想买些衣服。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-W02",
|
||||
"type": "word",
|
||||
"en": "shirt",
|
||||
"zh": "衬衫",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"shirt",
|
||||
"shirts"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm looking for a white shirt.",
|
||||
"zh": "我在找一件白衬衫。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你要找一件衬衫。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-W03",
|
||||
"type": "word",
|
||||
"en": "jacket",
|
||||
"zh": "夹克;外套",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"jacket",
|
||||
"jackets"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "This jacket is very nice.",
|
||||
"zh": "这件夹克很好看。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你喜欢这件夹克。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P02",
|
||||
"type": "phrase",
|
||||
"en": "a pair of shoes",
|
||||
"zh": "一双鞋",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"a pair of"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I want a pair of black shoes.",
|
||||
"zh": "我想要一双黑鞋。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你想买一双鞋。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P03",
|
||||
"type": "phrase",
|
||||
"en": "Can I try it on?",
|
||||
"zh": "我可以试穿吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"try it on",
|
||||
"try them on",
|
||||
"try this on",
|
||||
"try these on"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "These shoes are nice. Can I try them on?",
|
||||
"zh": "这双鞋不错,我可以试穿吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问店员能不能试穿。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P04",
|
||||
"type": "phrase",
|
||||
"en": "Where is the changing room?",
|
||||
"zh": "试衣间在哪儿?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"changing room"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The changing room is next to the mirror.",
|
||||
"zh": "试衣间在镜子旁边。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问试衣间在哪里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P05",
|
||||
"type": "phrase",
|
||||
"en": "It's too small.",
|
||||
"zh": "太小了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"too small",
|
||||
"too big",
|
||||
"too long",
|
||||
"too short"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "These pants are too long.",
|
||||
"zh": "这条裤子太长了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说这件外套太大了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P06",
|
||||
"type": "phrase",
|
||||
"en": "Do you have a bigger size?",
|
||||
"zh": "有大一号的吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"bigger size",
|
||||
"smaller size"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you have a smaller size?",
|
||||
"zh": "有小一号的吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问有没有小一号的。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P07",
|
||||
"type": "phrase",
|
||||
"en": "It doesn't fit.",
|
||||
"zh": "不合身。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "fit 在 CEFR-J 为 B1 动词,但试衣服时说“不合身”离不开它",
|
||||
"match": [
|
||||
[
|
||||
"doesn't fit",
|
||||
"don't fit",
|
||||
"fits"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "These shoes don't fit.",
|
||||
"zh": "这双鞋不合脚。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说这件衬衫不合身。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P08",
|
||||
"type": "phrase",
|
||||
"en": "Do you have it in blue?",
|
||||
"zh": "有蓝色的吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"in blue",
|
||||
"in black",
|
||||
"in red",
|
||||
"in white"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you have this jacket in black?",
|
||||
"zh": "这件夹克有黑色的吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问这件衬衫有没有白色的。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P09",
|
||||
"type": "phrase",
|
||||
"en": "It looks good on you.",
|
||||
"zh": "你穿着很好看。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"looks good on",
|
||||
"look good on"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Those shoes look good on you.",
|
||||
"zh": "那双鞋你穿着很好看。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "夸朋友穿这件外套好看。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-P10",
|
||||
"type": "phrase",
|
||||
"en": "I'll think about it.",
|
||||
"zh": "我再考虑一下。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"think about it"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's nice, but I'll think about it.",
|
||||
"zh": "挺好的,不过我再考虑一下。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "不想马上买,礼貌地说再考虑一下。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U12-R01",
|
||||
"en": "color",
|
||||
"zh": "颜色"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R02",
|
||||
"en": "red",
|
||||
"zh": "红色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R03",
|
||||
"en": "green",
|
||||
"zh": "绿色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R04",
|
||||
"en": "yellow",
|
||||
"zh": "黄色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R05",
|
||||
"en": "white",
|
||||
"zh": "白色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R06",
|
||||
"en": "gray",
|
||||
"zh": "灰色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R07",
|
||||
"en": "pink",
|
||||
"zh": "粉色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R08",
|
||||
"en": "purple",
|
||||
"zh": "紫色的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R09",
|
||||
"en": "T-shirt",
|
||||
"zh": "T 恤"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R10",
|
||||
"en": "dress",
|
||||
"zh": "连衣裙"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R11",
|
||||
"en": "skirt",
|
||||
"zh": "裙子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R12",
|
||||
"en": "jeans",
|
||||
"zh": "牛仔裤"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R13",
|
||||
"en": "pants",
|
||||
"zh": "裤子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R14",
|
||||
"en": "coat",
|
||||
"zh": "大衣"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R15",
|
||||
"en": "cap",
|
||||
"zh": "帽子(有檐的)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R16",
|
||||
"en": "sweater",
|
||||
"zh": "毛衣"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R17",
|
||||
"en": "sock",
|
||||
"zh": "袜子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R18",
|
||||
"en": "tight",
|
||||
"zh": "紧的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R19",
|
||||
"en": "mirror",
|
||||
"zh": "镜子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R20",
|
||||
"en": "style",
|
||||
"zh": "款式"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R21",
|
||||
"en": "lovely",
|
||||
"zh": "可爱的;漂亮的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R22",
|
||||
"en": "ugly",
|
||||
"zh": "难看的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R23",
|
||||
"en": "favorite",
|
||||
"zh": "最喜欢的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R24",
|
||||
"en": "button",
|
||||
"zh": "纽扣"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R25",
|
||||
"en": "present",
|
||||
"zh": "礼物"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R26",
|
||||
"en": "girlfriend",
|
||||
"zh": "女朋友"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R27",
|
||||
"en": "boyfriend",
|
||||
"zh": "男朋友"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R28",
|
||||
"en": "lady",
|
||||
"zh": "女士"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R29",
|
||||
"en": "sir",
|
||||
"zh": "先生"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R30",
|
||||
"en": "toy",
|
||||
"zh": "玩具"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R31",
|
||||
"en": "jewelry",
|
||||
"zh": "首饰"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R32",
|
||||
"en": "notebook",
|
||||
"zh": "笔记本"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R33",
|
||||
"en": "pencil",
|
||||
"zh": "铅笔"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R34",
|
||||
"en": "bookstore",
|
||||
"zh": "书店"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-R35",
|
||||
"en": "kid",
|
||||
"zh": "小孩"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U12-S1",
|
||||
"title": "逛店和找衣服",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U12-P01",
|
||||
"A1-U12-W01",
|
||||
"A1-U12-W02",
|
||||
"A1-U12-W03",
|
||||
"A1-U12-P02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Can I help you? No, thanks. I'm just looking. OK. The shirts are over there, and the jackets are next to the mirror.",
|
||||
"question": "夹克在哪里?",
|
||||
"options": [
|
||||
"镜子旁边",
|
||||
"门口",
|
||||
"楼上"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Just looking, thanks."
|
||||
},
|
||||
{
|
||||
"text": "I need some new clothes.",
|
||||
"tip": "clothes 读 /kləʊðz/,不要读成两个音节"
|
||||
},
|
||||
{
|
||||
"text": "I'm looking for a white shirt."
|
||||
},
|
||||
{
|
||||
"text": "This jacket is very nice."
|
||||
},
|
||||
{
|
||||
"text": "I want a pair of black shoes."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "City Store\nFloor 1: shoes, bags, jewelry and caps\nFloor 2: lady's clothes: dresses, skirts and T-shirts\nFloor 3: men's clothes: shirts, jackets, coats and jeans\nFloor 4: kids' clothes and toys\nFloor 5: bookstore: books, notebooks and pencils",
|
||||
"question": "买男士夹克去几楼?",
|
||||
"options": [
|
||||
"三楼",
|
||||
"二楼",
|
||||
"四楼"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:你在找什么衣服、想要一双什么颜色的鞋。",
|
||||
"example": "I'm looking for a jacket. I want a pair of black shoes.",
|
||||
"itemIds": [
|
||||
"A1-U12-W03",
|
||||
"A1-U12-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你今天想买的两件衣服。",
|
||||
"itemIds": [
|
||||
"A1-U12-W01",
|
||||
"A1-U12-W02",
|
||||
"A1-U12-W03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-S2",
|
||||
"title": "试穿与尺码",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U12-P03",
|
||||
"A1-U12-P04",
|
||||
"A1-U12-P05",
|
||||
"A1-U12-P06",
|
||||
"A1-U12-P07"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Can I try it on? Sure. The changing room is over there. ... Oh, it's too small. It doesn't fit. Do you have a bigger size? Yes, here you are.",
|
||||
"question": "顾客遇到什么问题?",
|
||||
"options": [
|
||||
"衣服太小",
|
||||
"衣服太贵",
|
||||
"试衣间有人"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Can I try it on?"
|
||||
},
|
||||
{
|
||||
"text": "Where is the changing room?"
|
||||
},
|
||||
{
|
||||
"text": "It's too small."
|
||||
},
|
||||
{
|
||||
"text": "Do you have a bigger size?",
|
||||
"tip": "bigger 重音在前:BIG-ger"
|
||||
},
|
||||
{
|
||||
"text": "It doesn't fit."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Size help\nS = small L = large\nTry this: Is your T-shirt tight? Then try a bigger size. Is it too long? Then try a smaller size.\nPants and jeans: sit down in the changing room. Are they OK?",
|
||||
"question": "衣服太紧应该怎么办?",
|
||||
"options": [
|
||||
"换大一号",
|
||||
"换小一号",
|
||||
"换一种颜色"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:这件太小了,问有没有大一号。",
|
||||
"example": "It's too small. Do you have a bigger size?",
|
||||
"itemIds": [
|
||||
"A1-U12-P05",
|
||||
"A1-U12-P06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你试穿的鞋太大,用英语告诉店员并要小一号。",
|
||||
"itemIds": [
|
||||
"A1-U12-P07",
|
||||
"A1-U12-P05",
|
||||
"A1-U12-P06"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-S3",
|
||||
"title": "颜色和决定",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U12-P08",
|
||||
"A1-U12-P09",
|
||||
"A1-U12-P10"
|
||||
],
|
||||
"listening": {
|
||||
"text": "I like this dress, but do you have it in blue? Sorry, we only have it in red and pink. Oh, it's lovely, but I'll think about it.",
|
||||
"question": "顾客想要什么颜色?",
|
||||
"options": [
|
||||
"蓝色",
|
||||
"红色",
|
||||
"粉色"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Do you have it in blue?"
|
||||
},
|
||||
{
|
||||
"text": "It looks good on you."
|
||||
},
|
||||
{
|
||||
"text": "I'll think about it."
|
||||
},
|
||||
{
|
||||
"text": "My favorite color is green."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "A present for my girlfriend\nI want a sweater for my girlfriend. Her favorite colors are purple and gray. In the store, there are yellow, green and white sweaters. The style is nice, but the yellow one is ugly. The lady says, \"Sir, we have it in purple. The buttons are lovely.\" It's a little expensive, so I'll think about it. I buy some socks for my boyfriend too. No, I don't have one!",
|
||||
"question": "女朋友最喜欢什么颜色?",
|
||||
"options": [
|
||||
"紫色和浅灰色",
|
||||
"黄色和绿色",
|
||||
"白色和红色"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问有没有蓝色的,然后说再考虑一下。",
|
||||
"example": "Do you have it in blue? I'll think about it.",
|
||||
"itemIds": [
|
||||
"A1-U12-P08",
|
||||
"A1-U12-P10"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "朋友在试衣服,用英语夸他穿着好看,再说说你最喜欢的颜色。",
|
||||
"itemIds": [
|
||||
"A1-U12-P09",
|
||||
"A1-U12-P08"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U12-C1",
|
||||
"kind": "main",
|
||||
"title": "在服装店买夹克",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "店员",
|
||||
"setting": "你在商场的服装店想买一件夹克。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello! Can I help you?",
|
||||
"goal": "说你在找一件夹克",
|
||||
"model": "Yes, I'm looking for a jacket.",
|
||||
"zh": "是的,我在找一件夹克。",
|
||||
"itemIds": [
|
||||
"A1-U12-W03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The jackets are here. What about this one?",
|
||||
"goal": "问能不能试穿、试衣间在哪",
|
||||
"model": "Can I try it on? Where is the changing room?",
|
||||
"zh": "我可以试穿吗?试衣间在哪儿?",
|
||||
"itemIds": [
|
||||
"A1-U12-P03",
|
||||
"A1-U12-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. It's over there, next to the mirror. ... How is it?",
|
||||
"goal": "说太小了、不合身",
|
||||
"model": "It's too small. It doesn't fit.",
|
||||
"zh": "太小了,不合身。",
|
||||
"itemIds": [
|
||||
"A1-U12-P05",
|
||||
"A1-U12-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. Here is a large. Is it better?",
|
||||
"goal": "说合身了,问有没有蓝色的",
|
||||
"model": "It fits. Do you have it in blue?",
|
||||
"zh": "合身了。有蓝色的吗?",
|
||||
"itemIds": [
|
||||
"A1-U12-P07",
|
||||
"A1-U12-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, here you are. It looks good on you!",
|
||||
"goal": "说你要了",
|
||||
"model": "I'll take it.",
|
||||
"zh": "我要了。",
|
||||
"itemIds": []
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-C2",
|
||||
"kind": "variant",
|
||||
"title": "给朋友挑衣服",
|
||||
"learnerRole": "朋友",
|
||||
"aiRole": "正在试衣服的朋友",
|
||||
"setting": "你陪朋友逛街,朋友在试衣服并问你的意见。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "What do you think about this shirt?",
|
||||
"goal": "夸他穿着好看",
|
||||
"model": "It looks good on you.",
|
||||
"zh": "你穿着很好看。",
|
||||
"itemIds": [
|
||||
"A1-U12-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thanks! But it's a little tight.",
|
||||
"goal": "让他问店员要大一号",
|
||||
"model": "Ask for a bigger size.",
|
||||
"zh": "问问有没有大一号的。",
|
||||
"itemIds": [
|
||||
"A1-U12-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. Do you like the color?",
|
||||
"goal": "说你更喜欢白色,问店里有没有白色的",
|
||||
"model": "I like white. Do they have it in white?",
|
||||
"zh": "我喜欢白色。他们有白色的吗?",
|
||||
"itemIds": [
|
||||
"A1-U12-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, but it's very expensive.",
|
||||
"goal": "说那就只买一双鞋",
|
||||
"model": "Just buy a pair of shoes.",
|
||||
"zh": "那就买一双鞋吧。",
|
||||
"itemIds": [
|
||||
"A1-U12-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Hmm. These shoes are nice.",
|
||||
"goal": "说你再考虑一下",
|
||||
"model": "OK, I'll think about it too.",
|
||||
"zh": "好,我也再考虑一下。",
|
||||
"itemIds": [
|
||||
"A1-U12-P10"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-C3",
|
||||
"kind": "variant",
|
||||
"title": "只是逛逛",
|
||||
"learnerRole": "顾客",
|
||||
"aiRole": "热情的店员",
|
||||
"setting": "你随便逛逛,店员一直来推荐,你要礼貌地回应。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! Are you looking for anything?",
|
||||
"goal": "说只是看看",
|
||||
"model": "I'm just looking at the clothes, thanks.",
|
||||
"zh": "我只是看看衣服,谢谢。",
|
||||
"itemIds": [
|
||||
"A1-U12-P01",
|
||||
"A1-U12-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "We have new clothes. This dress is on sale!",
|
||||
"goal": "说你需要的是衬衫,不是连衣裙",
|
||||
"model": "Thanks, but I need a shirt, not a dress.",
|
||||
"zh": "谢谢,不过我需要衬衫,不是连衣裙。",
|
||||
"itemIds": [
|
||||
"A1-U12-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Here are the shirts. Try this one!",
|
||||
"goal": "问能不能试穿",
|
||||
"model": "Can I try it on?",
|
||||
"zh": "我可以试穿吗?",
|
||||
"itemIds": [
|
||||
"A1-U12-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "How is it?",
|
||||
"goal": "说太长了,不合身",
|
||||
"model": "It's too long. It doesn't fit.",
|
||||
"zh": "太长了,不合身。",
|
||||
"itemIds": [
|
||||
"A1-U12-P05",
|
||||
"A1-U12-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Do you want a smaller size?",
|
||||
"goal": "说你再考虑一下",
|
||||
"model": "No, thanks. I'll think about it.",
|
||||
"zh": "不用了,谢谢。我再考虑一下。",
|
||||
"itemIds": [
|
||||
"A1-U12-P10"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U12-T1",
|
||||
"sceneId": "A1-U12-C1",
|
||||
"goal": "找到合适尺码和颜色的夹克并买下",
|
||||
"itemIds": [
|
||||
"A1-U12-W03",
|
||||
"A1-U12-P03",
|
||||
"A1-U12-P04",
|
||||
"A1-U12-P05",
|
||||
"A1-U12-P07",
|
||||
"A1-U12-P08"
|
||||
],
|
||||
"slots": [
|
||||
"款式",
|
||||
"尺码",
|
||||
"颜色"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-T2",
|
||||
"sceneId": "A1-U12-C2",
|
||||
"goal": "帮朋友挑衣服并给出建议",
|
||||
"itemIds": [
|
||||
"A1-U12-P09",
|
||||
"A1-U12-P06",
|
||||
"A1-U12-P08",
|
||||
"A1-U12-P02",
|
||||
"A1-U12-P10"
|
||||
],
|
||||
"slots": [
|
||||
"好不好看",
|
||||
"尺码",
|
||||
"颜色"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-T3",
|
||||
"sceneId": "A1-U12-C3",
|
||||
"goal": "礼貌应对店员的推荐",
|
||||
"itemIds": [
|
||||
"A1-U12-P01",
|
||||
"A1-U12-W02",
|
||||
"A1-U12-W01",
|
||||
"A1-U12-P03",
|
||||
"A1-U12-P05",
|
||||
"A1-U12-P07",
|
||||
"A1-U12-P10"
|
||||
],
|
||||
"slots": [
|
||||
"只是看看",
|
||||
"试穿",
|
||||
"拒绝"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U12-M1",
|
||||
"mode": "listening",
|
||||
"title": "店员招呼你试衣服",
|
||||
"text": "Hello! Are you looking for something? These clothes are on sale. What size are you? We have small, medium and large. This white T-shirt is nice, and the gray sweater is lovely. We also have red skirts, blue jeans, black pants, a warm coat and a yellow cap. Do you want to try it on? The mirror is over there. If it's too tight, tell me and I'll get another size. This pink dress is our favorite style. It looks good on you! Are you buying a present? We have socks, toys and jewelry too. Come and see!",
|
||||
"glosses": {
|
||||
"medium": "中号",
|
||||
"tell": "告诉",
|
||||
"warm": "暖和的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "店员主要在做什么?",
|
||||
"options": [
|
||||
"帮顾客挑衣服、试穿",
|
||||
"卖水果",
|
||||
"问路"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "店员说镜子在哪里?",
|
||||
"options": [
|
||||
"在那边",
|
||||
"门口",
|
||||
"楼上"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U12-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:试连衣裙",
|
||||
"text": "Mia: Excuse me, can I try on this dress? I want it as a present.\nStaff: Sure. What size and color?\nMia: Medium, in purple. Do you have that color?\nStaff: Yes. Here you are. The mirror is over there.\nMia: Hmm, it's too small and tight. It doesn't fit. This button is loose too.\nStaff: Sorry! Let me get a bigger size. How is this one?\nMia: Better! It's a lovely style, not ugly at all. I like the color. How much?\nStaff: Forty dollars. It's our favorite dress this year, sir — I mean, lady!\nMia: I'll take it. Do you have a white one too? And a green cap for a kid?\nStaff: Yes, the caps are near the toys and the notebooks.\nMia: Great, thank you!",
|
||||
"glosses": {
|
||||
"staff": "员工,服务员",
|
||||
"medium": "中号",
|
||||
"loose": "松的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Mia 第一次试的裙子怎么样?",
|
||||
"options": [
|
||||
"太小太紧,不合身",
|
||||
"刚刚好",
|
||||
"太大了"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "店员后来给她拿了什么?",
|
||||
"options": [
|
||||
"更大的尺码",
|
||||
"一双鞋",
|
||||
"一顶帽子"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "medium",
|
||||
"reason": "B1;尺码表里只作为 M 的说明出现,不单独教"
|
||||
},
|
||||
{
|
||||
"en": "department store",
|
||||
"reason": "department 为 B1;A1 用 mall 或 store"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,976 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U13",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "日期与约时间",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说日期、约见面、改时间",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "239",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "236",
|
||||
"use": "active",
|
||||
"reason": "CEFR-J 未标级别,When 问句是约时间的必需句型"
|
||||
},
|
||||
{
|
||||
"id": "120",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "21",
|
||||
"use": "active",
|
||||
"reason": "日期用 in March、on Friday、at three"
|
||||
},
|
||||
{
|
||||
"id": "59-2",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U13-P01",
|
||||
"type": "phrase",
|
||||
"en": "What's the date today?",
|
||||
"zh": "今天几号?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what's the date",
|
||||
"what is the date"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What's the date tomorrow?",
|
||||
"zh": "明天几号?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问今天几号。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P02",
|
||||
"type": "phrase",
|
||||
"en": "When is your birthday?",
|
||||
"zh": "你的生日是哪天?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"when is your birthday",
|
||||
"when's your birthday"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "When is your birthday, Anna?",
|
||||
"zh": "安娜,你的生日是哪天?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问新朋友的生日。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P03",
|
||||
"type": "phrase",
|
||||
"en": "My birthday is in March.",
|
||||
"zh": "我的生日在三月。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"in january",
|
||||
"in february",
|
||||
"in march",
|
||||
"in april",
|
||||
"in may",
|
||||
"in june",
|
||||
"in july",
|
||||
"in august",
|
||||
"in september",
|
||||
"in october",
|
||||
"in november",
|
||||
"in december"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The party is in July.",
|
||||
"zh": "聚会在七月。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的生日在几月。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P04",
|
||||
"type": "phrase",
|
||||
"en": "Are you free on Saturday?",
|
||||
"zh": "你周六有空吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"are you free"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Are you free tomorrow?",
|
||||
"zh": "你明天有空吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问朋友周六有没有空。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P05",
|
||||
"type": "phrase",
|
||||
"en": "Sorry, I'm busy.",
|
||||
"zh": "抱歉,我没空。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"i'm busy",
|
||||
"i am busy"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, I'm busy on Friday.",
|
||||
"zh": "抱歉,我周五没空。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "礼貌地说你那天没空。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P06",
|
||||
"type": "phrase",
|
||||
"en": "How about Sunday?",
|
||||
"zh": "周日怎么样?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how about"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "How about next week?",
|
||||
"zh": "下周怎么样?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "提议改到周日。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P07",
|
||||
"type": "phrase",
|
||||
"en": "Let's meet at three.",
|
||||
"zh": "我们三点见吧。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"let's meet",
|
||||
"let us meet"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Let's meet at the station.",
|
||||
"zh": "我们在车站见吧。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "约朋友三点见面。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P08",
|
||||
"type": "phrase",
|
||||
"en": "What time is good for you?",
|
||||
"zh": "你几点方便?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"good for you"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is Friday good for you?",
|
||||
"zh": "周五你方便吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问对方几点方便。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P09",
|
||||
"type": "phrase",
|
||||
"en": "See you then.",
|
||||
"zh": "到时见。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"see you then"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "OK, Saturday at two. See you then.",
|
||||
"zh": "好,周六两点。到时见。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "约好时间后道别。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P10",
|
||||
"type": "phrase",
|
||||
"en": "next week",
|
||||
"zh": "下周",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"next week",
|
||||
"next month",
|
||||
"next monday",
|
||||
"next friday"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm free next week.",
|
||||
"zh": "我下周有空。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你下周有空。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-P11",
|
||||
"type": "phrase",
|
||||
"en": "Can we change the date?",
|
||||
"zh": "我们能改个日期吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"change the date",
|
||||
"change the time"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, can we change the time?",
|
||||
"zh": "抱歉,我们能改个时间吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请对方改个日期。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-W01",
|
||||
"type": "word",
|
||||
"en": "tomorrow",
|
||||
"zh": "明天",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"tomorrow"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "See you tomorrow!",
|
||||
"zh": "明天见!"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你明天有空。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-W02",
|
||||
"type": "word",
|
||||
"en": "party",
|
||||
"zh": "聚会",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"party",
|
||||
"parties"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Come to my birthday party!",
|
||||
"zh": "来参加我的生日聚会吧!"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请朋友来你的聚会。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U13-R01",
|
||||
"en": "January",
|
||||
"zh": "一月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R02",
|
||||
"en": "February",
|
||||
"zh": "二月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R03",
|
||||
"en": "April",
|
||||
"zh": "四月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R04",
|
||||
"en": "June",
|
||||
"zh": "六月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R05",
|
||||
"en": "July",
|
||||
"zh": "七月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R06",
|
||||
"en": "August",
|
||||
"zh": "八月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R07",
|
||||
"en": "September",
|
||||
"zh": "九月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R08",
|
||||
"en": "October",
|
||||
"zh": "十月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R09",
|
||||
"en": "November",
|
||||
"zh": "十一月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R10",
|
||||
"en": "December",
|
||||
"zh": "十二月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R11",
|
||||
"en": "month",
|
||||
"zh": "月"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R12",
|
||||
"en": "later",
|
||||
"zh": "稍后;以后"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R13",
|
||||
"en": "ago",
|
||||
"zh": "以前"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R14",
|
||||
"en": "pm",
|
||||
"zh": "下午(p.m.)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R15",
|
||||
"en": "plan",
|
||||
"zh": "计划"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R16",
|
||||
"en": "celebrate",
|
||||
"zh": "庆祝"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R17",
|
||||
"en": "remember",
|
||||
"zh": "记得"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R18",
|
||||
"en": "forget",
|
||||
"zh": "忘记"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R19",
|
||||
"en": "hope",
|
||||
"zh": "希望"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R20",
|
||||
"en": "wish",
|
||||
"zh": "祝愿"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R21",
|
||||
"en": "dear",
|
||||
"zh": "亲爱的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R22",
|
||||
"en": "luck",
|
||||
"zh": "运气"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R23",
|
||||
"en": "age",
|
||||
"zh": "年龄"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R24",
|
||||
"en": "invite",
|
||||
"zh": "邀请"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R25",
|
||||
"en": "wedding",
|
||||
"zh": "婚礼"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R26",
|
||||
"en": "holiday",
|
||||
"zh": "假日"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R27",
|
||||
"en": "festival",
|
||||
"zh": "节日"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R28",
|
||||
"en": "calendar",
|
||||
"zh": "日历"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R29",
|
||||
"en": "bye",
|
||||
"zh": "再见"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R30",
|
||||
"en": "cut",
|
||||
"zh": "切"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R31",
|
||||
"en": "mom",
|
||||
"zh": "妈妈"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R32",
|
||||
"en": "dad",
|
||||
"zh": "爸爸"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R33",
|
||||
"en": "maybe",
|
||||
"zh": "也许"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R34",
|
||||
"en": "together",
|
||||
"zh": "一起"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-R35",
|
||||
"en": "know",
|
||||
"zh": "知道"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U13-S1",
|
||||
"title": "日期和生日",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U13-P01",
|
||||
"A1-U13-P02",
|
||||
"A1-U13-P03",
|
||||
"A1-U13-W01"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What's the date today? It's May 5. And tomorrow is May 6. Oh, tomorrow is my birthday!",
|
||||
"question": "说话人的生日是几号?",
|
||||
"options": [
|
||||
"五月六日",
|
||||
"五月五日",
|
||||
"六月五日"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What's the date today?"
|
||||
},
|
||||
{
|
||||
"text": "When is your birthday?"
|
||||
},
|
||||
{
|
||||
"text": "My birthday is in March.",
|
||||
"tip": "March 读 /mɑːtʃ/,月份首字母要大写"
|
||||
},
|
||||
{
|
||||
"text": "See you tomorrow!"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Our birthdays\nThere are twelve months in a year. Look at our family calendar:\nMom: January 12\nDad: April 3\nMy sister: August 20\nMy brother: October 9\nMe: December 25\nMy grandfather: February, my grandmother: June, my aunt: July, my uncle: September, my cousin: November.\nMy grandmother's age? Eighty! We always celebrate with a big cake, and she cuts it.",
|
||||
"question": "谁的生日在十月?",
|
||||
"options": [
|
||||
"哥哥",
|
||||
"妈妈",
|
||||
"姐姐"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问朋友生日是哪天,再说你的生日在几月。",
|
||||
"example": "When is your birthday? My birthday is in March.",
|
||||
"itemIds": [
|
||||
"A1-U13-P02",
|
||||
"A1-U13-P03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你家里两个人的生日在几月。",
|
||||
"itemIds": [
|
||||
"A1-U13-P03"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-S2",
|
||||
"title": "约时间",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U13-P04",
|
||||
"A1-U13-P05",
|
||||
"A1-U13-P06",
|
||||
"A1-U13-P07",
|
||||
"A1-U13-P08",
|
||||
"A1-U13-P09"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Are you free on Saturday? Sorry, I'm busy on Saturday. How about Sunday? Sunday is good. Let's meet at three. OK, see you then.",
|
||||
"question": "他们约在哪天见面?",
|
||||
"options": [
|
||||
"周日",
|
||||
"周六",
|
||||
"周五"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Are you free on Saturday?"
|
||||
},
|
||||
{
|
||||
"text": "Sorry, I'm busy."
|
||||
},
|
||||
{
|
||||
"text": "How about Sunday?"
|
||||
},
|
||||
{
|
||||
"text": "Let's meet at three."
|
||||
},
|
||||
{
|
||||
"text": "What time is good for you?"
|
||||
},
|
||||
{
|
||||
"text": "See you then."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Nina: Hi Leo! Are you free on Friday? Let's have dinner.\nLeo: Sorry, I'm busy on Friday. I work until 9 pm. How about Saturday?\nNina: Saturday is good. What time is good for you?\nLeo: 7 pm? Let's meet at the restaurant.\nNina: OK! See you then. Bye!",
|
||||
"question": "Leo 为什么周五不行?",
|
||||
"options": [
|
||||
"他要工作到九点",
|
||||
"他要去生日聚会",
|
||||
"他忘了时间"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问朋友周六有没有空,再约三点见面。",
|
||||
"example": "Are you free on Saturday? Let's meet at three.",
|
||||
"itemIds": [
|
||||
"A1-U13-P04",
|
||||
"A1-U13-P07"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "朋友约你周五见,你没空,用英语拒绝并提议别的时间。",
|
||||
"itemIds": [
|
||||
"A1-U13-P05",
|
||||
"A1-U13-P06",
|
||||
"A1-U13-P08"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-S3",
|
||||
"title": "改期和聚会",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U13-P10",
|
||||
"A1-U13-P11",
|
||||
"A1-U13-W02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hi, it's Tom. Sorry, I can't come to the party tomorrow. Can we change the date? Maybe next week?",
|
||||
"question": "Tom 想把见面改到什么时候?",
|
||||
"options": [
|
||||
"下周",
|
||||
"明天",
|
||||
"下个月"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Can we change the date?"
|
||||
},
|
||||
{
|
||||
"text": "I'm free next week."
|
||||
},
|
||||
{
|
||||
"text": "Come to my birthday party!"
|
||||
},
|
||||
{
|
||||
"text": "Remember the date!"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Dear friends,\nI want to invite you to my birthday party! It's on Saturday, June 14, at 6 pm. We can eat cake and celebrate together. Please don't forget! We had this plan two months ago!\nIt's a holiday plan too: there's a festival in the park later that night.\nSorry, we can't change the date. My brother's wedding is two days later.\nI hope you can come. Wish me luck with the cake!\nMia",
|
||||
"question": "聚会是哪一天?",
|
||||
"options": [
|
||||
"六月十四日",
|
||||
"六月六日",
|
||||
"六月十六日"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说抱歉,然后问能不能改到下周。",
|
||||
"example": "Sorry, can we change the date? How about next week?",
|
||||
"itemIds": [
|
||||
"A1-U13-P11",
|
||||
"A1-U13-P10"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语邀请朋友来你的聚会,说出日期和时间。",
|
||||
"itemIds": [
|
||||
"A1-U13-W02",
|
||||
"A1-U13-P07"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U13-C1",
|
||||
"kind": "main",
|
||||
"title": "约朋友吃饭",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "朋友",
|
||||
"setting": "你给朋友打电话,想约周末一起吃饭。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! What's up?",
|
||||
"goal": "问朋友周六有没有空",
|
||||
"model": "Are you free on Saturday?",
|
||||
"zh": "你周六有空吗?",
|
||||
"itemIds": [
|
||||
"A1-U13-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, I'm busy on Saturday. It's my mom's birthday.",
|
||||
"goal": "提议周日",
|
||||
"model": "How about Sunday?",
|
||||
"zh": "周日怎么样?",
|
||||
"itemIds": [
|
||||
"A1-U13-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sunday is OK. What time?",
|
||||
"goal": "问对方几点方便",
|
||||
"model": "What time is good for you?",
|
||||
"zh": "你几点方便?",
|
||||
"itemIds": [
|
||||
"A1-U13-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Six pm is good for me.",
|
||||
"goal": "约在饭店六点见",
|
||||
"model": "Let's meet at six at the restaurant.",
|
||||
"zh": "我们六点在饭店见吧。",
|
||||
"itemIds": [
|
||||
"A1-U13-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Great. I can't wait!",
|
||||
"goal": "说到时见",
|
||||
"model": "See you then.",
|
||||
"zh": "到时见。",
|
||||
"itemIds": [
|
||||
"A1-U13-P09"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-C2",
|
||||
"kind": "variant",
|
||||
"title": "邀请同事来生日聚会",
|
||||
"learnerRole": "过生日的人",
|
||||
"aiRole": "同事",
|
||||
"setting": "你下周过生日,想邀请同事来聚会。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! You look happy today.",
|
||||
"goal": "说你的生日在下周",
|
||||
"model": "My birthday is next week.",
|
||||
"zh": "我的生日在下周。",
|
||||
"itemIds": [
|
||||
"A1-U13-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh, nice! What's the date?",
|
||||
"goal": "说是五月九日,请对方来聚会",
|
||||
"model": "It's May 9. Come to my party!",
|
||||
"zh": "是五月九日。来参加我的聚会吧!",
|
||||
"itemIds": [
|
||||
"A1-U13-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I'd love to. When is it?",
|
||||
"goal": "说还不知道时间,也许明天知道",
|
||||
"model": "I don't know the time yet. Maybe tomorrow.",
|
||||
"zh": "我还不知道时间,也许明天就知道。",
|
||||
"itemIds": [
|
||||
"A1-U13-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. When is your birthday, then?",
|
||||
"goal": "说你的生日在五月",
|
||||
"model": "My birthday is in May.",
|
||||
"zh": "我的生日在五月。",
|
||||
"itemIds": [
|
||||
"A1-U13-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "And when is my birthday? Do you remember?",
|
||||
"goal": "反问对方生日是哪天",
|
||||
"model": "No! When is your birthday?",
|
||||
"zh": "不记得!你的生日是哪天?",
|
||||
"itemIds": [
|
||||
"A1-U13-P02"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-C3",
|
||||
"kind": "variant",
|
||||
"title": "改期",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "朋友",
|
||||
"setting": "你们本来约好明天见,但你临时有事,需要改期。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! Are we OK for tomorrow at three?",
|
||||
"goal": "说抱歉明天没空",
|
||||
"model": "Sorry, I'm busy tomorrow.",
|
||||
"zh": "抱歉,我明天没空。",
|
||||
"itemIds": [
|
||||
"A1-U13-P05",
|
||||
"A1-U13-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh no. What's the problem?",
|
||||
"goal": "问能不能改个日期",
|
||||
"model": "I have to work. Can we change the date?",
|
||||
"zh": "我要上班。我们能改个日期吗?",
|
||||
"itemIds": [
|
||||
"A1-U13-P11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. When are you free?",
|
||||
"goal": "说下周有空",
|
||||
"model": "I'm free next week.",
|
||||
"zh": "我下周有空。",
|
||||
"itemIds": [
|
||||
"A1-U13-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Next week I'm busy on Monday.",
|
||||
"goal": "提议下周五",
|
||||
"model": "How about Friday?",
|
||||
"zh": "周五怎么样?",
|
||||
"itemIds": [
|
||||
"A1-U13-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Friday is good.",
|
||||
"goal": "问今天几号,确认日期",
|
||||
"model": "Great. What's the date today?",
|
||||
"zh": "太好了。今天几号?",
|
||||
"itemIds": [
|
||||
"A1-U13-P01"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U13-T1",
|
||||
"sceneId": "A1-U13-C1",
|
||||
"goal": "约朋友周末吃饭,定下日子、时间和地点",
|
||||
"itemIds": [
|
||||
"A1-U13-P04",
|
||||
"A1-U13-P06",
|
||||
"A1-U13-P08",
|
||||
"A1-U13-P07",
|
||||
"A1-U13-P09"
|
||||
],
|
||||
"slots": [
|
||||
"哪天",
|
||||
"几点",
|
||||
"在哪儿"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-T2",
|
||||
"sceneId": "A1-U13-C2",
|
||||
"goal": "邀请同事参加你的生日聚会",
|
||||
"itemIds": [
|
||||
"A1-U13-P10",
|
||||
"A1-U13-W02",
|
||||
"A1-U13-W01",
|
||||
"A1-U13-P03",
|
||||
"A1-U13-P02"
|
||||
],
|
||||
"slots": [
|
||||
"日期",
|
||||
"聚会",
|
||||
"生日"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-T3",
|
||||
"sceneId": "A1-U13-C3",
|
||||
"goal": "临时没空,和朋友改约时间",
|
||||
"itemIds": [
|
||||
"A1-U13-P05",
|
||||
"A1-U13-W01",
|
||||
"A1-U13-P11",
|
||||
"A1-U13-P10",
|
||||
"A1-U13-P06",
|
||||
"A1-U13-P01"
|
||||
],
|
||||
"slots": [
|
||||
"原因",
|
||||
"新日期"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U13-M1",
|
||||
"mode": "listening",
|
||||
"title": "朋友打来约生日聚会",
|
||||
"text": "Hi, it's me! I want to have a birthday party next month, in April. Are you free? My birthday is on the fifteenth. Please remember the date and write it on your calendar; don't forget! The party is at my home at three pm. My mom and dad will cook, and we will cut a big cake and celebrate together. I hope you can come. Maybe you can bring a small gift. I invited some old friends too. If Saturday is not good, how about later, on Sunday? I wish you a good week. Let me know. Bye!",
|
||||
"glosses": {
|
||||
"games": "游戏",
|
||||
"fifteenth": "(每月)十五号",
|
||||
"invited": "邀请(过去式)",
|
||||
"bring": "带来"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段留言是关于什么的?",
|
||||
"options": [
|
||||
"邀请你参加生日聚会",
|
||||
"改机票",
|
||||
"问天气"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "聚会几点开始?",
|
||||
"options": [
|
||||
"下午三点",
|
||||
"上午三点",
|
||||
"晚上七点"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U13-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:约见面时间",
|
||||
"text": "Ben: Are you free on Sunday? Let's meet.\nMia: Sorry, I'm busy on Sunday. My cousin's wedding is that day! How about Monday?\nBen: Monday is good. What time? Do you remember the cafe near the station?\nMia: Yes. Let's meet at ten in the morning. Don't forget!\nBen: I won't. Oh, can we change the time to eleven?\nMia: Sure. Wait, next Monday is a holiday, a big festival. Maybe the cafe is closed.\nBen: Good point. Let me check the calendar. It's open. See you at eleven then.\nMia: Great. I hope the weather is nice. Do you want lunch after that?\nBen: Yes! And happy birthday for last month, by the way.\nMia: Oh, that was months ago! Thank you. See you Monday. Bye!\nBen: Bye, and good luck at the wedding!",
|
||||
"glosses": {
|
||||
"sounds": "听起来",
|
||||
"point": "good point 说得对",
|
||||
"won't": "不会",
|
||||
"check": "查看",
|
||||
"weather": "天气"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "他们最后约在星期几?",
|
||||
"options": [
|
||||
"星期一",
|
||||
"星期天",
|
||||
"星期六"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Mia 星期天为什么没空?",
|
||||
"options": [
|
||||
"要去参加表亲的婚礼",
|
||||
"要上班",
|
||||
"要考试"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "appointment",
|
||||
"reason": "A2;A1 先用 meet 表达约见"
|
||||
},
|
||||
{
|
||||
"en": "fifth",
|
||||
"reason": "序数词 fourth 以后不在 A1 表里;日期先用数字读法(May 5)"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,961 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U14",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "在机场",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "值机、问登机口、回答入境的简单问题",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "61",
|
||||
"use": "active",
|
||||
"reason": "只用 I'm staying / I'm visiting 说行程"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "241",
|
||||
"use": "active",
|
||||
"reason": "CEFR-J 未标级别,Which gate 是机场里必需的问句"
|
||||
},
|
||||
{
|
||||
"id": "245",
|
||||
"use": "active"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U14-P01",
|
||||
"type": "phrase",
|
||||
"en": "Here is my passport.",
|
||||
"zh": "这是我的护照。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "passport 在 CEFR-J 为 B1,但出入境时必须会说",
|
||||
"match": [
|
||||
[
|
||||
"my passport"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Here is my passport and ticket.",
|
||||
"zh": "这是我的护照和机票。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "工作人员要看证件,把护照递给他。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-W01",
|
||||
"type": "word",
|
||||
"en": "flight",
|
||||
"zh": "航班",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "flight 为 A2,是机场里最常听到的词",
|
||||
"match": [
|
||||
[
|
||||
"flight",
|
||||
"flights"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My flight is at ten.",
|
||||
"zh": "我的航班是十点的。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的航班几点。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-W02",
|
||||
"type": "word",
|
||||
"en": "plane",
|
||||
"zh": "飞机",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"plane",
|
||||
"planes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The plane is late.",
|
||||
"zh": "飞机晚点了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说飞机晚点了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P02",
|
||||
"type": "phrase",
|
||||
"en": "Which gate is it?",
|
||||
"zh": "在几号登机口?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"which gate"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Which gate is my flight?",
|
||||
"zh": "我的航班在几号登机口?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问你的航班在哪个登机口。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P03",
|
||||
"type": "phrase",
|
||||
"en": "I have one bag to check.",
|
||||
"zh": "我有一件行李要托运。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"bag to check",
|
||||
"bags to check"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I have two bags to check.",
|
||||
"zh": "我有两件行李要托运。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你有一件行李要托运。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P04",
|
||||
"type": "phrase",
|
||||
"en": "A window seat, please.",
|
||||
"zh": "请给我靠窗的座位。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"window seat",
|
||||
"aisle seat"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I have an aisle seat?",
|
||||
"zh": "可以给我靠过道的座位吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "要一个靠窗的座位。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P05",
|
||||
"type": "phrase",
|
||||
"en": "Is it on time?",
|
||||
"zh": "准点吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"on time"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is the plane on time?",
|
||||
"zh": "飞机准点吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问航班是否准点。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P06",
|
||||
"type": "phrase",
|
||||
"en": "I'm here on holiday.",
|
||||
"zh": "我来度假。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"on holiday",
|
||||
"on vacation",
|
||||
"on business"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm here on business.",
|
||||
"zh": "我来出差。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "入境时说你来度假。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P07",
|
||||
"type": "phrase",
|
||||
"en": "I'm visiting friends.",
|
||||
"zh": "我来看朋友。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"visiting"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm visiting my sister.",
|
||||
"zh": "我来看我姐姐。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你来看朋友。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P08",
|
||||
"type": "phrase",
|
||||
"en": "I'm staying for two weeks.",
|
||||
"zh": "我待两周。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"staying for"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm staying for five days.",
|
||||
"zh": "我待五天。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你要待一周。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P09",
|
||||
"type": "phrase",
|
||||
"en": "I'm staying at a hotel.",
|
||||
"zh": "我住酒店。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"staying at",
|
||||
"staying with"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm staying with a friend.",
|
||||
"zh": "我住朋友家。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你住在哪里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P10",
|
||||
"type": "phrase",
|
||||
"en": "It's my first visit.",
|
||||
"zh": "这是我第一次来。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"first visit",
|
||||
"first time"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's my first time here.",
|
||||
"zh": "这是我第一次来这里。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你第一次来这个国家。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-P11",
|
||||
"type": "phrase",
|
||||
"en": "Where can I get my bags?",
|
||||
"zh": "在哪里取行李?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"get my bags",
|
||||
"get my bag"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Where can I get my bag?",
|
||||
"zh": "我在哪里取行李?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "下飞机后问在哪里取行李。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U14-R01",
|
||||
"en": "airplane",
|
||||
"zh": "飞机"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R02",
|
||||
"en": "fly",
|
||||
"zh": "飞;乘飞机"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R03",
|
||||
"en": "jet",
|
||||
"zh": "喷气式飞机"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R04",
|
||||
"en": "vacation",
|
||||
"zh": "假期"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R05",
|
||||
"en": "foreign",
|
||||
"zh": "外国的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R06",
|
||||
"en": "foreigner",
|
||||
"zh": "外国人"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R07",
|
||||
"en": "nationality",
|
||||
"zh": "国籍"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R08",
|
||||
"en": "information",
|
||||
"zh": "信息"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R09",
|
||||
"en": "business",
|
||||
"zh": "商务;生意"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R10",
|
||||
"en": "camera",
|
||||
"zh": "相机"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R11",
|
||||
"en": "row",
|
||||
"zh": "排"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R12",
|
||||
"en": "wheel",
|
||||
"zh": "轮子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R13",
|
||||
"en": "world",
|
||||
"zh": "世界"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R14",
|
||||
"en": "language",
|
||||
"zh": "语言"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R15",
|
||||
"en": "island",
|
||||
"zh": "岛"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R16",
|
||||
"en": "pleasure",
|
||||
"zh": "荣幸(My pleasure.)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R17",
|
||||
"en": "stand",
|
||||
"zh": "站立"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R18",
|
||||
"en": "step",
|
||||
"zh": "迈步"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R19",
|
||||
"en": "push",
|
||||
"zh": "推"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R20",
|
||||
"en": "hold",
|
||||
"zh": "拿着"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R21",
|
||||
"en": "drop",
|
||||
"zh": "掉下"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R22",
|
||||
"en": "out",
|
||||
"zh": "出去"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R23",
|
||||
"en": "away",
|
||||
"zh": "离开"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R24",
|
||||
"en": "land",
|
||||
"zh": "降落"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R25",
|
||||
"en": "delay",
|
||||
"zh": "延误"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R26",
|
||||
"en": "screen",
|
||||
"zh": "屏幕"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R27",
|
||||
"en": "move",
|
||||
"zh": "移动"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R28",
|
||||
"en": "country",
|
||||
"zh": "国家"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R29",
|
||||
"en": "alone",
|
||||
"zh": "单独地"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R30",
|
||||
"en": "why",
|
||||
"zh": "为什么"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-R31",
|
||||
"en": "hand",
|
||||
"zh": "手"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U14-S1",
|
||||
"title": "值机",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U14-P01",
|
||||
"A1-U14-W01",
|
||||
"A1-U14-P03",
|
||||
"A1-U14-P04"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Good morning. Where are you flying today? To London. Here is my passport. Thank you. Do you have any bags? I have one bag to check. And a window seat, please.",
|
||||
"question": "乘客要什么座位?",
|
||||
"options": [
|
||||
"靠窗",
|
||||
"靠过道",
|
||||
"最前排"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Here is my passport."
|
||||
},
|
||||
{
|
||||
"text": "My flight is at ten."
|
||||
},
|
||||
{
|
||||
"text": "I have one bag to check."
|
||||
},
|
||||
{
|
||||
"text": "A window seat, please.",
|
||||
"tip": "window seat 两个词都要重读"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Check-in information\n1. Have your passport and ticket ready.\n2. Put your bag on the machine. One bag can be 23 kilos.\n3. Take your ticket. It has your gate and seat: Row 15, seat A is a window seat.\n4. Please don't leave your bags alone! Big jets fly from here to every island and country in the world.",
|
||||
"question": "靠窗座位在哪一排?",
|
||||
"options": [
|
||||
"第十五排",
|
||||
"第二十三排",
|
||||
"第一排"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你有一件行李要托运,再要一个靠窗座位。",
|
||||
"example": "I have one bag to check. A window seat, please.",
|
||||
"itemIds": [
|
||||
"A1-U14-P03",
|
||||
"A1-U14-P04"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语跟值机柜台说你的航班、行李和座位要求。",
|
||||
"itemIds": [
|
||||
"A1-U14-W01",
|
||||
"A1-U14-P03",
|
||||
"A1-U14-P04",
|
||||
"A1-U14-P01"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-S2",
|
||||
"title": "登机口与延误",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U14-W02",
|
||||
"A1-U14-P02",
|
||||
"A1-U14-P05"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me, which gate is it for the flight to Tokyo? Gate 12. Is it on time? No, sorry. There's a delay. The plane is late. It can leave at 3:30.",
|
||||
"question": "飞机大概几点出发?",
|
||||
"options": [
|
||||
"三点半",
|
||||
"十二点",
|
||||
"两点半"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Which gate is it?"
|
||||
},
|
||||
{
|
||||
"text": "Is it on time?"
|
||||
},
|
||||
{
|
||||
"text": "The plane is late."
|
||||
},
|
||||
{
|
||||
"text": "I can see the planes out the window."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "The screen at the airport\nFlight 205 to Seoul: Gate 8: ON TIME\nFlight 318 to Bangkok: Gate 14: DELAY: now 16:40\nFlight 422 to Paris: Gate 3: GO TO GATE NOW\nPlease move to your gate. Please don't lose your ticket. The airplane from Singapore can't land now, so it is late.",
|
||||
"question": "去曼谷的航班怎么了?",
|
||||
"options": [
|
||||
"延误了",
|
||||
"正在登机",
|
||||
"准点"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问在几号登机口,再问准点吗。",
|
||||
"example": "Which gate is it? Is it on time?",
|
||||
"itemIds": [
|
||||
"A1-U14-P02",
|
||||
"A1-U14-P05"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你看不懂屏幕,用英语问工作人员你的航班在哪个登机口、准不准点。",
|
||||
"itemIds": [
|
||||
"A1-U14-W01",
|
||||
"A1-U14-P02",
|
||||
"A1-U14-P05"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-S3",
|
||||
"title": "入境与取行李",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U14-P06",
|
||||
"A1-U14-P07",
|
||||
"A1-U14-P08",
|
||||
"A1-U14-P09",
|
||||
"A1-U14-P10",
|
||||
"A1-U14-P11"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hello. Why are you here? I'm here on holiday. I'm visiting friends. How long are you staying? I'm staying for two weeks. Where are you staying? At a hotel in the city.",
|
||||
"question": "旅客待多久?",
|
||||
"options": [
|
||||
"两周",
|
||||
"两天",
|
||||
"两个月"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I'm here on holiday."
|
||||
},
|
||||
{
|
||||
"text": "I'm visiting friends."
|
||||
},
|
||||
{
|
||||
"text": "I'm staying for two weeks."
|
||||
},
|
||||
{
|
||||
"text": "I'm staying at a hotel."
|
||||
},
|
||||
{
|
||||
"text": "It's my first visit."
|
||||
},
|
||||
{
|
||||
"text": "Where can I get my bags?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Welcome to our country: for foreign passports\nForeigners on vacation or business: stand in line and wait. Step up when the light is green.\nHold your passport in your hand. Have your address in this country ready.\nThey may ask about your trip, your nationality or your language.\nThen get your bags and move away to the door. Bags with wheels are easy to push. Please don't drop your camera or phone! Put them in your pocket.",
|
||||
"question": "外国游客排队时要准备好什么?",
|
||||
"options": [
|
||||
"护照和在当地的地址",
|
||||
"相机和手机",
|
||||
"行李和推车"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写三句话:说你来度假、待两周、住酒店。",
|
||||
"example": "I'm here on holiday. I'm staying for two weeks. I'm staying at a hotel.",
|
||||
"itemIds": [
|
||||
"A1-U14-P06",
|
||||
"A1-U14-P08",
|
||||
"A1-U14-P09"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "入境官问你为什么来、待多久、住哪儿,用英语回答。",
|
||||
"itemIds": [
|
||||
"A1-U14-P06",
|
||||
"A1-U14-P07",
|
||||
"A1-U14-P08",
|
||||
"A1-U14-P09",
|
||||
"A1-U14-P10"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U14-C1",
|
||||
"kind": "main",
|
||||
"title": "入境检查",
|
||||
"learnerRole": "旅客",
|
||||
"aiRole": "入境检查官",
|
||||
"setting": "你刚下飞机,在入境柜台回答检查官的问题。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Good afternoon. Passport, please.",
|
||||
"goal": "递上护照",
|
||||
"model": "Here is my passport.",
|
||||
"zh": "这是我的护照。",
|
||||
"itemIds": [
|
||||
"A1-U14-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thank you. Why are you visiting?",
|
||||
"goal": "说你来度假,也来看朋友",
|
||||
"model": "I'm here on holiday. I'm visiting friends.",
|
||||
"zh": "我来度假,也来看朋友。",
|
||||
"itemIds": [
|
||||
"A1-U14-P06",
|
||||
"A1-U14-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "How long are you staying?",
|
||||
"goal": "说待两周",
|
||||
"model": "I'm staying for two weeks.",
|
||||
"zh": "我待两周。",
|
||||
"itemIds": [
|
||||
"A1-U14-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "And where are you staying?",
|
||||
"goal": "说住在一家酒店",
|
||||
"model": "I'm staying at a hotel in the city.",
|
||||
"zh": "我住在市里的一家酒店。",
|
||||
"itemIds": [
|
||||
"A1-U14-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Is this your first time in our country?",
|
||||
"goal": "说是第一次来,再问在哪取行李",
|
||||
"model": "Yes, it's my first visit. Where can I get my bags?",
|
||||
"zh": "是的,第一次来。我在哪里取行李?",
|
||||
"itemIds": [
|
||||
"A1-U14-P10",
|
||||
"A1-U14-P11"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-C2",
|
||||
"kind": "variant",
|
||||
"title": "值机柜台",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "值机员",
|
||||
"setting": "你在机场值机柜台办理登机手续。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Good morning. Where are you flying today?",
|
||||
"goal": "说去东京,递上护照",
|
||||
"model": "To Tokyo. Here is my passport.",
|
||||
"zh": "去东京。这是我的护照。",
|
||||
"itemIds": [
|
||||
"A1-U14-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thank you. Do you have any bags?",
|
||||
"goal": "说有一件行李要托运",
|
||||
"model": "Yes, I have one bag to check.",
|
||||
"zh": "是的,我有一件行李要托运。",
|
||||
"itemIds": [
|
||||
"A1-U14-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. Put it here, please. A window or an aisle seat?",
|
||||
"goal": "要靠窗的座位",
|
||||
"model": "A window seat, please.",
|
||||
"zh": "请给我靠窗的座位。",
|
||||
"itemIds": [
|
||||
"A1-U14-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Here is your ticket.",
|
||||
"goal": "问在几号登机口",
|
||||
"model": "Thank you. Which gate is it?",
|
||||
"zh": "谢谢。在几号登机口?",
|
||||
"itemIds": [
|
||||
"A1-U14-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Gate 22. Your flight leaves at 11:30.",
|
||||
"goal": "问航班准不准点",
|
||||
"model": "Is my flight on time?",
|
||||
"zh": "我的航班准点吗?",
|
||||
"itemIds": [
|
||||
"A1-U14-W01",
|
||||
"A1-U14-P05"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-C3",
|
||||
"kind": "variant",
|
||||
"title": "航班延误",
|
||||
"learnerRole": "乘客",
|
||||
"aiRole": "登机口工作人员",
|
||||
"setting": "你在登机口等了很久,飞机还没来。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. Can I help you?",
|
||||
"goal": "问航班准不准点",
|
||||
"model": "Is my flight on time?",
|
||||
"zh": "我的航班准点吗?",
|
||||
"itemIds": [
|
||||
"A1-U14-W01",
|
||||
"A1-U14-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, there's a delay. The plane is late. It can't land now.",
|
||||
"goal": "问飞机几点到",
|
||||
"model": "What time is the plane here?",
|
||||
"zh": "飞机几点到?",
|
||||
"itemIds": [
|
||||
"A1-U14-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "At about four. Are you OK?",
|
||||
"goal": "说你在酒店住到明天,时间没问题",
|
||||
"model": "Yes. I'm staying at a hotel until tomorrow.",
|
||||
"zh": "没事。我在酒店住到明天。",
|
||||
"itemIds": [
|
||||
"A1-U14-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good. Is this your first visit?",
|
||||
"goal": "说是第一次来,来看朋友",
|
||||
"model": "Yes, it's my first visit. I'm visiting friends.",
|
||||
"zh": "是的,第一次来。我来看朋友。",
|
||||
"itemIds": [
|
||||
"A1-U14-P10",
|
||||
"A1-U14-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "My pleasure. Please wait here.",
|
||||
"goal": "问是不是这个登机口",
|
||||
"model": "OK. Which gate is it, again?",
|
||||
"zh": "好的。再问一下,是哪个登机口?",
|
||||
"itemIds": [
|
||||
"A1-U14-P02"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U14-T1",
|
||||
"sceneId": "A1-U14-C1",
|
||||
"goal": "回答入境检查官关于来意、停留和住处的问题",
|
||||
"itemIds": [
|
||||
"A1-U14-P01",
|
||||
"A1-U14-P06",
|
||||
"A1-U14-P07",
|
||||
"A1-U14-P08",
|
||||
"A1-U14-P09",
|
||||
"A1-U14-P10",
|
||||
"A1-U14-P11"
|
||||
],
|
||||
"slots": [
|
||||
"来意",
|
||||
"停留时间",
|
||||
"住处"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-T2",
|
||||
"sceneId": "A1-U14-C2",
|
||||
"goal": "办理值机:行李、座位和登机口",
|
||||
"itemIds": [
|
||||
"A1-U14-P01",
|
||||
"A1-U14-P03",
|
||||
"A1-U14-P04",
|
||||
"A1-U14-P02",
|
||||
"A1-U14-W01",
|
||||
"A1-U14-P05"
|
||||
],
|
||||
"slots": [
|
||||
"行李",
|
||||
"座位",
|
||||
"登机口"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-T3",
|
||||
"sceneId": "A1-U14-C3",
|
||||
"goal": "航班延误时向工作人员问清楚情况",
|
||||
"itemIds": [
|
||||
"A1-U14-W01",
|
||||
"A1-U14-P05",
|
||||
"A1-U14-W02",
|
||||
"A1-U14-P09",
|
||||
"A1-U14-P10",
|
||||
"A1-U14-P07",
|
||||
"A1-U14-P02"
|
||||
],
|
||||
"slots": [
|
||||
"是否准点",
|
||||
"到达时间"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U14-M1",
|
||||
"mode": "listening",
|
||||
"title": "机场登机广播",
|
||||
"text": "Good morning. This is a message for flight CA108 to London. Please go to gate twelve now. Have your passport and boarding pass ready, in your hand. Your seat is in row twenty. The flight is on time; it will not be delayed. It's a long flight over the sea to a foreign country, about ten hours. Please do not stand or move in the plane when we fly, land or when the wheels are down. Turn off your phone and look at the screen for information. Please do not push. Have a good vacation, and welcome to the world of flying!",
|
||||
"glosses": {
|
||||
"pass": "boarding pass 登机牌",
|
||||
"message": "留言,信息",
|
||||
"boarding": "登机",
|
||||
"delayed": "延误的",
|
||||
"flying": "飞行",
|
||||
"off": "关掉",
|
||||
"sea": "大海"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段广播主要通知什么?",
|
||||
"options": [
|
||||
"请乘客去登机口登机",
|
||||
"飞机取消了",
|
||||
"改坐火车"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "这个航班去哪里?",
|
||||
"options": [
|
||||
"伦敦",
|
||||
"东京",
|
||||
"北京"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U14-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:入境问答",
|
||||
"text": "Officer: Passport, please. What's your nationality, and why are you here?\nBen: Here you are. I'm here on holiday, for a vacation. It's my first visit to your country.\nOfficer: Are you traveling alone or on business?\nBen: Alone, not for business. I'm visiting friends and I want to see the world.\nOfficer: How long are you staying?\nBen: Two weeks. I'll stay at a hotel near the city center, and then fly to an island for some days.\nOfficer: Do you speak our language?\nBen: A little. Please give me some information if I need help.\nOfficer: Of course. Do you have a camera in your bag?\nBen: Yes, one camera. Please don't drop it!\nOfficer: No, I won't. Welcome, and enjoy your holiday.\nBen: Thank you. My pleasure!",
|
||||
"glosses": {
|
||||
"reason": "原因",
|
||||
"officer": "(海关)官员",
|
||||
"course": "of course 当然",
|
||||
"won't": "不会",
|
||||
"give": "给"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Ben 来做什么?",
|
||||
"options": [
|
||||
"度假旅游",
|
||||
"工作出差",
|
||||
"上学"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "他一个人来还是出差?",
|
||||
"options": [
|
||||
"一个人来",
|
||||
"出差",
|
||||
"和家人来"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "customs",
|
||||
"reason": "B1;A1 只练入境问答"
|
||||
},
|
||||
{
|
||||
"en": "luggage",
|
||||
"reason": "B1;A1 用 bag / bags 表达行李"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,918 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U15",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "入住酒店",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说明预订信息、办理入住",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "239",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "146",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "21",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "22",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U15-P01",
|
||||
"type": "phrase",
|
||||
"en": "I have a reservation.",
|
||||
"zh": "我有预订。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "reservation 为 B1,但入住酒店第一句就要用",
|
||||
"match": [
|
||||
[
|
||||
"have a reservation"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Hello, I have a reservation for tonight.",
|
||||
"zh": "你好,我预订了今晚的房间。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "到了前台,说你有预订。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P02",
|
||||
"type": "phrase",
|
||||
"en": "It's under the name Chen.",
|
||||
"zh": "预订名字是陈。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"under the name"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The reservation is under the name Wang.",
|
||||
"zh": "预订用的是王这个名字。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉前台预订用的名字。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P03",
|
||||
"type": "phrase",
|
||||
"en": "for three nights",
|
||||
"zh": "住三晚",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"night",
|
||||
"nights"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm staying for one night.",
|
||||
"zh": "我住一晚。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你要住两晚。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P04",
|
||||
"type": "phrase",
|
||||
"en": "Can I check in now?",
|
||||
"zh": "我现在可以入住吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"check in"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What time can I check in?",
|
||||
"zh": "我几点可以入住?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问现在能不能入住。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P05",
|
||||
"type": "phrase",
|
||||
"en": "What time is check-out?",
|
||||
"zh": "几点退房?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"check-out",
|
||||
"check out"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I check out late?",
|
||||
"zh": "我可以晚点退房吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问几点退房。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P06",
|
||||
"type": "phrase",
|
||||
"en": "Do I fill in this form?",
|
||||
"zh": "我要填这张表吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"fill in"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Please fill in this form.",
|
||||
"zh": "请填一下这张表。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问要不要填表。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P07",
|
||||
"type": "phrase",
|
||||
"en": "Here is your key.",
|
||||
"zh": "这是您的钥匙。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"key",
|
||||
"keys"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I have two keys?",
|
||||
"zh": "可以给我两把钥匙吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "要两把房间钥匙。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P08",
|
||||
"type": "phrase",
|
||||
"en": "What's the Wi-Fi password?",
|
||||
"zh": "Wi-Fi 密码是多少?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "password 为 B1,住酒店时几乎每次都要问",
|
||||
"match": [
|
||||
[
|
||||
"password"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What's the password, please?",
|
||||
"zh": "请问密码是多少?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问 Wi-Fi 密码。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P09",
|
||||
"type": "phrase",
|
||||
"en": "What time is breakfast?",
|
||||
"zh": "早饭几点?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"time is breakfast"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What time is breakfast tomorrow?",
|
||||
"zh": "明天早饭几点?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问早饭时间。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P10",
|
||||
"type": "phrase",
|
||||
"en": "Where is the elevator?",
|
||||
"zh": "电梯在哪里?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "elevator 为 A2,酒店里找路必需",
|
||||
"match": [
|
||||
[
|
||||
"elevator"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The elevator is next to the bar.",
|
||||
"zh": "电梯在酒吧旁边。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问电梯在哪里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P11",
|
||||
"type": "phrase",
|
||||
"en": "Can I have a quiet room?",
|
||||
"zh": "能给我一间安静的房间吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"quiet room"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I have a quiet room with a view?",
|
||||
"zh": "能给我一间安静、有景色的房间吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "要一间安静的房间。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P12",
|
||||
"type": "phrase",
|
||||
"en": "Can you keep my bags?",
|
||||
"zh": "能帮我寄存行李吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"keep my bags",
|
||||
"keep my bag"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can you keep my bags until three?",
|
||||
"zh": "能帮我寄存行李到三点吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "退房后请前台寄存行李。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-P13",
|
||||
"type": "phrase",
|
||||
"en": "Can I pay later?",
|
||||
"zh": "我可以晚点付钱吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"pay later"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I pay later, at check-out?",
|
||||
"zh": "我可以退房时再付吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问能不能晚点付钱。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U15-R01",
|
||||
"en": "guest",
|
||||
"zh": "客人"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R02",
|
||||
"en": "code",
|
||||
"zh": "代码;密码"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R03",
|
||||
"en": "swimming pool",
|
||||
"zh": "游泳池"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R04",
|
||||
"en": "bath",
|
||||
"zh": "浴缸;洗澡"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R05",
|
||||
"en": "hall",
|
||||
"zh": "大厅"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R06",
|
||||
"en": "clock",
|
||||
"zh": "钟"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R07",
|
||||
"en": "sea",
|
||||
"zh": "大海"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R08",
|
||||
"en": "beach",
|
||||
"zh": "海滩"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R09",
|
||||
"en": "tower",
|
||||
"zh": "塔"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R10",
|
||||
"en": "garbage",
|
||||
"zh": "垃圾"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R11",
|
||||
"en": "note",
|
||||
"zh": "便条"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R12",
|
||||
"en": "copy",
|
||||
"zh": "复印件"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R13",
|
||||
"en": "rule",
|
||||
"zh": "规定"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R14",
|
||||
"en": "smoking",
|
||||
"zh": "吸烟"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R15",
|
||||
"en": "bar",
|
||||
"zh": "酒吧"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R16",
|
||||
"en": "receptionist",
|
||||
"zh": "前台接待员"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R17",
|
||||
"en": "double",
|
||||
"zh": "双人的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R18",
|
||||
"en": "view",
|
||||
"zh": "景色"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R19",
|
||||
"en": "balcony",
|
||||
"zh": "阳台"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R20",
|
||||
"en": "surname",
|
||||
"zh": "姓"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R21",
|
||||
"en": "ID",
|
||||
"zh": "身份证件"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R22",
|
||||
"en": "high",
|
||||
"zh": "高的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R23",
|
||||
"en": "star",
|
||||
"zh": "星级;星星"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R24",
|
||||
"en": "hill",
|
||||
"zh": "小山"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-R25",
|
||||
"en": "own",
|
||||
"zh": "自己的"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U15-S1",
|
||||
"title": "办理入住",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U15-P01",
|
||||
"A1-U15-P02",
|
||||
"A1-U15-P03",
|
||||
"A1-U15-P04",
|
||||
"A1-U15-P06"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Good afternoon. I have a reservation. It's under the name Chen. For three nights. Yes, a double room. Can I check in now? Sure. Please fill in this form.",
|
||||
"question": "客人住几晚?",
|
||||
"options": [
|
||||
"三晚",
|
||||
"两晚",
|
||||
"一晚"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I have a reservation.",
|
||||
"tip": "reservation 重音在第三个音节:res-er-VA-tion"
|
||||
},
|
||||
{
|
||||
"text": "It's under the name Chen."
|
||||
},
|
||||
{
|
||||
"text": "I'm staying for three nights."
|
||||
},
|
||||
{
|
||||
"text": "Can I check in now?"
|
||||
},
|
||||
{
|
||||
"text": "Do I fill in this form?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Guest form\nSurname: ______ First name: ______\nNationality: ______\nID or passport number: ______\nRoom: single / double\nNights: ______\nEach guest must fill in their own form. If you have a child, write his or her name below yours. Please show us a copy of your passport.",
|
||||
"question": "带孩子的客人要怎么填?",
|
||||
"options": [
|
||||
"把孩子的名字写在自己下面",
|
||||
"给孩子另填一张",
|
||||
"不用写孩子"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你有预订,名字是什么。",
|
||||
"example": "I have a reservation. It's under the name Chen.",
|
||||
"itemIds": [
|
||||
"A1-U15-P01",
|
||||
"A1-U15-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语跟前台说你的预订、住几晚,并问现在能不能入住。",
|
||||
"itemIds": [
|
||||
"A1-U15-P01",
|
||||
"A1-U15-P02",
|
||||
"A1-U15-P03",
|
||||
"A1-U15-P04"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-S2",
|
||||
"title": "房间和钥匙",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U15-P07",
|
||||
"A1-U15-P08",
|
||||
"A1-U15-P09",
|
||||
"A1-U15-P10"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Here is your key. Your room is 1208, on floor 12. The elevator is over there. Breakfast is from seven to ten. And the Wi-Fi password is on the card.",
|
||||
"question": "早饭几点开始?",
|
||||
"options": [
|
||||
"七点",
|
||||
"十点",
|
||||
"十二点"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Here is your key."
|
||||
},
|
||||
{
|
||||
"text": "What's the Wi-Fi password?"
|
||||
},
|
||||
{
|
||||
"text": "What time is breakfast?"
|
||||
},
|
||||
{
|
||||
"text": "Where is the elevator?",
|
||||
"tip": "elevator 重音在第一个音节:EL-e-va-tor"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Welcome to Sea View Hotel: four stars\nBreakfast: 7:00-10:00, in the hall on the first floor.\nSwimming pool: on the high floor above the bar. It's open to everyone until 9 pm.\nWi-Fi: the password is on your key card. The code for the door is 4471.\nRules: no smoking inside. Please put garbage in the bag beside the door. The clock in the hall is always right.",
|
||||
"question": "游泳池在哪里?",
|
||||
"options": [
|
||||
"酒吧楼上的高层",
|
||||
"一楼大厅",
|
||||
"前台旁边"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问 Wi-Fi 密码,再问早饭几点。",
|
||||
"example": "What's the Wi-Fi password? What time is breakfast?",
|
||||
"itemIds": [
|
||||
"A1-U15-P08",
|
||||
"A1-U15-P09"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "刚拿到钥匙,用英语问电梯在哪、早饭几点。",
|
||||
"itemIds": [
|
||||
"A1-U15-P07",
|
||||
"A1-U15-P10",
|
||||
"A1-U15-P09"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-S3",
|
||||
"title": "特别需求",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U15-P05",
|
||||
"A1-U15-P11",
|
||||
"A1-U15-P12",
|
||||
"A1-U15-P13"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me, what time is check-out? Twelve. Can you keep my bags? My flight is at six. Yes. And can I pay later? Yes, at check-out.",
|
||||
"question": "客人为什么要寄存行李?",
|
||||
"options": [
|
||||
"航班在六点",
|
||||
"房间太吵",
|
||||
"他要去海滩"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What time is check-out?"
|
||||
},
|
||||
{
|
||||
"text": "Can I have a quiet room?"
|
||||
},
|
||||
{
|
||||
"text": "Can you keep my bags?"
|
||||
},
|
||||
{
|
||||
"text": "Can I pay later?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "A note from the receptionist\nDear guest,\nYour room has a bath and a balcony with a view of the sea and the beach. From there, you can see the old tower on the hill. Someone in the room next to yours is not quiet, so we can move you to a quiet room. Check-out is at 11:00. We can keep your bags after that.",
|
||||
"question": "酒店可以帮客人做什么?",
|
||||
"options": [
|
||||
"换到安静的房间",
|
||||
"送去海滩",
|
||||
"免费早饭"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问几点退房,再请前台寄存行李。",
|
||||
"example": "What time is check-out? Can you keep my bags?",
|
||||
"itemIds": [
|
||||
"A1-U15-P05",
|
||||
"A1-U15-P12"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你的航班很晚,用英语问退房时间并请酒店寄存行李。",
|
||||
"itemIds": [
|
||||
"A1-U15-P05",
|
||||
"A1-U15-P12",
|
||||
"A1-U15-P13"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U15-C1",
|
||||
"kind": "main",
|
||||
"title": "前台入住",
|
||||
"learnerRole": "客人",
|
||||
"aiRole": "前台接待员",
|
||||
"setting": "你到达酒店前台,办理入住。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Good evening. Welcome to Sea View Hotel.",
|
||||
"goal": "说你有预订",
|
||||
"model": "Hi. I have a reservation.",
|
||||
"zh": "你好,我有预订。",
|
||||
"itemIds": [
|
||||
"A1-U15-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "What's the name, please?",
|
||||
"goal": "说预订名字是陈",
|
||||
"model": "It's under the name Chen.",
|
||||
"zh": "预订名字是陈。",
|
||||
"itemIds": [
|
||||
"A1-U15-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, Mr Chen. A double room for three nights. Please fill in this form.",
|
||||
"goal": "确认后问现在能不能入住",
|
||||
"model": "OK. Can I check in now?",
|
||||
"zh": "好的。我现在可以入住吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, you can. Here is your key. Your room is on floor 8.",
|
||||
"goal": "问电梯在哪里",
|
||||
"model": "Thank you. Where is the elevator?",
|
||||
"zh": "谢谢。电梯在哪里?",
|
||||
"itemIds": [
|
||||
"A1-U15-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "It's over there, beside the bar.",
|
||||
"goal": "问 Wi-Fi 密码和早饭时间",
|
||||
"model": "What's the Wi-Fi password? And what time is breakfast?",
|
||||
"zh": "Wi-Fi 密码是多少?早饭几点?",
|
||||
"itemIds": [
|
||||
"A1-U15-P08",
|
||||
"A1-U15-P09"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-C2",
|
||||
"kind": "variant",
|
||||
"title": "提前到达",
|
||||
"learnerRole": "客人",
|
||||
"aiRole": "前台接待员",
|
||||
"setting": "你早上九点就到了酒店,但入住时间是下午两点。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Good morning! How can I help you?",
|
||||
"goal": "说有预订,问现在能否入住",
|
||||
"model": "I have a reservation. Can I check in now?",
|
||||
"zh": "我有预订。现在可以入住吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P01",
|
||||
"A1-U15-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, check-in is at two pm.",
|
||||
"goal": "请前台先寄存行李",
|
||||
"model": "OK. Can you keep my bags?",
|
||||
"zh": "好的。能帮我寄存行李吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. What's your name?",
|
||||
"goal": "说名字,住两晚",
|
||||
"model": "It's under the name Li. For two nights.",
|
||||
"zh": "名字是李。住两晚。",
|
||||
"itemIds": [
|
||||
"A1-U15-P02",
|
||||
"A1-U15-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thank you. Do you want to pay now?",
|
||||
"goal": "问能不能晚点付",
|
||||
"model": "Can I pay later?",
|
||||
"zh": "我可以晚点付吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P13"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Do you have any questions?",
|
||||
"goal": "问能不能要一间安静的房间",
|
||||
"model": "Yes. Can I have a quiet room?",
|
||||
"zh": "有。能给我一间安静的房间吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P11"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-C3",
|
||||
"kind": "variant",
|
||||
"title": "退房前",
|
||||
"learnerRole": "客人",
|
||||
"aiRole": "前台接待员",
|
||||
"setting": "你明天要走,来前台问退房的事。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. What can I do for you?",
|
||||
"goal": "问几点退房",
|
||||
"model": "What time is check-out?",
|
||||
"zh": "几点退房?",
|
||||
"itemIds": [
|
||||
"A1-U15-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "It's at eleven.",
|
||||
"goal": "说航班很晚,请寄存行李",
|
||||
"model": "My flight is late. Can you keep my bags?",
|
||||
"zh": "我的航班很晚,能帮我寄存行李吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, no problem. Here is a card for your bags.",
|
||||
"goal": "问要不要填表",
|
||||
"model": "Thank you. Do I fill in this form?",
|
||||
"zh": "谢谢。我要填这张表吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, please. Just your name and room.",
|
||||
"goal": "问还要不要钥匙",
|
||||
"model": "OK. Here is my key. Do you want it now?",
|
||||
"zh": "好的,这是我的钥匙。你现在要吗?",
|
||||
"itemIds": [
|
||||
"A1-U15-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Tomorrow is OK. Have a good night!",
|
||||
"goal": "问早饭几点",
|
||||
"model": "Thanks. What time is breakfast?",
|
||||
"zh": "谢谢。早饭几点?",
|
||||
"itemIds": [
|
||||
"A1-U15-P09"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U15-T1",
|
||||
"sceneId": "A1-U15-C1",
|
||||
"goal": "在前台完成入住并问清楚房间信息",
|
||||
"itemIds": [
|
||||
"A1-U15-P01",
|
||||
"A1-U15-P02",
|
||||
"A1-U15-P04",
|
||||
"A1-U15-P10",
|
||||
"A1-U15-P08",
|
||||
"A1-U15-P09",
|
||||
"A1-U15-P07",
|
||||
"A1-U15-P03"
|
||||
],
|
||||
"slots": [
|
||||
"预订",
|
||||
"钥匙",
|
||||
"密码",
|
||||
"早饭"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-T2",
|
||||
"sceneId": "A1-U15-C2",
|
||||
"goal": "提前到达时安排寄存和付款",
|
||||
"itemIds": [
|
||||
"A1-U15-P01",
|
||||
"A1-U15-P04",
|
||||
"A1-U15-P12",
|
||||
"A1-U15-P02",
|
||||
"A1-U15-P03",
|
||||
"A1-U15-P13",
|
||||
"A1-U15-P11"
|
||||
],
|
||||
"slots": [
|
||||
"入住时间",
|
||||
"寄存",
|
||||
"付款"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-T3",
|
||||
"sceneId": "A1-U15-C3",
|
||||
"goal": "退房前问清时间并寄存行李",
|
||||
"itemIds": [
|
||||
"A1-U15-P05",
|
||||
"A1-U15-P12",
|
||||
"A1-U15-P06",
|
||||
"A1-U15-P07",
|
||||
"A1-U15-P09"
|
||||
],
|
||||
"slots": [
|
||||
"退房",
|
||||
"行李"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U15-M1",
|
||||
"mode": "listening",
|
||||
"title": "酒店前台接待",
|
||||
"text": "Good evening, and welcome to our hotel — it's a high, five-star tower by the sea. Do you have a reservation? Can I have your surname and your ID? Your room is a double room on the fifth floor, with a balcony and a nice view of the beach. The elevator is in the hall, near the big clock. Breakfast is from seven to nine. There is a swimming pool, a bar, and a small garden on the hill. One rule: no smoking in the room, please. The Wi-Fi code is on this note. Here is your key. Check-out is at twelve. Enjoy your stay!",
|
||||
"glosses": {
|
||||
"fifth": "第五",
|
||||
"desk": "桌子;front desk 前台",
|
||||
"five-star": "五星级",
|
||||
"key": "钥匙"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话主要在做什么?",
|
||||
"options": [
|
||||
"帮客人办理入住",
|
||||
"点晚餐",
|
||||
"问路"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "早餐几点供应?",
|
||||
"options": [
|
||||
"早上七点到九点",
|
||||
"中午十二点",
|
||||
"晚上七点"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U15-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:办入住",
|
||||
"text": "Guest: Hi, I have a reservation. My surname is Chen, for three nights.\nReceptionist: Let me check. Can I see your ID, please? Yes, one double room for three nights.\nGuest: Can I check in now? Can I have a quiet room with a view of the sea?\nReceptionist: Of course. Room 502 has a balcony and a view of the beach and the tower.\nGuest: Lovely. What's the Wi-Fi code?\nReceptionist: It's on this note. Here is a copy of the hotel rules; there is no smoking inside.\nGuest: OK. Is there a swimming pool?\nReceptionist: Yes, near the bar. What time is check-out?\nGuest: At twelve. Can you keep my bags after that? And where do I put the garbage?\nReceptionist: I'll keep your bags in the hall. The garbage goes in the box by the door.\nGuest: Great, thank you!",
|
||||
"glosses": {
|
||||
"course": "of course 当然",
|
||||
"inside": "里面",
|
||||
"key": "钥匙"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "这位客人订了几晚?",
|
||||
"options": [
|
||||
"三晚",
|
||||
"两晚",
|
||||
"一晚"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "客人想要什么样的房间?",
|
||||
"options": [
|
||||
"安静、能看海的房间",
|
||||
"最便宜的房间",
|
||||
"楼层最低的房间"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "reception",
|
||||
"reason": "B1;A1 用 front desk 以外的说法,直接对 receptionist 说话"
|
||||
},
|
||||
{
|
||||
"en": "deposit",
|
||||
"reason": "B1;押金留到 A2 酒店问题单元"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,955 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U16",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "天气与穿着",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "问天气、说该穿什么",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "239",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "61",
|
||||
"use": "active",
|
||||
"reason": "用 It's raining 描述眼前的天气"
|
||||
},
|
||||
{
|
||||
"id": "69",
|
||||
"use": "active",
|
||||
"reason": "只用 Is it going to rain? 问天气预报,这是出门前必问的一句"
|
||||
},
|
||||
{
|
||||
"id": "117",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "161",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U16-P01",
|
||||
"type": "phrase",
|
||||
"en": "What's the weather like today?",
|
||||
"zh": "今天天气怎么样?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what's the weather like",
|
||||
"what is the weather like"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What's the weather like in Tokyo?",
|
||||
"zh": "东京天气怎么样?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问朋友今天天气怎么样。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P02",
|
||||
"type": "phrase",
|
||||
"en": "It's sunny.",
|
||||
"zh": "天气晴朗。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"sunny",
|
||||
"cloudy",
|
||||
"rainy",
|
||||
"snowy"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's cloudy and cool today.",
|
||||
"zh": "今天多云,挺凉快。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说今天是晴天。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P03",
|
||||
"type": "phrase",
|
||||
"en": "It's raining.",
|
||||
"zh": "正在下雨。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"raining",
|
||||
"snowing"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's raining outside.",
|
||||
"zh": "外面在下雨。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说外面正在下雨。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-W01",
|
||||
"type": "word",
|
||||
"en": "warm",
|
||||
"zh": "暖和的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"warm"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's warm in spring.",
|
||||
"zh": "春天很暖和。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说今天很暖和。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-W02",
|
||||
"type": "word",
|
||||
"en": "cool",
|
||||
"zh": "凉快的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"cool"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The evening is cool.",
|
||||
"zh": "晚上很凉快。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说早上挺凉快。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P04",
|
||||
"type": "phrase",
|
||||
"en": "It's very cold outside.",
|
||||
"zh": "外面很冷。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"cold outside",
|
||||
"hot outside"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's very hot outside today.",
|
||||
"zh": "今天外面很热。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说外面很热。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P05",
|
||||
"type": "phrase",
|
||||
"en": "What's the temperature?",
|
||||
"zh": "气温多少度?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "temperature 为 A2,旅行前查天气时必需",
|
||||
"match": [
|
||||
[
|
||||
"temperature"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What's the temperature in London?",
|
||||
"zh": "伦敦气温多少度?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问现在气温多少度。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P06",
|
||||
"type": "phrase",
|
||||
"en": "Take an umbrella.",
|
||||
"zh": "带把伞。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"take an umbrella",
|
||||
"take your umbrella"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's raining. Take your umbrella.",
|
||||
"zh": "下雨了,带上你的伞。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "提醒朋友带伞。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P07",
|
||||
"type": "phrase",
|
||||
"en": "Put on your coat.",
|
||||
"zh": "穿上外套。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"put on"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Put on your hat. It's cold.",
|
||||
"zh": "戴上帽子,很冷。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "提醒孩子穿上外套。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P08",
|
||||
"type": "phrase",
|
||||
"en": "Take off your jacket.",
|
||||
"zh": "把夹克脱了吧。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"take off"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "It's hot. Take off your coat.",
|
||||
"zh": "很热,把外套脱了吧。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "屋里很热,让朋友脱掉外套。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P09",
|
||||
"type": "phrase",
|
||||
"en": "I like summer.",
|
||||
"zh": "我喜欢夏天。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"summer",
|
||||
"winter",
|
||||
"spring",
|
||||
"autumn"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I don't like winter.",
|
||||
"zh": "我不喜欢冬天。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你最喜欢哪个季节。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P10",
|
||||
"type": "phrase",
|
||||
"en": "Is it going to rain?",
|
||||
"zh": "会下雨吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"going to rain",
|
||||
"going to snow"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is it going to snow tomorrow?",
|
||||
"zh": "明天会下雪吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "出门前问会不会下雨。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-P11",
|
||||
"type": "phrase",
|
||||
"en": "The wind is strong.",
|
||||
"zh": "风很大。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"wind"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The wind is strong today. Take a coat.",
|
||||
"zh": "今天风很大,带件外套。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说今天风很大。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U16-R01",
|
||||
"en": "rainy",
|
||||
"zh": "下雨的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R02",
|
||||
"en": "cloudy",
|
||||
"zh": "多云的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R03",
|
||||
"en": "snowy",
|
||||
"zh": "下雪的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R04",
|
||||
"en": "foggy",
|
||||
"zh": "有雾的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R05",
|
||||
"en": "snow",
|
||||
"zh": "雪;下雪"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R06",
|
||||
"en": "cloud",
|
||||
"zh": "云"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R07",
|
||||
"en": "sun",
|
||||
"zh": "太阳"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R08",
|
||||
"en": "sunshine",
|
||||
"zh": "阳光"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R09",
|
||||
"en": "sky",
|
||||
"zh": "天空"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R10",
|
||||
"en": "moon",
|
||||
"zh": "月亮"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R11",
|
||||
"en": "dry",
|
||||
"zh": "干燥的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R12",
|
||||
"en": "wet",
|
||||
"zh": "湿的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R13",
|
||||
"en": "storm",
|
||||
"zh": "暴风雨"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R14",
|
||||
"en": "degree",
|
||||
"zh": "度"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R15",
|
||||
"en": "windy",
|
||||
"zh": "刮风的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R16",
|
||||
"en": "spring",
|
||||
"zh": "春天"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R17",
|
||||
"en": "autumn",
|
||||
"zh": "秋天"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R18",
|
||||
"en": "winter",
|
||||
"zh": "冬天"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R19",
|
||||
"en": "blow",
|
||||
"zh": "吹"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R20",
|
||||
"en": "scarf",
|
||||
"zh": "围巾"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R21",
|
||||
"en": "sunglasses",
|
||||
"zh": "墨镜"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R22",
|
||||
"en": "tree",
|
||||
"zh": "树"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R23",
|
||||
"en": "flower",
|
||||
"zh": "花"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R24",
|
||||
"en": "bird",
|
||||
"zh": "鸟"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R25",
|
||||
"en": "grow",
|
||||
"zh": "生长"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R26",
|
||||
"en": "jump",
|
||||
"zh": "跳"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R27",
|
||||
"en": "terrible",
|
||||
"zh": "糟糕的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R28",
|
||||
"en": "wonderful",
|
||||
"zh": "极好的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R29",
|
||||
"en": "bright",
|
||||
"zh": "明亮的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R30",
|
||||
"en": "idea",
|
||||
"zh": "主意"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-R31",
|
||||
"en": "really",
|
||||
"zh": "真的"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U16-S1",
|
||||
"title": "今天天气怎么样",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U16-P01",
|
||||
"A1-U16-P02",
|
||||
"A1-U16-P03",
|
||||
"A1-U16-P05",
|
||||
"A1-U16-P04"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What's the weather like today? It's sunny in the morning, but it's cloudy in the afternoon. What's the temperature? Twenty-five degrees.",
|
||||
"question": "下午天气怎么样?",
|
||||
"options": [
|
||||
"多云",
|
||||
"晴天",
|
||||
"下雨"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What's the weather like today?"
|
||||
},
|
||||
{
|
||||
"text": "It's sunny."
|
||||
},
|
||||
{
|
||||
"text": "It's raining.",
|
||||
"tip": "raining 的 ai 读 /eɪ/"
|
||||
},
|
||||
{
|
||||
"text": "What's the temperature?"
|
||||
},
|
||||
{
|
||||
"text": "It's very cold outside."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Weather in five cities\nLondon: rainy, 12 degrees\nBeijing: sunny and dry, 30 degrees\nTokyo: cloudy and windy, 18 degrees\nSydney: foggy in the morning, then sunshine, 22 degrees\nSeoul: snowy, -3 degrees. There is ice on the roads!",
|
||||
"question": "哪个城市在下雪?",
|
||||
"options": [
|
||||
"首尔",
|
||||
"伦敦",
|
||||
"东京"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问今天天气怎么样,再说外面在下雨。",
|
||||
"example": "What's the weather like today? It's raining.",
|
||||
"itemIds": [
|
||||
"A1-U16-P01",
|
||||
"A1-U16-P03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "看看窗外,用英语说说今天的天气和气温。",
|
||||
"itemIds": [
|
||||
"A1-U16-P02",
|
||||
"A1-U16-P04",
|
||||
"A1-U16-P05"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-S2",
|
||||
"title": "出门穿什么",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U16-W01",
|
||||
"A1-U16-W02",
|
||||
"A1-U16-P06",
|
||||
"A1-U16-P07",
|
||||
"A1-U16-P08",
|
||||
"A1-U16-P11"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Is it cold? No, it's warm today. Take off your jacket. But take an umbrella. It's cloudy. And in the evening it's cool, so put on a coat.",
|
||||
"question": "晚上天气怎么样?",
|
||||
"options": [
|
||||
"凉快",
|
||||
"暖和",
|
||||
"很热"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "It's warm today."
|
||||
},
|
||||
{
|
||||
"text": "The evening is cool."
|
||||
},
|
||||
{
|
||||
"text": "Take an umbrella."
|
||||
},
|
||||
{
|
||||
"text": "Put on your coat."
|
||||
},
|
||||
{
|
||||
"text": "Take off your jacket."
|
||||
},
|
||||
{
|
||||
"text": "The wind is strong."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "What to wear\nSunny and hot: wear shorts, a T-shirt and sunglasses. Take water too.\nRainy and wet: take an umbrella and wear a coat.\nCold and snowy: put on a warm coat, a hat and a scarf.\nWindy: the wind can blow your hat away!",
|
||||
"question": "刮风时要注意什么?",
|
||||
"options": [
|
||||
"帽子会被吹走",
|
||||
"要戴墨镜",
|
||||
"要穿短裤"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:提醒朋友带伞、穿上外套。",
|
||||
"example": "Take an umbrella. Put on your coat.",
|
||||
"itemIds": [
|
||||
"A1-U16-P06",
|
||||
"A1-U16-P07"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "朋友要出门,今天风很大很冷,用英语提醒他穿什么。",
|
||||
"itemIds": [
|
||||
"A1-U16-P11",
|
||||
"A1-U16-P07",
|
||||
"A1-U16-P04"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-S3",
|
||||
"title": "季节和天气预报",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U16-P09",
|
||||
"A1-U16-P10"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Is it going to rain tomorrow? No, it's going to be sunny. Great! I like summer because I can swim every day.",
|
||||
"question": "说话人为什么喜欢夏天?",
|
||||
"options": [
|
||||
"可以每天游泳",
|
||||
"天气凉快",
|
||||
"可以看雪"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I like summer."
|
||||
},
|
||||
{
|
||||
"text": "Is it going to rain?"
|
||||
},
|
||||
{
|
||||
"text": "I don't like winter. It's very cold."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Spring, summer, autumn and winter in my city\nSpring is warm. Trees and flowers grow, and birds sing.\nSummer is very hot. The sun is bright, and there are no clouds in the blue sky.\nAutumn is cool and dry. I love it more than summer.\nWinter is terrible: there are storms and it snows. But children jump in the snow and look at the moon at night. It's wonderful for them!",
|
||||
"question": "作者更喜欢哪个季节?",
|
||||
"options": [
|
||||
"秋天",
|
||||
"夏天",
|
||||
"冬天"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你喜欢哪个季节,再问明天会不会下雨。",
|
||||
"example": "I like autumn. Is it going to rain tomorrow?",
|
||||
"itemIds": [
|
||||
"A1-U16-P09",
|
||||
"A1-U16-P10"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你最喜欢的季节和那时的天气。",
|
||||
"itemIds": [
|
||||
"A1-U16-P09",
|
||||
"A1-U16-P02",
|
||||
"A1-U16-W01"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U16-C1",
|
||||
"kind": "main",
|
||||
"title": "出门前问天气",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "酒店前台",
|
||||
"setting": "你住在酒店,今天要出去玩,先问问前台天气。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Good morning! Going out today?",
|
||||
"goal": "问今天天气怎么样",
|
||||
"model": "Yes. What's the weather like today?",
|
||||
"zh": "是的。今天天气怎么样?",
|
||||
"itemIds": [
|
||||
"A1-U16-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "It's sunny now, but it's cloudy later.",
|
||||
"goal": "问气温多少度",
|
||||
"model": "What's the temperature?",
|
||||
"zh": "气温多少度?",
|
||||
"itemIds": [
|
||||
"A1-U16-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "About twenty degrees. It's warm.",
|
||||
"goal": "问会不会下雨",
|
||||
"model": "Is it going to rain?",
|
||||
"zh": "会下雨吗?",
|
||||
"itemIds": [
|
||||
"A1-U16-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Maybe in the evening.",
|
||||
"goal": "说那你带上伞",
|
||||
"model": "OK. I can take an umbrella.",
|
||||
"zh": "好的,我带把伞。",
|
||||
"itemIds": [
|
||||
"A1-U16-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good idea. And the wind is strong by the sea.",
|
||||
"goal": "说你穿上外套",
|
||||
"model": "Thanks. I can put on my coat.",
|
||||
"zh": "谢谢,我穿上外套。",
|
||||
"itemIds": [
|
||||
"A1-U16-P07"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-C2",
|
||||
"kind": "variant",
|
||||
"title": "和朋友视频聊天气",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "在外国的朋友",
|
||||
"setting": "你和住在国外的朋友视频,互相问天气。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! What's the weather like there?",
|
||||
"goal": "说外面正在下雨,很冷",
|
||||
"model": "It's raining. It's very cold outside.",
|
||||
"zh": "在下雨,外面很冷。",
|
||||
"itemIds": [
|
||||
"A1-U16-P03",
|
||||
"A1-U16-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh no! Here it's sunny and hot.",
|
||||
"goal": "问那边气温多少度",
|
||||
"model": "What's the temperature?",
|
||||
"zh": "气温多少度?",
|
||||
"itemIds": [
|
||||
"A1-U16-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Thirty-two degrees! I like summer.",
|
||||
"goal": "说你喜欢冬天,不喜欢夏天",
|
||||
"model": "I don't like summer. I like winter. It's cool.",
|
||||
"zh": "我不喜欢夏天,我喜欢冬天,很凉快。",
|
||||
"itemIds": [
|
||||
"A1-U16-P09",
|
||||
"A1-U16-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Really? Is it going to snow there?",
|
||||
"goal": "说风很大,但是不会下雪",
|
||||
"model": "No, but the wind is strong.",
|
||||
"zh": "不会,但是风很大。",
|
||||
"itemIds": [
|
||||
"A1-U16-P11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Put on your coat, then!",
|
||||
"goal": "让朋友脱掉外套",
|
||||
"model": "And you, take off your jacket!",
|
||||
"zh": "你呢,把夹克脱了吧!",
|
||||
"itemIds": [
|
||||
"A1-U16-P08"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-C3",
|
||||
"kind": "variant",
|
||||
"title": "带孩子出门",
|
||||
"learnerRole": "家长",
|
||||
"aiRole": "孩子",
|
||||
"setting": "你要带孩子去公园,孩子不想多穿衣服。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Can we go to the park now?",
|
||||
"goal": "说外面很冷",
|
||||
"model": "Yes, but it's very cold outside.",
|
||||
"zh": "可以,但是外面很冷。",
|
||||
"itemIds": [
|
||||
"A1-U16-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I don't want my coat!",
|
||||
"goal": "让孩子穿上外套",
|
||||
"model": "Put on your coat, please.",
|
||||
"zh": "请穿上外套。",
|
||||
"itemIds": [
|
||||
"A1-U16-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK... Is it going to rain?",
|
||||
"goal": "说会下雨,带把伞",
|
||||
"model": "Yes, it's going to rain. Take an umbrella.",
|
||||
"zh": "会下雨,带把伞。",
|
||||
"itemIds": [
|
||||
"A1-U16-P10",
|
||||
"A1-U16-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Look! It's sunny now!",
|
||||
"goal": "说现在暖和了",
|
||||
"model": "Yes, it's sunny and warm now.",
|
||||
"zh": "是啊,现在出太阳了,很暖和。",
|
||||
"itemIds": [
|
||||
"A1-U16-W01",
|
||||
"A1-U16-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I'm hot!",
|
||||
"goal": "让孩子把夹克脱了",
|
||||
"model": "OK, take off your jacket.",
|
||||
"zh": "好,把夹克脱了吧。",
|
||||
"itemIds": [
|
||||
"A1-U16-P08"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U16-T1",
|
||||
"sceneId": "A1-U16-C1",
|
||||
"goal": "出门前问清天气并决定带什么",
|
||||
"itemIds": [
|
||||
"A1-U16-P01",
|
||||
"A1-U16-P05",
|
||||
"A1-U16-P10",
|
||||
"A1-U16-P06",
|
||||
"A1-U16-P07"
|
||||
],
|
||||
"slots": [
|
||||
"天气",
|
||||
"气温",
|
||||
"要带的东西"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-T2",
|
||||
"sceneId": "A1-U16-C2",
|
||||
"goal": "和朋友比较两地的天气和季节",
|
||||
"itemIds": [
|
||||
"A1-U16-P03",
|
||||
"A1-U16-P04",
|
||||
"A1-U16-P05",
|
||||
"A1-U16-P09",
|
||||
"A1-U16-P11",
|
||||
"A1-U16-P08",
|
||||
"A1-U16-W02"
|
||||
],
|
||||
"slots": [
|
||||
"天气",
|
||||
"气温",
|
||||
"喜欢的季节"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-T3",
|
||||
"sceneId": "A1-U16-C3",
|
||||
"goal": "根据天气提醒孩子穿什么",
|
||||
"itemIds": [
|
||||
"A1-U16-P04",
|
||||
"A1-U16-P07",
|
||||
"A1-U16-P10",
|
||||
"A1-U16-P06",
|
||||
"A1-U16-W01",
|
||||
"A1-U16-P02",
|
||||
"A1-U16-P08"
|
||||
],
|
||||
"slots": [
|
||||
"冷热",
|
||||
"下雨",
|
||||
"穿脱"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U16-M1",
|
||||
"mode": "listening",
|
||||
"title": "天气预报",
|
||||
"text": "Good morning! Here is today's weather. In spring the sky is bright and blue, with a warm sun and sunshine. This morning it is dry, about twenty degrees; take your sunglasses. In the afternoon clouds will grow, the wind will blow strong, and it will be a windy, wet day — it is going to rain. Take an umbrella. Tonight a storm will come, so it will be cold; put on your coat and a scarf. Tomorrow the rainy sky will go away and it will be sunny again. The trees, flowers and birds love the sun. Have a wonderful day!",
|
||||
"glosses": {
|
||||
"blue": "蓝色的",
|
||||
"away": "go away 离开、消散"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这是什么内容?",
|
||||
"options": [
|
||||
"今天的天气预报",
|
||||
"一条新闻",
|
||||
"一个广告"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "下午天气怎么样?",
|
||||
"options": [
|
||||
"刮大风,要下雨",
|
||||
"一直是晴天",
|
||||
"下雪"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U16-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:聊天气和出行",
|
||||
"text": "Mia: What's the weather like today? I want to go to the park and see the flowers and birds.\nBen: The sky is cloudy now, and it's wet after the rain. It's not a bright day.\nMia: Is a storm coming? Should I take an umbrella?\nBen: Yes. The wind will blow hard — it's windy and cold. Put on a coat and a scarf.\nMia: What a terrible day! What about tomorrow?\nBen: Tomorrow the sun will grow bright and the sky will be blue and dry. A wonderful day for the beach!\nMia: Great idea! In summer I love the sunshine, the sea and the trees.\nBen: Me too. Let's go early, before the clouds come back.\nMia: Really? Then let's jump on the first bus. Perfect!",
|
||||
"glosses": {
|
||||
"perfect": "完美的",
|
||||
"blue": "蓝色的",
|
||||
"hard": "用力地"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Ben 建议 Mia 今天带什么?",
|
||||
"options": [
|
||||
"雨伞、大衣和围巾",
|
||||
"相机",
|
||||
"什么都不用带"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "明天天气怎么样?",
|
||||
"options": [
|
||||
"晴朗干燥",
|
||||
"下雨",
|
||||
"刮大风"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "season",
|
||||
"reason": "B1;A1 直接说 spring、summer、autumn、winter"
|
||||
},
|
||||
{
|
||||
"en": "forecast",
|
||||
"reason": "B1;A1 用 Is it going to rain? 表达"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,911 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U17",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "酒店里的需求",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "要物品、报告简单问题",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "16",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "59-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "250",
|
||||
"use": "active",
|
||||
"reason": "CEFR-J 未标级别,Could you ...? 是向酒店提要求时最礼貌的说法"
|
||||
},
|
||||
{
|
||||
"id": "117",
|
||||
"use": "active"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U17-P01",
|
||||
"type": "phrase",
|
||||
"en": "There's no hot water.",
|
||||
"zh": "没有热水。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"no hot water"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "There's no hot water in the shower.",
|
||||
"zh": "淋浴没有热水。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉前台房间没有热水。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P02",
|
||||
"type": "phrase",
|
||||
"en": "The TV doesn't work.",
|
||||
"zh": "电视坏了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"doesn't work",
|
||||
"don't work"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The light doesn't work.",
|
||||
"zh": "灯不亮。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说房间的灯不亮。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-W01",
|
||||
"type": "word",
|
||||
"en": "broken",
|
||||
"zh": "坏的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"broken"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The chair is broken.",
|
||||
"zh": "椅子坏了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说房间的椅子坏了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-W02",
|
||||
"type": "word",
|
||||
"en": "dirty",
|
||||
"zh": "脏的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"dirty"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "The towels are dirty.",
|
||||
"zh": "毛巾是脏的。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说床单是脏的。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P03",
|
||||
"type": "phrase",
|
||||
"en": "Can I have another towel?",
|
||||
"zh": "可以再给我一条毛巾吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"another towel",
|
||||
"more towels"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I have two more towels?",
|
||||
"zh": "可以再给我两条毛巾吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "再要一条毛巾。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P04",
|
||||
"type": "phrase",
|
||||
"en": "It's too noisy.",
|
||||
"zh": "太吵了。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "noisy 为 A2,投诉房间时离不开这个词",
|
||||
"match": [
|
||||
[
|
||||
"noisy"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My room is too noisy.",
|
||||
"zh": "我的房间太吵了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说房间太吵,睡不着。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P05",
|
||||
"type": "phrase",
|
||||
"en": "Can you send someone?",
|
||||
"zh": "能派个人来吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "send 为 A2,请酒店派人处理问题时必需",
|
||||
"match": [
|
||||
[
|
||||
"send someone"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can you send someone to my room?",
|
||||
"zh": "能派个人来我房间吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请前台派人来修。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P06",
|
||||
"type": "phrase",
|
||||
"en": "Can I change rooms?",
|
||||
"zh": "我能换个房间吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"change rooms",
|
||||
"another room"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I have another room?",
|
||||
"zh": "能给我换一间房吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问能不能换房间。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P07",
|
||||
"type": "phrase",
|
||||
"en": "Could you bring me some water?",
|
||||
"zh": "能给我拿点水吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"bring me"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Could you bring me a blanket?",
|
||||
"zh": "能给我拿条毛毯吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请服务员给你拿点水。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P08",
|
||||
"type": "phrase",
|
||||
"en": "My room number is 1206.",
|
||||
"zh": "我的房间号是 1206。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"room number"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What's your room number?",
|
||||
"zh": "你的房间号是多少?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "打电话给前台时先说房间号。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P09",
|
||||
"type": "phrase",
|
||||
"en": "I can't open the door.",
|
||||
"zh": "我打不开门。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"can't open"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I can't open the window.",
|
||||
"zh": "我打不开窗。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你打不开房门。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P10",
|
||||
"type": "phrase",
|
||||
"en": "Please clean my room.",
|
||||
"zh": "请打扫我的房间。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"clean my room",
|
||||
"clean the room"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Please clean the room at ten.",
|
||||
"zh": "请十点打扫房间。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请酒店打扫你的房间。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-P11",
|
||||
"type": "phrase",
|
||||
"en": "Thank you for your help.",
|
||||
"zh": "谢谢你的帮助。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"thank you for",
|
||||
"thanks for"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Thanks for the towels.",
|
||||
"zh": "谢谢你送的毛巾。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问题解决后道谢。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U17-R01",
|
||||
"en": "noise",
|
||||
"zh": "噪音"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R02",
|
||||
"en": "paper",
|
||||
"zh": "纸"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R03",
|
||||
"en": "wall",
|
||||
"zh": "墙"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R04",
|
||||
"en": "desk",
|
||||
"zh": "书桌"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R05",
|
||||
"en": "chair",
|
||||
"zh": "椅子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R06",
|
||||
"en": "couch",
|
||||
"zh": "沙发"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R07",
|
||||
"en": "blanket",
|
||||
"zh": "毛毯"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R08",
|
||||
"en": "shampoo",
|
||||
"zh": "洗发水"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R09",
|
||||
"en": "lamp",
|
||||
"zh": "台灯"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R10",
|
||||
"en": "repair",
|
||||
"zh": "修理"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R11",
|
||||
"en": "moment",
|
||||
"zh": "片刻"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R12",
|
||||
"en": "give",
|
||||
"zh": "给"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R13",
|
||||
"en": "break",
|
||||
"zh": "弄坏"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R14",
|
||||
"en": "hole",
|
||||
"zh": "洞"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R15",
|
||||
"en": "hear",
|
||||
"zh": "听见"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R16",
|
||||
"en": "matter",
|
||||
"zh": "事情(What's the matter?)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R17",
|
||||
"en": "happen",
|
||||
"zh": "发生"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R18",
|
||||
"en": "lock",
|
||||
"zh": "锁"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R19",
|
||||
"en": "extra",
|
||||
"zh": "额外的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R20",
|
||||
"en": "rude",
|
||||
"zh": "粗鲁的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R21",
|
||||
"en": "strange",
|
||||
"zh": "奇怪的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R22",
|
||||
"en": "awake",
|
||||
"zh": "醒着的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R23",
|
||||
"en": "hard",
|
||||
"zh": "硬的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R24",
|
||||
"en": "bad",
|
||||
"zh": "坏的;糟糕的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-R25",
|
||||
"en": "call",
|
||||
"zh": "打电话"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U17-S1",
|
||||
"title": "房间里的问题",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U17-P01",
|
||||
"A1-U17-P02",
|
||||
"A1-U17-W01",
|
||||
"A1-U17-W02",
|
||||
"A1-U17-P08"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hello, this is room 1206. There's no hot water. And the TV doesn't work. OK, sir. What's your room number again? My room number is 1206.",
|
||||
"question": "客人房间有什么问题?",
|
||||
"options": [
|
||||
"没有热水,电视坏了",
|
||||
"太吵,门打不开",
|
||||
"毛巾脏了"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "There's no hot water."
|
||||
},
|
||||
{
|
||||
"text": "The TV doesn't work."
|
||||
},
|
||||
{
|
||||
"text": "The chair is broken.",
|
||||
"tip": "broken 读 /ˈbrəʊkən/"
|
||||
},
|
||||
{
|
||||
"text": "The towels are dirty."
|
||||
},
|
||||
{
|
||||
"text": "My room number is 1206."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Room problems\nThe lamp on the desk doesn't work.\nThe couch is dirty.\nThere's a hole in the wall.\nThe shower has no hot water.\nThere's no toilet paper and no shampoo.\nCall 0 and give us your room number. We can repair everything soon!",
|
||||
"question": "客人发现问题时应该怎么做?",
|
||||
"options": [
|
||||
"拨 0 并说房间号",
|
||||
"自己修理",
|
||||
"去前台排队"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说出房间号,再说没有热水。",
|
||||
"example": "My room number is 1206. There's no hot water.",
|
||||
"itemIds": [
|
||||
"A1-U17-P08",
|
||||
"A1-U17-P01"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语打电话给前台,说你房间的两个问题。",
|
||||
"itemIds": [
|
||||
"A1-U17-P08",
|
||||
"A1-U17-P02",
|
||||
"A1-U17-W01",
|
||||
"A1-U17-W02"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-S2",
|
||||
"title": "请求帮助",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U17-P03",
|
||||
"A1-U17-P05",
|
||||
"A1-U17-P07",
|
||||
"A1-U17-P10",
|
||||
"A1-U17-P11"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hi, can I have another towel? And could you bring me some water? Sure. Anything else? Can you send someone? The light is broken. Yes, in a moment. Thank you for your help.",
|
||||
"question": "客人要了哪两样东西?",
|
||||
"options": [
|
||||
"毛巾和水",
|
||||
"毛毯和水",
|
||||
"毛巾和洗发水"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Can I have another towel?"
|
||||
},
|
||||
{
|
||||
"text": "Can you send someone?"
|
||||
},
|
||||
{
|
||||
"text": "Could you bring me some water?",
|
||||
"tip": "Could you 常连读成 /kʊdʒu/"
|
||||
},
|
||||
{
|
||||
"text": "Please clean my room."
|
||||
},
|
||||
{
|
||||
"text": "Thank you for your help."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Hotel help\nNeed something extra? Call 0.\n· Towels, blankets and shampoo: we can bring them to your room.\n· Something is broken? We can send someone.\n· Please clean my room: put this card on your door.\n· Don't break the lock! If you can't open the door, call us.",
|
||||
"question": "想让酒店打扫房间,应该怎么做?",
|
||||
"options": [
|
||||
"把卡挂在门上",
|
||||
"拨 0",
|
||||
"去前台"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:再要一条毛巾,再请人拿点水。",
|
||||
"example": "Can I have another towel? Could you bring me some water?",
|
||||
"itemIds": [
|
||||
"A1-U17-P03",
|
||||
"A1-U17-P07"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语请酒店派人来修电视,最后道谢。",
|
||||
"itemIds": [
|
||||
"A1-U17-P05",
|
||||
"A1-U17-P02",
|
||||
"A1-U17-P11"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-S3",
|
||||
"title": "换房间",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U17-P04",
|
||||
"A1-U17-P06",
|
||||
"A1-U17-P09"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What's the matter? It's too noisy. I can hear music from the next room. Can I change rooms? Sure. Here is the key to room 1508. ... Hello? I can't open the door.",
|
||||
"question": "客人为什么要换房间?",
|
||||
"options": [
|
||||
"太吵了",
|
||||
"门打不开",
|
||||
"房间太脏"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "It's too noisy."
|
||||
},
|
||||
{
|
||||
"text": "Can I change rooms?"
|
||||
},
|
||||
{
|
||||
"text": "I can't open the door."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "A bad night\nWhat happened? I can't sleep. My room is too noisy. There's a strange noise from the wall, and I'm awake at 3 am. The bed is very hard too. I call the front desk, but the man is rude. He says, \"No, you can't change rooms.\" Tomorrow I can ask again.",
|
||||
"question": "写这段话的人为什么睡不着?",
|
||||
"options": [
|
||||
"房间太吵,床也太硬",
|
||||
"房间太冷",
|
||||
"灯坏了"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说太吵了,问能不能换房间。",
|
||||
"example": "It's too noisy. Can I change rooms?",
|
||||
"itemIds": [
|
||||
"A1-U17-P04",
|
||||
"A1-U17-P06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你拿到新房卡却打不开门,用英语向前台说明。",
|
||||
"itemIds": [
|
||||
"A1-U17-P09",
|
||||
"A1-U17-P08",
|
||||
"A1-U17-P05"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U17-C1",
|
||||
"kind": "main",
|
||||
"title": "打电话给前台",
|
||||
"learnerRole": "客人",
|
||||
"aiRole": "前台",
|
||||
"setting": "你刚入住,发现房间有几个问题,打电话给前台。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Front desk. How can I help you?",
|
||||
"goal": "先说房间号",
|
||||
"model": "Hello. My room number is 1206.",
|
||||
"zh": "你好,我的房间号是 1206。",
|
||||
"itemIds": [
|
||||
"A1-U17-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, Mr Chen. What's the matter?",
|
||||
"goal": "说没有热水",
|
||||
"model": "There's no hot water.",
|
||||
"zh": "没有热水。",
|
||||
"itemIds": [
|
||||
"A1-U17-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I'm sorry. Anything else?",
|
||||
"goal": "说电视也坏了",
|
||||
"model": "And the TV doesn't work.",
|
||||
"zh": "电视也坏了。",
|
||||
"itemIds": [
|
||||
"A1-U17-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. What can we do for you?",
|
||||
"goal": "请前台派人来",
|
||||
"model": "Can you send someone?",
|
||||
"zh": "能派个人来吗?",
|
||||
"itemIds": [
|
||||
"A1-U17-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, in ten minutes.",
|
||||
"goal": "道谢",
|
||||
"model": "Thank you for your help.",
|
||||
"zh": "谢谢你的帮助。",
|
||||
"itemIds": [
|
||||
"A1-U17-P11"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-C2",
|
||||
"kind": "variant",
|
||||
"title": "房间太吵要换房",
|
||||
"learnerRole": "客人",
|
||||
"aiRole": "前台",
|
||||
"setting": "半夜房间很吵,你到前台要求换房间。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Good evening. Can I help you?",
|
||||
"goal": "说房间太吵",
|
||||
"model": "Yes. My room is too noisy.",
|
||||
"zh": "是的,我的房间太吵了。",
|
||||
"itemIds": [
|
||||
"A1-U17-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh, I'm sorry. Is it the music?",
|
||||
"goal": "问能不能换房间",
|
||||
"model": "Yes. Can I change rooms?",
|
||||
"zh": "是的。我能换个房间吗?",
|
||||
"itemIds": [
|
||||
"A1-U17-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. Here is the key to room 815.",
|
||||
"goal": "说房间也有点脏",
|
||||
"model": "Thank you. And my room is a little dirty.",
|
||||
"zh": "谢谢。我的房间也有点脏。",
|
||||
"itemIds": [
|
||||
"A1-U17-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry about that. We can clean it tomorrow.",
|
||||
"goal": "请明天早上打扫房间",
|
||||
"model": "OK. Please clean my room in the morning.",
|
||||
"zh": "好的。请早上打扫我的房间。",
|
||||
"itemIds": [
|
||||
"A1-U17-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. Anything else?",
|
||||
"goal": "请给你拿点水",
|
||||
"model": "Could you bring me some water?",
|
||||
"zh": "能给我拿点水吗?",
|
||||
"itemIds": [
|
||||
"A1-U17-P07"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-C3",
|
||||
"kind": "variant",
|
||||
"title": "进不去房间",
|
||||
"learnerRole": "客人",
|
||||
"aiRole": "前台",
|
||||
"setting": "你回到酒店,房门打不开,房间里的东西也有问题。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello again! Is everything OK?",
|
||||
"goal": "说你打不开门",
|
||||
"model": "No. I can't open the door.",
|
||||
"zh": "不好。我打不开门。",
|
||||
"itemIds": [
|
||||
"A1-U17-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh, the key card is broken. Here is a new one.",
|
||||
"goal": "再要一条毛巾",
|
||||
"model": "Thanks. Can I have another towel?",
|
||||
"zh": "谢谢。可以再给我一条毛巾吗?",
|
||||
"itemIds": [
|
||||
"A1-U17-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. Anything else?",
|
||||
"goal": "说椅子坏了",
|
||||
"model": "The chair in my room is broken.",
|
||||
"zh": "我房间的椅子坏了。",
|
||||
"itemIds": [
|
||||
"A1-U17-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I can send someone. What's your room number?",
|
||||
"goal": "说房间号",
|
||||
"model": "My room number is 815.",
|
||||
"zh": "我的房间号是 815。",
|
||||
"itemIds": [
|
||||
"A1-U17-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK, in a moment.",
|
||||
"goal": "道谢",
|
||||
"model": "Thank you for your help.",
|
||||
"zh": "谢谢你的帮助。",
|
||||
"itemIds": [
|
||||
"A1-U17-P11"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U17-T1",
|
||||
"sceneId": "A1-U17-C1",
|
||||
"goal": "打电话给前台说明房间问题并请人来修",
|
||||
"itemIds": [
|
||||
"A1-U17-P08",
|
||||
"A1-U17-P01",
|
||||
"A1-U17-P02",
|
||||
"A1-U17-P05",
|
||||
"A1-U17-P11"
|
||||
],
|
||||
"slots": [
|
||||
"房间号",
|
||||
"问题",
|
||||
"派人"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-T2",
|
||||
"sceneId": "A1-U17-C2",
|
||||
"goal": "当面投诉房间太吵并换房",
|
||||
"itemIds": [
|
||||
"A1-U17-P04",
|
||||
"A1-U17-P06",
|
||||
"A1-U17-W02",
|
||||
"A1-U17-P10",
|
||||
"A1-U17-P07"
|
||||
],
|
||||
"slots": [
|
||||
"问题",
|
||||
"换房",
|
||||
"其他请求"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-T3",
|
||||
"sceneId": "A1-U17-C3",
|
||||
"goal": "进不去房间时处理多个小问题",
|
||||
"itemIds": [
|
||||
"A1-U17-P09",
|
||||
"A1-U17-P03",
|
||||
"A1-U17-W01",
|
||||
"A1-U17-P08",
|
||||
"A1-U17-P11"
|
||||
],
|
||||
"slots": [
|
||||
"房门",
|
||||
"毛巾",
|
||||
"损坏"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U17-M1",
|
||||
"mode": "listening",
|
||||
"title": "打给前台报房间问题",
|
||||
"text": "Hello, front desk? This is room 1206. I'm sorry, but there are some problems, and I can't sleep. There's no hot water, and the TV is broken; it doesn't work. The lamp on the desk is broken too. The room is a little dirty, and there is a hole in the wall. It's also very noisy — I can hear a strange noise all night, so I'm still awake. Can you send someone to repair these things and clean the room? Could you bring me another blanket, some shampoo and a new chair? Can I change rooms? One moment — sorry, that's a lot. Thank you for your help.",
|
||||
"glosses": {
|
||||
"send": "派(人)",
|
||||
"still": "仍然",
|
||||
"things": "东西"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "客人打电话主要是为了什么?",
|
||||
"options": [
|
||||
"报告房间的问题",
|
||||
"订早餐",
|
||||
"问路"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "下面哪个是客人说的问题?",
|
||||
"options": [
|
||||
"没有热水,电视坏了",
|
||||
"床太小",
|
||||
"房间太贵"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U17-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:报修",
|
||||
"text": "Guest: Hi, my room number is 305. The TV doesn't work, and there's no hot water.\nAnna: I'm so sorry. What's the matter? I'll send someone to repair it now.\nGuest: Thank you. There's also a strange noise in the wall. What happened? I can't sleep; I'm still awake.\nAnna: Sorry for the trouble. Can I bring you anything?\nGuest: Yes, please. Can I have another blanket, some shampoo, and paper? And the lamp is broken.\nAnna: Of course. We'll give you a new lamp and repair the door lock too.\nGuest: Thank you. The couch has a hole in it, and the chair is broken. Sorry, that's a lot!\nAnna: No problem. Some guests are rude, but you are very kind. I'll fix everything today.\nGuest: One moment — could you bring an extra towel too?\nAnna: Sure, I'll bring it now.\nGuest: Thank you for your help.",
|
||||
"glosses": {
|
||||
"fix": "修理,解决",
|
||||
"trouble": "麻烦",
|
||||
"course": "of course 当然",
|
||||
"still": "仍然"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "客人房间里什么坏了?",
|
||||
"options": [
|
||||
"电视和台灯",
|
||||
"空调",
|
||||
"窗户"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "客人还额外要了什么?",
|
||||
"options": [
|
||||
"一条毛巾",
|
||||
"一双鞋",
|
||||
"一份报纸"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "pillow",
|
||||
"reason": "B1;A1 用 blanket、towel 这类词"
|
||||
},
|
||||
{
|
||||
"en": "heater",
|
||||
"reason": "B1;A1 用 no hot water / it's cold 表达"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,936 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U18",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "药店与不舒服",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说一个症状、听懂基本建议",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "59-2",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "60",
|
||||
"use": "active",
|
||||
"reason": "用 My head hurts 说哪里疼"
|
||||
},
|
||||
{
|
||||
"id": "246",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "64",
|
||||
"use": "receptive"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U18-P01",
|
||||
"type": "phrase",
|
||||
"en": "I don't feel well.",
|
||||
"zh": "我不舒服。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"don't feel well"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I don't feel well today.",
|
||||
"zh": "我今天不舒服。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉朋友你不舒服。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P02",
|
||||
"type": "phrase",
|
||||
"en": "I have a headache.",
|
||||
"zh": "我头疼。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"have a headache",
|
||||
"have a fever",
|
||||
"have a cold",
|
||||
"have a stomachache",
|
||||
"has a headache",
|
||||
"has a fever",
|
||||
"has a cold"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I have a cold.",
|
||||
"zh": "我感冒了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你发烧了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P03",
|
||||
"type": "phrase",
|
||||
"en": "My head hurts.",
|
||||
"zh": "我头疼。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"hurts",
|
||||
"hurt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My back hurts.",
|
||||
"zh": "我背疼。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的腿疼。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-W01",
|
||||
"type": "word",
|
||||
"en": "sick",
|
||||
"zh": "生病的",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"sick"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I feel sick.",
|
||||
"zh": "我觉得恶心/不舒服。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的孩子生病了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-W02",
|
||||
"type": "word",
|
||||
"en": "medicine",
|
||||
"zh": "药",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"medicine"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I need some medicine.",
|
||||
"zh": "我需要一些药。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你需要买药。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P04",
|
||||
"type": "phrase",
|
||||
"en": "Is there a pharmacy near here?",
|
||||
"zh": "附近有药店吗?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "pharmacy 为 B1,但旅行中找药店时必需",
|
||||
"match": [
|
||||
[
|
||||
"pharmacy"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Where is the pharmacy?",
|
||||
"zh": "药店在哪里?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在街上问附近有没有药店。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P05",
|
||||
"type": "phrase",
|
||||
"en": "Do you have something for a headache?",
|
||||
"zh": "有治头疼的药吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"something for"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you have something for a cold?",
|
||||
"zh": "有治感冒的药吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "在药店问有没有治发烧的药。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P06",
|
||||
"type": "phrase",
|
||||
"en": "How often do I take it?",
|
||||
"zh": "这个药多久吃一次?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"how often"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "How often do I take this medicine?",
|
||||
"zh": "这个药多久吃一次?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问药怎么吃。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P07",
|
||||
"type": "phrase",
|
||||
"en": "Three times a day.",
|
||||
"zh": "一天三次。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"times a day",
|
||||
"once a day",
|
||||
"twice a day"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Take it two times a day.",
|
||||
"zh": "一天吃两次。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说一天吃两次。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P08",
|
||||
"type": "phrase",
|
||||
"en": "after meals",
|
||||
"zh": "饭后",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"after meals",
|
||||
"before meals"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Take it after meals.",
|
||||
"zh": "饭后吃。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说这个药饭前吃。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P09",
|
||||
"type": "phrase",
|
||||
"en": "I need to see a doctor.",
|
||||
"zh": "我需要看医生。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"see a doctor"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I see a doctor today?",
|
||||
"zh": "我今天能看医生吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你需要看医生。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P10",
|
||||
"type": "phrase",
|
||||
"en": "I feel better now.",
|
||||
"zh": "我现在好多了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"feel better"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you feel better?",
|
||||
"zh": "你好点了吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉朋友你好多了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-P11",
|
||||
"type": "phrase",
|
||||
"en": "Take care.",
|
||||
"zh": "保重。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"take care"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Take care and drink water.",
|
||||
"zh": "保重,多喝水。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "朋友生病了,跟他说保重。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U18-R01",
|
||||
"en": "fever",
|
||||
"zh": "发烧"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R02",
|
||||
"en": "health",
|
||||
"zh": "健康"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R03",
|
||||
"en": "healthy",
|
||||
"zh": "健康的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R04",
|
||||
"en": "body",
|
||||
"zh": "身体"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R05",
|
||||
"en": "arm",
|
||||
"zh": "手臂"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R06",
|
||||
"en": "leg",
|
||||
"zh": "腿"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R07",
|
||||
"en": "ear",
|
||||
"zh": "耳朵"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R08",
|
||||
"en": "nose",
|
||||
"zh": "鼻子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R09",
|
||||
"en": "mouth",
|
||||
"zh": "嘴"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R10",
|
||||
"en": "neck",
|
||||
"zh": "脖子"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R11",
|
||||
"en": "shoulder",
|
||||
"zh": "肩膀"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R12",
|
||||
"en": "knee",
|
||||
"zh": "膝盖"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R13",
|
||||
"en": "heart",
|
||||
"zh": "心脏"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R14",
|
||||
"en": "nurse",
|
||||
"zh": "护士"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R15",
|
||||
"en": "breathe",
|
||||
"zh": "呼吸"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R16",
|
||||
"en": "once",
|
||||
"zh": "一次"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R17",
|
||||
"en": "stomach",
|
||||
"zh": "胃"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R18",
|
||||
"en": "pill",
|
||||
"zh": "药片"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R19",
|
||||
"en": "ill",
|
||||
"zh": "生病的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R20",
|
||||
"en": "dentist",
|
||||
"zh": "牙医"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R21",
|
||||
"en": "toothache",
|
||||
"zh": "牙疼"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R22",
|
||||
"en": "blood",
|
||||
"zh": "血"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R23",
|
||||
"en": "twice",
|
||||
"zh": "两次"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R24",
|
||||
"en": "important",
|
||||
"zh": "重要的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R25",
|
||||
"en": "yesterday",
|
||||
"zh": "昨天"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-R26",
|
||||
"en": "still",
|
||||
"zh": "仍然"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U18-S1",
|
||||
"title": "说哪里不舒服",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U18-P01",
|
||||
"A1-U18-P02",
|
||||
"A1-U18-P03",
|
||||
"A1-U18-W01",
|
||||
"A1-U18-P09"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What's the matter? I don't feel well. I have a headache, and my neck hurts. I think I need to see a doctor.",
|
||||
"question": "说话人哪里不舒服?",
|
||||
"options": [
|
||||
"头疼和脖子疼",
|
||||
"发烧和胃疼",
|
||||
"牙疼"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I don't feel well."
|
||||
},
|
||||
{
|
||||
"text": "I have a headache.",
|
||||
"tip": "headache 读 /ˈhedeɪk/,ch 读 /k/"
|
||||
},
|
||||
{
|
||||
"text": "My head hurts."
|
||||
},
|
||||
{
|
||||
"text": "I feel sick."
|
||||
},
|
||||
{
|
||||
"text": "I need to see a doctor."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Your body\nHead: eyes, ears, nose and mouth\nNeck and shoulders\nArms and hands\nLegs and knees\nIn your body, your heart is very important. When you run, you breathe fast.\nToothache? See a dentist. A fever or blood? See a doctor or a nurse.",
|
||||
"question": "牙疼应该看谁?",
|
||||
"options": [
|
||||
"牙医",
|
||||
"护士",
|
||||
"医生"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你不舒服,头疼。",
|
||||
"example": "I don't feel well. I have a headache.",
|
||||
"itemIds": [
|
||||
"A1-U18-P01",
|
||||
"A1-U18-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你哪里疼,并说需要看医生。",
|
||||
"itemIds": [
|
||||
"A1-U18-P03",
|
||||
"A1-U18-W01",
|
||||
"A1-U18-P09"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-S2",
|
||||
"title": "在药店",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U18-W02",
|
||||
"A1-U18-P04",
|
||||
"A1-U18-P05",
|
||||
"A1-U18-P06",
|
||||
"A1-U18-P07",
|
||||
"A1-U18-P08"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Excuse me, is there a pharmacy near here? Yes, next to the bank. ... Hello. Do you have something for a headache? Yes, this medicine. How often do I take it? Three times a day, after meals.",
|
||||
"question": "药怎么吃?",
|
||||
"options": [
|
||||
"一天三次,饭后",
|
||||
"一天两次,饭前",
|
||||
"一天一次,睡前"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I need some medicine."
|
||||
},
|
||||
{
|
||||
"text": "Is there a pharmacy near here?"
|
||||
},
|
||||
{
|
||||
"text": "Do you have something for a headache?"
|
||||
},
|
||||
{
|
||||
"text": "How often do I take it?"
|
||||
},
|
||||
{
|
||||
"text": "Three times a day."
|
||||
},
|
||||
{
|
||||
"text": "Take it after meals."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Medicine box\nFor headaches and fevers\nPeople over 12: take 2 pills three times a day, after meals.\nChildren: take 1 pill twice a day.\nDon't take more than 6 pills a day.\nIll for more than three days? See a doctor.\nFor stomach problems, take it once a day, before meals.",
|
||||
"question": "大人一天最多吃几片?",
|
||||
"options": [
|
||||
"六片",
|
||||
"三片",
|
||||
"两片"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问有没有治头疼的药,再问多久吃一次。",
|
||||
"example": "Do you have something for a headache? How often do I take it?",
|
||||
"itemIds": [
|
||||
"A1-U18-P05",
|
||||
"A1-U18-P06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你在药店买感冒药,用英语问清楚怎么吃。",
|
||||
"itemIds": [
|
||||
"A1-U18-W02",
|
||||
"A1-U18-P05",
|
||||
"A1-U18-P06",
|
||||
"A1-U18-P07",
|
||||
"A1-U18-P08"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-S3",
|
||||
"title": "好些了",
|
||||
"minutes": 11,
|
||||
"itemIds": [
|
||||
"A1-U18-P10",
|
||||
"A1-U18-P11"
|
||||
],
|
||||
"listening": {
|
||||
"text": "How are you today? I was sick yesterday, but I feel better now. Good! Take care.",
|
||||
"question": "说话人今天怎么样?",
|
||||
"options": [
|
||||
"好多了",
|
||||
"还在发烧",
|
||||
"要去看医生"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "I feel better now."
|
||||
},
|
||||
{
|
||||
"text": "Take care."
|
||||
},
|
||||
{
|
||||
"text": "I was sick yesterday."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "A note to my friend\nHi Sam,\nI was ill yesterday. I had a fever, and my body hurt. Today I feel better now. I drink a lot of water and sleep. Health is important! I want to be healthy, so I run every day.\nTake care,\nLily",
|
||||
"question": "Lily 昨天怎么了?",
|
||||
"options": [
|
||||
"发烧,全身疼",
|
||||
"牙疼",
|
||||
"胃疼"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你昨天病了,现在好多了。",
|
||||
"example": "I was sick yesterday. I feel better now.",
|
||||
"itemIds": [
|
||||
"A1-U18-P10",
|
||||
"A1-U18-W01"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "朋友生病了,用英语问他好点没有,并说保重。",
|
||||
"itemIds": [
|
||||
"A1-U18-P10",
|
||||
"A1-U18-P11"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U18-C1",
|
||||
"kind": "main",
|
||||
"title": "在药店买药",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "药剂师",
|
||||
"setting": "你旅行时头疼,走进一家药店。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. How can I help you?",
|
||||
"goal": "说你不舒服,头疼",
|
||||
"model": "I don't feel well. I have a headache.",
|
||||
"zh": "我不舒服,头疼。",
|
||||
"itemIds": [
|
||||
"A1-U18-P01",
|
||||
"A1-U18-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh, I'm sorry. Do you have a fever?",
|
||||
"goal": "说没有发烧,问有没有治头疼的药",
|
||||
"model": "No. Do you have something for a headache?",
|
||||
"zh": "没有。有治头疼的药吗?",
|
||||
"itemIds": [
|
||||
"A1-U18-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes. This medicine is good.",
|
||||
"goal": "问多久吃一次",
|
||||
"model": "How often do I take it?",
|
||||
"zh": "这个药多久吃一次?",
|
||||
"itemIds": [
|
||||
"A1-U18-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Three times a day, after meals.",
|
||||
"goal": "复述:一天三次,饭后",
|
||||
"model": "Three times a day, after meals. OK.",
|
||||
"zh": "一天三次,饭后。好的。",
|
||||
"itemIds": [
|
||||
"A1-U18-P07",
|
||||
"A1-U18-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Is it very bad?",
|
||||
"goal": "说如果还疼就去看医生",
|
||||
"model": "If my head hurts tomorrow, I need to see a doctor.",
|
||||
"zh": "如果明天头还疼,我需要看医生。",
|
||||
"itemIds": [
|
||||
"A1-U18-P03",
|
||||
"A1-U18-P09"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-C2",
|
||||
"kind": "variant",
|
||||
"title": "在街上找药店",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "路人",
|
||||
"setting": "你的孩子生病了,你在街上找药店。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Are you OK?",
|
||||
"goal": "说孩子生病了",
|
||||
"model": "My son is sick.",
|
||||
"zh": "我儿子病了。",
|
||||
"itemIds": [
|
||||
"A1-U18-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh no. What's the matter?",
|
||||
"goal": "说他发烧了",
|
||||
"model": "He has a fever.",
|
||||
"zh": "他发烧了。",
|
||||
"itemIds": [
|
||||
"A1-U18-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "He needs some medicine.",
|
||||
"goal": "问附近有没有药店",
|
||||
"model": "Yes, he needs medicine. Is there a pharmacy near here?",
|
||||
"zh": "是的,他需要药。附近有药店吗?",
|
||||
"itemIds": [
|
||||
"A1-U18-P04",
|
||||
"A1-U18-W02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, it's on the next street.",
|
||||
"goal": "说也许需要看医生",
|
||||
"model": "Thank you. Maybe he needs to see a doctor too.",
|
||||
"zh": "谢谢。也许他还需要看医生。",
|
||||
"itemIds": [
|
||||
"A1-U18-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "There's a hospital near the pharmacy. Take care!",
|
||||
"goal": "道谢",
|
||||
"model": "Thanks. You take care too.",
|
||||
"zh": "谢谢,你也保重。",
|
||||
"itemIds": [
|
||||
"A1-U18-P11"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-C3",
|
||||
"kind": "variant",
|
||||
"title": "朋友来问候",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "朋友",
|
||||
"setting": "你昨天生病了,今天朋友打电话来问候。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! How are you today?",
|
||||
"goal": "说好多了",
|
||||
"model": "I feel better now.",
|
||||
"zh": "我现在好多了。",
|
||||
"itemIds": [
|
||||
"A1-U18-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Great! Do you still have a headache?",
|
||||
"goal": "说还是不太舒服,背疼",
|
||||
"model": "A little. I don't feel well, and my back hurts.",
|
||||
"zh": "有一点。还是不太舒服,背也疼。",
|
||||
"itemIds": [
|
||||
"A1-U18-P01",
|
||||
"A1-U18-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Do you have medicine?",
|
||||
"goal": "说有药,一天吃两次",
|
||||
"model": "Yes. I take the medicine two times a day.",
|
||||
"zh": "有,这个药一天吃两次。",
|
||||
"itemIds": [
|
||||
"A1-U18-W02",
|
||||
"A1-U18-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Before meals or after meals?",
|
||||
"goal": "说饭后吃",
|
||||
"model": "After meals.",
|
||||
"zh": "饭后吃。",
|
||||
"itemIds": [
|
||||
"A1-U18-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. Take care!",
|
||||
"goal": "道谢",
|
||||
"model": "Thanks. Take care.",
|
||||
"zh": "谢谢。保重。",
|
||||
"itemIds": [
|
||||
"A1-U18-P11"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U18-T1",
|
||||
"sceneId": "A1-U18-C1",
|
||||
"goal": "在药店说明症状并问清楚药怎么吃",
|
||||
"itemIds": [
|
||||
"A1-U18-P01",
|
||||
"A1-U18-P02",
|
||||
"A1-U18-P05",
|
||||
"A1-U18-P06",
|
||||
"A1-U18-P07",
|
||||
"A1-U18-P08",
|
||||
"A1-U18-P03",
|
||||
"A1-U18-P09"
|
||||
],
|
||||
"slots": [
|
||||
"症状",
|
||||
"药",
|
||||
"用法"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-T2",
|
||||
"sceneId": "A1-U18-C2",
|
||||
"goal": "孩子生病时在街上找药店",
|
||||
"itemIds": [
|
||||
"A1-U18-W01",
|
||||
"A1-U18-P02",
|
||||
"A1-U18-P04",
|
||||
"A1-U18-W02",
|
||||
"A1-U18-P09",
|
||||
"A1-U18-P11"
|
||||
],
|
||||
"slots": [
|
||||
"症状",
|
||||
"药店"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-T3",
|
||||
"sceneId": "A1-U18-C3",
|
||||
"goal": "回答朋友的问候,说说病情",
|
||||
"itemIds": [
|
||||
"A1-U18-P10",
|
||||
"A1-U18-P01",
|
||||
"A1-U18-P03",
|
||||
"A1-U18-W02",
|
||||
"A1-U18-P07",
|
||||
"A1-U18-P08",
|
||||
"A1-U18-P11"
|
||||
],
|
||||
"slots": [
|
||||
"现在怎么样",
|
||||
"哪里疼",
|
||||
"吃药"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U18-M1",
|
||||
"mode": "listening",
|
||||
"title": "药店店员给建议",
|
||||
"text": "Hello. What's the matter? You don't look well; you don't look healthy today. Do you have a fever? OK, you have a headache, your stomach hurts, and your neck and shoulder hurt too. Don't worry. Your health is important. Here is some medicine. Take one pill twice a day, after meals — not once, but two times. Please breathe slowly, drink a lot of water, and rest. If your ear, nose or leg still hurts tomorrow, or if you feel ill, you need to see a doctor. For a toothache, see a dentist. The nurse can help too. The pharmacy is open until ten. Take care!",
|
||||
"glosses": {
|
||||
"rest": "休息",
|
||||
"hurts": "疼",
|
||||
"hurt": "疼",
|
||||
"slowly": "慢慢地"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "说话人主要在做什么?",
|
||||
"options": [
|
||||
"给生病的人建议和药",
|
||||
"卖衣服",
|
||||
"介绍菜单"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "这个药怎么吃?",
|
||||
"options": [
|
||||
"一天两次,饭后吃",
|
||||
"一天三次,饭前吃",
|
||||
"睡前吃"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U18-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:说不舒服",
|
||||
"text": "Ben: I don't feel well. I have a bad headache, and my body hurts everywhere.\nMia: Oh no. Do you have a fever? You don't look healthy.\nBen: A little. My head and my stomach hurt, and my arm and leg are tired.\nMia: Yesterday you were fine. You should rest. It's important. Do you have any medicine?\nBen: No. Is there a pharmacy near here?\nMia: Yes, next to the bank. The nurse there can give you something for a headache.\nBen: Thanks. My tooth hurts too. Maybe I need a dentist for this toothache.\nMia: Take care. Breathe slowly and drink water. I hope you feel better soon.\nBen: Thank you, my friend. I still feel ill, but a little better now.",
|
||||
"glosses": {
|
||||
"rest": "休息",
|
||||
"hurts": "疼",
|
||||
"hurt": "疼",
|
||||
"everywhere": "到处",
|
||||
"slowly": "慢慢地"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Ben 哪里不舒服?",
|
||||
"options": [
|
||||
"头疼、发一点烧、浑身疼",
|
||||
"只是牙疼",
|
||||
"只是肚子疼"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "药店在哪里?",
|
||||
"options": [
|
||||
"银行旁边",
|
||||
"车站里",
|
||||
"酒店对面"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "sore throat",
|
||||
"reason": "sore 为 B1、throat 为 B2;A1 用 My throat hurts 以外的说法,先不教"
|
||||
},
|
||||
{
|
||||
"en": "cough",
|
||||
"reason": "B1–B2;留到 A2 看病单元"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,913 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U19",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "遇到麻烦时求助",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说明遇到的麻烦、请求帮忙、感谢与回应",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "64",
|
||||
"use": "active",
|
||||
"reason": "只用 I was / It was 说刚才的情况"
|
||||
},
|
||||
{
|
||||
"id": "65",
|
||||
"use": "active",
|
||||
"reason": "只用 lost、took、happened 几个过去式"
|
||||
},
|
||||
{
|
||||
"id": "117",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "101",
|
||||
"use": "active"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U19-P01",
|
||||
"type": "phrase",
|
||||
"en": "I lost my wallet.",
|
||||
"zh": "我的钱包丢了。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "wallet 为 A2,旅行中丢东西时最先要说",
|
||||
"match": [
|
||||
[
|
||||
"lost my"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I lost my phone.",
|
||||
"zh": "我的手机丢了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你的钱包丢了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P02",
|
||||
"type": "phrase",
|
||||
"en": "Someone took my bag.",
|
||||
"zh": "有人拿走了我的包。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"took my"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Someone took my phone.",
|
||||
"zh": "有人拿走了我的手机。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说有人拿走了你的包。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P03",
|
||||
"type": "phrase",
|
||||
"en": "Call the police!",
|
||||
"zh": "快报警!",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "police 为 A2,遇到偷窃必须会说",
|
||||
"match": [
|
||||
[
|
||||
"call the police"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Please call the police.",
|
||||
"zh": "请报警。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请旁边的人报警。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P04",
|
||||
"type": "phrase",
|
||||
"en": "Where is the police station?",
|
||||
"zh": "警察局在哪里?",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "police station 为 A2,报案时要去",
|
||||
"match": [
|
||||
[
|
||||
"police station"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Is there a police station near here?",
|
||||
"zh": "附近有警察局吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问警察局在哪里。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P05",
|
||||
"type": "phrase",
|
||||
"en": "I can't find my passport.",
|
||||
"zh": "我找不到我的护照了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"can't find"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I can't find my hotel.",
|
||||
"zh": "我找不到我的酒店了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你找不到护照了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P06",
|
||||
"type": "phrase",
|
||||
"en": "What happened?",
|
||||
"zh": "发生了什么事?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what happened"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What happened to your bag?",
|
||||
"zh": "你的包怎么了?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "看到有人着急,问发生了什么。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P07",
|
||||
"type": "phrase",
|
||||
"en": "I was on the bus.",
|
||||
"zh": "我当时在公交车上。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"i was"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I was in the station.",
|
||||
"zh": "我当时在车站。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你当时在出租车上。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P08",
|
||||
"type": "phrase",
|
||||
"en": "Does anyone speak Chinese?",
|
||||
"zh": "有人会说中文吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"does anyone speak"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Does anyone speak English?",
|
||||
"zh": "有人会说英语吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问有没有人会说中文。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P09",
|
||||
"type": "phrase",
|
||||
"en": "Don't worry.",
|
||||
"zh": "别担心。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"don't worry"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Don't worry. We can help you.",
|
||||
"zh": "别担心,我们能帮你。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "安慰着急的朋友。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P10",
|
||||
"type": "phrase",
|
||||
"en": "I need help.",
|
||||
"zh": "我需要帮助。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"need help"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I need help, please.",
|
||||
"zh": "我需要帮助,请帮帮我。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你需要帮助。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-P11",
|
||||
"type": "phrase",
|
||||
"en": "It's an emergency.",
|
||||
"zh": "这是紧急情况。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "emergency 为 A2,求助时要让对方知道情况紧急",
|
||||
"match": [
|
||||
[
|
||||
"emergency"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Please help. It's an emergency!",
|
||||
"zh": "请帮帮忙,这是急事!"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉别人情况紧急。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-W01",
|
||||
"type": "word",
|
||||
"en": "quickly",
|
||||
"zh": "快点",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"quickly"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Please come quickly!",
|
||||
"zh": "请快点来!"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请别人快点来。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-W02",
|
||||
"type": "word",
|
||||
"en": "fire",
|
||||
"zh": "火;火灾",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"fire"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "There's a fire in the kitchen!",
|
||||
"zh": "厨房着火了!"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "喊人说着火了。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U19-R01",
|
||||
"en": "thief",
|
||||
"zh": "小偷"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R02",
|
||||
"en": "steal",
|
||||
"zh": "偷"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R03",
|
||||
"en": "danger",
|
||||
"zh": "危险"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R04",
|
||||
"en": "dangerous",
|
||||
"zh": "危险的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R05",
|
||||
"en": "mistake",
|
||||
"zh": "错误"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R06",
|
||||
"en": "trouble",
|
||||
"zh": "麻烦"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R07",
|
||||
"en": "report",
|
||||
"zh": "报告"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R08",
|
||||
"en": "fall",
|
||||
"zh": "摔倒"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R09",
|
||||
"en": "save",
|
||||
"zh": "救;保存"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R10",
|
||||
"en": "believe",
|
||||
"zh": "相信"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R11",
|
||||
"en": "guess",
|
||||
"zh": "猜"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R12",
|
||||
"en": "true",
|
||||
"zh": "真的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R13",
|
||||
"en": "lucky",
|
||||
"zh": "幸运的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R14",
|
||||
"en": "poor",
|
||||
"zh": "可怜的;穷的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R15",
|
||||
"en": "real",
|
||||
"zh": "真实的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R16",
|
||||
"en": "reason",
|
||||
"zh": "原因"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R17",
|
||||
"en": "touch",
|
||||
"zh": "碰"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R18",
|
||||
"en": "throw",
|
||||
"zh": "扔"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R19",
|
||||
"en": "policeman",
|
||||
"zh": "警察"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R20",
|
||||
"en": "somebody",
|
||||
"zh": "某人"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R21",
|
||||
"en": "safe",
|
||||
"zh": "安全的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R22",
|
||||
"en": "everywhere",
|
||||
"zh": "到处"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R23",
|
||||
"en": "tell",
|
||||
"zh": "告诉"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R24",
|
||||
"en": "story",
|
||||
"zh": "故事"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-R25",
|
||||
"en": "thing",
|
||||
"zh": "东西"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U19-S1",
|
||||
"title": "丢了东西",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U19-P01",
|
||||
"A1-U19-P05",
|
||||
"A1-U19-P06",
|
||||
"A1-U19-P07",
|
||||
"A1-U19-P10"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What happened? I lost my wallet. Where were you? I was on the bus. And I can't find my passport too. I need help.",
|
||||
"question": "说话人丢了什么?",
|
||||
"options": [
|
||||
"钱包,也找不到护照",
|
||||
"手机和包",
|
||||
"酒店钥匙"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What happened?"
|
||||
},
|
||||
{
|
||||
"text": "I lost my wallet."
|
||||
},
|
||||
{
|
||||
"text": "I can't find my passport."
|
||||
},
|
||||
{
|
||||
"text": "I was on the bus."
|
||||
},
|
||||
{
|
||||
"text": "I need help."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Lost something?\n1. Don't be afraid. Maybe it's your mistake. Look everywhere: in your bag, in your pockets, in the hotel.\n2. Think: where was it? Was it in the taxi or on the bus?\n3. Call the bus or taxi office. Tell them the date and time.\n4. Can't find it? Go to the police station. They can write a report.\n5. You lost your passport? Call your country's office in the city.",
|
||||
"question": "找不到丢失的东西,最后应该去哪里?",
|
||||
"options": [
|
||||
"警察局",
|
||||
"出租车公司",
|
||||
"酒店前台"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你钱包丢了,当时在公交车上。",
|
||||
"example": "I lost my wallet. I was on the bus.",
|
||||
"itemIds": [
|
||||
"A1-U19-P01",
|
||||
"A1-U19-P07"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语告诉酒店前台你丢了什么、当时在哪里。",
|
||||
"itemIds": [
|
||||
"A1-U19-P01",
|
||||
"A1-U19-P05",
|
||||
"A1-U19-P07",
|
||||
"A1-U19-P10"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-S2",
|
||||
"title": "被偷和报警",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U19-P02",
|
||||
"A1-U19-P03",
|
||||
"A1-U19-P04",
|
||||
"A1-U19-P08",
|
||||
"A1-U19-P11"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Help! Someone took my bag! Call the police! It's an emergency. Does anyone speak Chinese? I do. Don't worry. The police station is next to the bank.",
|
||||
"question": "警察局在哪里?",
|
||||
"options": [
|
||||
"银行旁边",
|
||||
"公交车站旁边",
|
||||
"酒店对面"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Someone took my bag."
|
||||
},
|
||||
{
|
||||
"text": "Call the police!",
|
||||
"tip": "police 重音在第二个音节:po-LICE"
|
||||
},
|
||||
{
|
||||
"text": "Where is the police station?"
|
||||
},
|
||||
{
|
||||
"text": "Does anyone speak Chinese?"
|
||||
},
|
||||
{
|
||||
"text": "It's an emergency."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "At the police station\nPoliceman: What happened?\nMia: Somebody took my bag on the train. I believe it was a thief. Thieves can steal bags on trains.\nPoliceman: What was in it?\nMia: My phone and my wallet. There was nothing else.\nPoliceman: Do you remember the man? Guess his age.\nMia: About thirty. He was tall.\nPoliceman: OK. Here is your report. Who should we call?",
|
||||
"question": "Mia 的包里有什么?",
|
||||
"options": [
|
||||
"手机和钱包",
|
||||
"护照和钥匙",
|
||||
"相机和钱"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说有人拿走了你的包,请人报警。",
|
||||
"example": "Someone took my bag. Call the police!",
|
||||
"itemIds": [
|
||||
"A1-U19-P02",
|
||||
"A1-U19-P03"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你的手机被偷了,用英语向路人求助并问警察局在哪。",
|
||||
"itemIds": [
|
||||
"A1-U19-P02",
|
||||
"A1-U19-P11",
|
||||
"A1-U19-P04",
|
||||
"A1-U19-P08"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-S3",
|
||||
"title": "紧急情况",
|
||||
"minutes": 11,
|
||||
"itemIds": [
|
||||
"A1-U19-P09",
|
||||
"A1-U19-W01",
|
||||
"A1-U19-W02"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Fire! There's a fire in the kitchen! Come quickly! Don't worry, everybody. Go out of the building. Is anyone hurt? No, we're all safe.",
|
||||
"question": "大家现在怎么样?",
|
||||
"options": [
|
||||
"都很安全",
|
||||
"有人受伤",
|
||||
"还在厨房里"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Don't worry."
|
||||
},
|
||||
{
|
||||
"text": "Please come quickly!"
|
||||
},
|
||||
{
|
||||
"text": "There's a fire!"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Is it true?\nMy friend Ben was lucky. He had a real problem in a big city. He was on a bridge, and a poor old woman was in danger. The road was dangerous. She had a fall. Ben ran to her quickly and helped her. People say he saved her. Now Ben tells this story, and his reason is this: \"Don't touch your phone. Look at the people around you!\" Some people throw things on the road, and some people help.",
|
||||
"question": "Ben 做了什么?",
|
||||
"options": [
|
||||
"帮了摔倒的老人",
|
||||
"报了警",
|
||||
"救了火"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:喊着火了,请人快点来。",
|
||||
"example": "There's a fire! Please come quickly!",
|
||||
"itemIds": [
|
||||
"A1-U19-W02",
|
||||
"A1-U19-W01"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "朋友很害怕,用英语安慰他,并说你们要快点出去。",
|
||||
"itemIds": [
|
||||
"A1-U19-P09",
|
||||
"A1-U19-W01"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U19-C1",
|
||||
"kind": "main",
|
||||
"title": "在街上包被抢",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "路人",
|
||||
"setting": "你在街上,有人抢走了你的包,你向路人求助。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Are you OK? Are you in trouble? What happened?",
|
||||
"goal": "说有人拿走了你的包",
|
||||
"model": "Someone took my bag!",
|
||||
"zh": "有人拿走了我的包!",
|
||||
"itemIds": [
|
||||
"A1-U19-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh no! What was in it?",
|
||||
"goal": "说钱包在里面,情况紧急",
|
||||
"model": "My wallet. It's an emergency. I need help.",
|
||||
"zh": "我的钱包在里面。情况紧急,我需要帮助。",
|
||||
"itemIds": [
|
||||
"A1-U19-P11",
|
||||
"A1-U19-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Don't worry. I can help you.",
|
||||
"goal": "请对方报警",
|
||||
"model": "Please call the police!",
|
||||
"zh": "请报警!",
|
||||
"itemIds": [
|
||||
"A1-U19-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. I can call them now.",
|
||||
"goal": "请他们快点来",
|
||||
"model": "Can they come quickly?",
|
||||
"zh": "他们能快点来吗?",
|
||||
"itemIds": [
|
||||
"A1-U19-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Good idea. Do you know a police station near here?",
|
||||
"goal": "问警察局在哪",
|
||||
"model": "No. Where is the police station?",
|
||||
"zh": "警察局在哪里?",
|
||||
"itemIds": [
|
||||
"A1-U19-P04"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-C2",
|
||||
"kind": "variant",
|
||||
"title": "在酒店前台说丢了护照",
|
||||
"learnerRole": "客人",
|
||||
"aiRole": "酒店前台",
|
||||
"setting": "你回到酒店,发现护照不见了。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello. You look worried.",
|
||||
"goal": "说你找不到护照",
|
||||
"model": "I can't find my passport.",
|
||||
"zh": "我找不到我的护照了。",
|
||||
"itemIds": [
|
||||
"A1-U19-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh no! When was the last time you had it?",
|
||||
"goal": "说当时在出租车上",
|
||||
"model": "I was in a taxi.",
|
||||
"zh": "我当时在出租车上。",
|
||||
"itemIds": [
|
||||
"A1-U19-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Do you remember the taxi?",
|
||||
"goal": "说不记得,钱包也丢了",
|
||||
"model": "No. And I lost my wallet.",
|
||||
"zh": "不记得。而且我的钱包也丢了。",
|
||||
"itemIds": [
|
||||
"A1-U19-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Don't worry. We can call the taxi office.",
|
||||
"goal": "问有没有人会说中文",
|
||||
"model": "Thank you. Does anyone speak Chinese here?",
|
||||
"zh": "谢谢。这里有人会说中文吗?",
|
||||
"itemIds": [
|
||||
"A1-U19-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Yes, Mr Wang can help you.",
|
||||
"goal": "说你需要帮助",
|
||||
"model": "Great. I need help.",
|
||||
"zh": "太好了。我需要帮助。",
|
||||
"itemIds": [
|
||||
"A1-U19-P10"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-C3",
|
||||
"kind": "variant",
|
||||
"title": "朋友遇到紧急情况",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "慌张的朋友",
|
||||
"setting": "你和朋友在公寓里,朋友突然跑过来喊你。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Come here! Come here!",
|
||||
"goal": "问发生了什么",
|
||||
"model": "What happened?",
|
||||
"zh": "发生了什么事?",
|
||||
"itemIds": [
|
||||
"A1-U19-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "There's a fire in the kitchen!",
|
||||
"goal": "说着火了,要快点",
|
||||
"model": "A fire? Quickly!",
|
||||
"zh": "着火了?快!",
|
||||
"itemIds": [
|
||||
"A1-U19-W02",
|
||||
"A1-U19-W01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "What can we do?",
|
||||
"goal": "叫朋友别担心,报警",
|
||||
"model": "Don't worry. Call the police.",
|
||||
"zh": "别担心。报警。",
|
||||
"itemIds": [
|
||||
"A1-U19-P09",
|
||||
"A1-U19-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The number is 110?",
|
||||
"goal": "说这是紧急情况,要叫他们快点来",
|
||||
"model": "Yes. Tell them it's an emergency.",
|
||||
"zh": "对。告诉他们这是紧急情况。",
|
||||
"itemIds": [
|
||||
"A1-U19-P11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK! Are you OK?",
|
||||
"goal": "说你需要帮助,不会英语的邻居有人会说中文吗",
|
||||
"model": "I need help. Does anyone speak Chinese?",
|
||||
"zh": "我需要帮助。有人会说中文吗?",
|
||||
"itemIds": [
|
||||
"A1-U19-P10",
|
||||
"A1-U19-P08"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U19-T1",
|
||||
"sceneId": "A1-U19-C1",
|
||||
"goal": "包被抢后向路人求助并找警察",
|
||||
"itemIds": [
|
||||
"A1-U19-P02",
|
||||
"A1-U19-P11",
|
||||
"A1-U19-P10",
|
||||
"A1-U19-P03",
|
||||
"A1-U19-W01",
|
||||
"A1-U19-P04"
|
||||
],
|
||||
"slots": [
|
||||
"发生了什么",
|
||||
"报警",
|
||||
"警察局"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-T2",
|
||||
"sceneId": "A1-U19-C2",
|
||||
"goal": "向酒店说明护照和钱包丢失的经过",
|
||||
"itemIds": [
|
||||
"A1-U19-P05",
|
||||
"A1-U19-P07",
|
||||
"A1-U19-P01",
|
||||
"A1-U19-P08",
|
||||
"A1-U19-P10"
|
||||
],
|
||||
"slots": [
|
||||
"丢了什么",
|
||||
"在哪里丢的",
|
||||
"求助"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-T3",
|
||||
"sceneId": "A1-U19-C3",
|
||||
"goal": "家里着火时和朋友应对",
|
||||
"itemIds": [
|
||||
"A1-U19-P06",
|
||||
"A1-U19-W02",
|
||||
"A1-U19-W01",
|
||||
"A1-U19-P09",
|
||||
"A1-U19-P03",
|
||||
"A1-U19-P11",
|
||||
"A1-U19-P10",
|
||||
"A1-U19-P08"
|
||||
],
|
||||
"slots": [
|
||||
"情况",
|
||||
"报警",
|
||||
"求助"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U19-M1",
|
||||
"mode": "listening",
|
||||
"title": "有人在求助",
|
||||
"text": "Excuse me! I need help — it's an emergency! A thief took my bag on the bus and ran away. I think somebody wanted to steal my money. My passport and my wallet are in the bag! This is real trouble. Please call the police and report it. Where is the police station? Is it safe here? I was on the bus from the airport. I looked everywhere, but I can't find my phone. I made a big mistake — I didn't hold my bag. It's dangerous here! Does anyone speak Chinese? Please help me quickly. Thank you very much.",
|
||||
"glosses": {
|
||||
"ran": "跑(过去式)",
|
||||
"anyone": "任何人",
|
||||
"made": "make 的过去式;made a mistake 犯了错"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这个人为什么求助?",
|
||||
"options": [
|
||||
"包被小偷拿走了",
|
||||
"找不到酒店",
|
||||
"想问路"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "他的护照在哪里?",
|
||||
"options": [
|
||||
"在被拿走的包里",
|
||||
"在酒店",
|
||||
"在口袋里"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U19-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:丢了钱包",
|
||||
"text": "Anna: What happened? You look worried. Are you in trouble?\nBen: I lost my wallet! I can't find it anywhere. I think a thief took it.\nAnna: Don't worry. Where were you? Try to remember.\nBen: I was in the cafe near the station. I guess I left it on the table. It's a silly mistake.\nAnna: Maybe nobody took it. Was there money in it?\nBen: Yes, and my ID card. Do you believe it's still there?\nAnna: Maybe. It's not a dangerous place; people are kind and honest there. Let's go back and look.\nBen: You're right. I'm lucky you're here. If we can't find it, we'll report it at the police station.\nAnna: Good plan. That's the true way to be safe.\nBen: Thank you so much. You saved me!",
|
||||
"glosses": {
|
||||
"anywhere": "任何地方",
|
||||
"silly": "傻的",
|
||||
"nobody": "没有人",
|
||||
"honest": "诚实的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Ben 觉得钱包丢在哪里了?",
|
||||
"options": [
|
||||
"车站旁边的咖啡店",
|
||||
"公交车上",
|
||||
"酒店房间"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "如果找不到,他们打算怎么办?",
|
||||
"options": [
|
||||
"去派出所报案",
|
||||
"回家",
|
||||
"给妈妈打电话"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "ambulance",
|
||||
"reason": "B1;A1 用 call the police / It's an emergency 求助"
|
||||
},
|
||||
{
|
||||
"en": "embassy",
|
||||
"reason": "B2;A1 用 your country's office 解释"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,858 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U20",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "打电话与留言",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "留下姓名、号码和简短信息",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "141",
|
||||
"use": "active",
|
||||
"reason": "只用 I'll call again later 表示马上决定的事"
|
||||
},
|
||||
{
|
||||
"id": "237",
|
||||
"use": "receptive"
|
||||
},
|
||||
{
|
||||
"id": "117",
|
||||
"use": "active"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U20-P01",
|
||||
"type": "phrase",
|
||||
"en": "Hello, this is Chen.",
|
||||
"zh": "你好,我是陈。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"this is"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Hello, this is Anna from room 305.",
|
||||
"zh": "你好,我是 305 房间的安娜。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "打电话时先说你是谁。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P02",
|
||||
"type": "phrase",
|
||||
"en": "Can I speak to Mr Brown?",
|
||||
"zh": "我能和布朗先生通话吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"speak to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Can I speak to Lucy, please?",
|
||||
"zh": "请问露西在吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "打电话找你的朋友汤姆。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P03",
|
||||
"type": "phrase",
|
||||
"en": "Who's calling, please?",
|
||||
"zh": "请问您是哪位?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"who's calling",
|
||||
"who is calling"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Hello. Who's calling?",
|
||||
"zh": "你好,请问是哪位?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "接电话时问对方是谁。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P04",
|
||||
"type": "phrase",
|
||||
"en": "Hold on, please.",
|
||||
"zh": "请稍等。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"hold on"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Hold on. I can get him.",
|
||||
"zh": "稍等,我去叫他。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请对方在电话里稍等。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P05",
|
||||
"type": "phrase",
|
||||
"en": "He's not here right now.",
|
||||
"zh": "他现在不在。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"not here right now"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, she's not here right now.",
|
||||
"zh": "抱歉,她现在不在。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "告诉打电话的人你妈妈不在。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P06",
|
||||
"type": "phrase",
|
||||
"en": "Can I leave a message?",
|
||||
"zh": "我可以留个言吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"leave a message"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Do you want to leave a message?",
|
||||
"zh": "您要留言吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "要找的人不在,问能不能留言。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P07",
|
||||
"type": "phrase",
|
||||
"en": "Can you call me back?",
|
||||
"zh": "你能给我回电话吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"call me back",
|
||||
"call you back"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I can call you back later.",
|
||||
"zh": "我晚点给你回电话。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请对方给你回电话。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P08",
|
||||
"type": "phrase",
|
||||
"en": "What's your mobile number?",
|
||||
"zh": "你的手机号是多少?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"mobile number",
|
||||
"phone number"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My mobile number is 138 2046 7751.",
|
||||
"zh": "我的手机号是 138 2046 7751。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问对方的手机号。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P09",
|
||||
"type": "phrase",
|
||||
"en": "Sorry, I can't hear you.",
|
||||
"zh": "抱歉,我听不见你说话。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"can't hear"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I can't hear you. Can you speak slowly?",
|
||||
"zh": "我听不见,你能慢点说吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "电话信号不好,说你听不见。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P10",
|
||||
"type": "phrase",
|
||||
"en": "Sorry, wrong number.",
|
||||
"zh": "抱歉,打错了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"wrong number"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I think you have the wrong number.",
|
||||
"zh": "我想你打错了。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "有人打错电话,告诉他打错了。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P11",
|
||||
"type": "phrase",
|
||||
"en": "I'll call again later.",
|
||||
"zh": "我晚点再打。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"call again",
|
||||
"call later"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "OK, I'll call later.",
|
||||
"zh": "好,我晚点再打。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "对方忙,说你晚点再打。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-P12",
|
||||
"type": "phrase",
|
||||
"en": "Please text me.",
|
||||
"zh": "请给我发短信。",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "text 为 A2 动词,旅行中发短信确认地址很常用",
|
||||
"match": [
|
||||
[
|
||||
"text me"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Text me the address, please.",
|
||||
"zh": "请把地址发短信给我。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "请朋友把时间发短信给你。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U20-R01",
|
||||
"en": "telephone",
|
||||
"zh": "电话"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R02",
|
||||
"en": "mail",
|
||||
"zh": "邮件"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R03",
|
||||
"en": "ring",
|
||||
"zh": "(电话)响"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R04",
|
||||
"en": "voice",
|
||||
"zh": "声音"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R05",
|
||||
"en": "conversation",
|
||||
"zh": "对话"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R06",
|
||||
"en": "talk",
|
||||
"zh": "说话"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R07",
|
||||
"en": "mind",
|
||||
"zh": "介意(Never mind.)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R08",
|
||||
"en": "grandma",
|
||||
"zh": "奶奶;外婆"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R09",
|
||||
"en": "grandpa",
|
||||
"zh": "爷爷;外公"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R10",
|
||||
"en": "mum",
|
||||
"zh": "妈妈"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R11",
|
||||
"en": "haircut",
|
||||
"zh": "理发"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R12",
|
||||
"en": "class",
|
||||
"zh": "课;班"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R13",
|
||||
"en": "lesson",
|
||||
"zh": "课"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R14",
|
||||
"en": "course",
|
||||
"zh": "课程"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R15",
|
||||
"en": "test",
|
||||
"zh": "考试"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R16",
|
||||
"en": "college",
|
||||
"zh": "学院"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R17",
|
||||
"en": "club",
|
||||
"zh": "俱乐部"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R18",
|
||||
"en": "group",
|
||||
"zh": "小组"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R19",
|
||||
"en": "owner",
|
||||
"zh": "老板;主人"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-R20",
|
||||
"en": "worker",
|
||||
"zh": "工作人员"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U20-S1",
|
||||
"title": "打电话找人",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U20-P01",
|
||||
"A1-U20-P02",
|
||||
"A1-U20-P03",
|
||||
"A1-U20-P04",
|
||||
"A1-U20-P05"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hello? Hello, this is Chen. Can I speak to Mr Brown? Who's calling, please? Chen, from London. Hold on, please. ... Sorry, he's not here right now.",
|
||||
"question": "布朗先生现在怎么样?",
|
||||
"options": [
|
||||
"不在",
|
||||
"在开会",
|
||||
"马上来接电话"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Hello, this is Chen."
|
||||
},
|
||||
{
|
||||
"text": "Can I speak to Mr Brown?"
|
||||
},
|
||||
{
|
||||
"text": "Who's calling, please?"
|
||||
},
|
||||
{
|
||||
"text": "Hold on, please.",
|
||||
"tip": "hold on 连读成 /həʊl dɒn/"
|
||||
},
|
||||
{
|
||||
"text": "He's not here right now."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Phone talk\nThe phone rings. Grandma answers.\nGrandma: Hello?\nLeo: Hi Grandma, this is Leo. Can I speak to Mum?\nGrandma: Hold on... Sorry, she's not here right now. She's at her dance class. Do you want to talk to Grandpa?\nLeo: Yes, please!\nIt's a short conversation, but Grandma loves Leo's voice.",
|
||||
"question": "妈妈现在在哪里?",
|
||||
"options": [
|
||||
"在舞蹈课上",
|
||||
"在上班",
|
||||
"在家里"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你是谁,再问能不能和汤姆通话。",
|
||||
"example": "Hello, this is Chen. Can I speak to Tom?",
|
||||
"itemIds": [
|
||||
"A1-U20-P01",
|
||||
"A1-U20-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "你接到电话,要找的人不在,用英语问对方是谁并说那人不在。",
|
||||
"itemIds": [
|
||||
"A1-U20-P03",
|
||||
"A1-U20-P04",
|
||||
"A1-U20-P05"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-S2",
|
||||
"title": "留言和回电",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U20-P06",
|
||||
"A1-U20-P07",
|
||||
"A1-U20-P08",
|
||||
"A1-U20-P11",
|
||||
"A1-U20-P12"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Can I leave a message? Sure. Can you ask Lucy to call me back? What's your mobile number? It's 139 5520 8864. OK. Or I'll call again later.",
|
||||
"question": "打电话的人希望露西做什么?",
|
||||
"options": [
|
||||
"给他回电话",
|
||||
"发邮件",
|
||||
"去找他"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Can I leave a message?"
|
||||
},
|
||||
{
|
||||
"text": "Can you call me back?"
|
||||
},
|
||||
{
|
||||
"text": "What's your mobile number?"
|
||||
},
|
||||
{
|
||||
"text": "I'll call again later."
|
||||
},
|
||||
{
|
||||
"text": "Please text me."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Phone messages\nFor: Mr Brown\nFrom: Sara, the owner of Sun Hair\nMessage: Your haircut is on Friday at 3 pm. Please call her back.\n-\nFor: Ella\nFrom: Kate, the College English Club\nMessage: The group lesson is on Monday. Your test is next week. Please text her or send an email or mail. Her number is 136 2210 5547.",
|
||||
"question": "艾拉的考试在什么时候?",
|
||||
"options": [
|
||||
"下周",
|
||||
"周一",
|
||||
"周五"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问能不能留言,请对方回电话。",
|
||||
"example": "Can I leave a message? Can you call me back?",
|
||||
"itemIds": [
|
||||
"A1-U20-P06",
|
||||
"A1-U20-P07"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语给不在的朋友留言:说你是谁,请他回电话,并留下手机号。",
|
||||
"itemIds": [
|
||||
"A1-U20-P01",
|
||||
"A1-U20-P06",
|
||||
"A1-U20-P07",
|
||||
"A1-U20-P08"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-S3",
|
||||
"title": "电话里的问题",
|
||||
"minutes": 11,
|
||||
"itemIds": [
|
||||
"A1-U20-P09",
|
||||
"A1-U20-P10"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Hello? Is that Max? Sorry, wrong number. Oh, sorry! ... Hello, Max? Sorry, I can't hear you. Can you speak slowly?",
|
||||
"question": "第一个电话出了什么问题?",
|
||||
"options": [
|
||||
"打错了",
|
||||
"听不见",
|
||||
"没人接"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Sorry, I can't hear you."
|
||||
},
|
||||
{
|
||||
"text": "Sorry, wrong number."
|
||||
},
|
||||
{
|
||||
"text": "Can you spell your name, please?"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "Phone help for your trip\nCan't hear the worker on the telephone? Say \"Sorry, I can't hear you.\" Ask them to speak slowly or to spell a word.\nWrong number? Say sorry. Never mind!\nA course or a class on the phone is not easy. Text or email is OK too.",
|
||||
"question": "电话里听不清时可以怎么做?",
|
||||
"options": [
|
||||
"请对方慢点说或拼出来",
|
||||
"马上挂电话",
|
||||
"大声喊"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:说你听不见,请对方慢点说。",
|
||||
"example": "Sorry, I can't hear you. Can you speak slowly?",
|
||||
"itemIds": [
|
||||
"A1-U20-P09"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "有人打错电话找“Max”,用英语礼貌地告诉他打错了。",
|
||||
"itemIds": [
|
||||
"A1-U20-P10",
|
||||
"A1-U20-P03"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U20-C1",
|
||||
"kind": "main",
|
||||
"title": "打电话到朋友家",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "朋友的家人",
|
||||
"setting": "你打电话找朋友汤姆,接电话的是他的家人。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello?",
|
||||
"goal": "说你是谁",
|
||||
"model": "Hello, this is Chen.",
|
||||
"zh": "你好,我是陈。",
|
||||
"itemIds": [
|
||||
"A1-U20-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Hi, Chen.",
|
||||
"goal": "问能不能和汤姆通话",
|
||||
"model": "Can I speak to Tom, please?",
|
||||
"zh": "请问汤姆在吗?",
|
||||
"itemIds": [
|
||||
"A1-U20-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sorry, he's not here right now.",
|
||||
"goal": "问能不能留言",
|
||||
"model": "Can I leave a message?",
|
||||
"zh": "我可以留个言吗?",
|
||||
"itemIds": [
|
||||
"A1-U20-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure.",
|
||||
"goal": "请他回电话",
|
||||
"model": "Can you ask him to call me back?",
|
||||
"zh": "你能让他给我回电话吗?",
|
||||
"itemIds": [
|
||||
"A1-U20-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK. What's your mobile number?",
|
||||
"goal": "留下号码,说晚点再打",
|
||||
"model": "It's 138 2046 7751. Or I'll call again later.",
|
||||
"zh": "138 2046 7751。或者我晚点再打。",
|
||||
"itemIds": [
|
||||
"A1-U20-P11"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-C2",
|
||||
"kind": "variant",
|
||||
"title": "接电话",
|
||||
"learnerRole": "接电话的人",
|
||||
"aiRole": "打电话来的人",
|
||||
"setting": "你在朋友家,电话响了,你帮忙接电话。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello, can I speak to Lily?",
|
||||
"goal": "问对方是谁",
|
||||
"model": "Who's calling, please?",
|
||||
"zh": "请问您是哪位?",
|
||||
"itemIds": [
|
||||
"A1-U20-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "This is Ben, from her English class.",
|
||||
"goal": "请对方稍等",
|
||||
"model": "Hold on, please.",
|
||||
"zh": "请稍等。",
|
||||
"itemIds": [
|
||||
"A1-U20-P04"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK.",
|
||||
"goal": "说她现在不在",
|
||||
"model": "Sorry, she's not here right now.",
|
||||
"zh": "抱歉,她现在不在。",
|
||||
"itemIds": [
|
||||
"A1-U20-P05"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh. Can she call me back?",
|
||||
"goal": "问对方的手机号",
|
||||
"model": "Sure. What's your mobile number?",
|
||||
"zh": "好的。你的手机号是多少?",
|
||||
"itemIds": [
|
||||
"A1-U20-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "It's 136... hello?",
|
||||
"goal": "说听不见",
|
||||
"model": "Sorry, I can't hear you.",
|
||||
"zh": "抱歉,我听不见你说话。",
|
||||
"itemIds": [
|
||||
"A1-U20-P09"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-C3",
|
||||
"kind": "variant",
|
||||
"title": "打错电话和发短信",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "陌生人/朋友",
|
||||
"setting": "你打给酒店想确认地址,结果先打错,再打给朋友。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hello?",
|
||||
"goal": "说你是谁,问能不能和 Sam 通话",
|
||||
"model": "Hello, this is Chen. Can I speak to Sam?",
|
||||
"zh": "你好,我是陈。能和 Sam 通话吗?",
|
||||
"itemIds": [
|
||||
"A1-U20-P01",
|
||||
"A1-U20-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sam? There's no Sam here.",
|
||||
"goal": "说抱歉,打错了",
|
||||
"model": "Oh, sorry. Wrong number.",
|
||||
"zh": "哦,抱歉,打错了。",
|
||||
"itemIds": [
|
||||
"A1-U20-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "No problem. Bye.",
|
||||
"goal": "说你晚点再打",
|
||||
"model": "OK, I'll call again later.",
|
||||
"zh": "好,我晚点再打。",
|
||||
"itemIds": [
|
||||
"A1-U20-P11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "(Sam) Hi Chen! Sorry, I'm busy right now.",
|
||||
"goal": "请他发短信给你地址",
|
||||
"model": "Please text me the address.",
|
||||
"zh": "请把地址发短信给我。",
|
||||
"itemIds": [
|
||||
"A1-U20-P12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Sure. I can text you in five minutes.",
|
||||
"goal": "请他之后给你回电话",
|
||||
"model": "Thanks. Can you call me back tonight?",
|
||||
"zh": "谢谢。你今晚能给我回电话吗?",
|
||||
"itemIds": [
|
||||
"A1-U20-P07"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U20-T1",
|
||||
"sceneId": "A1-U20-C1",
|
||||
"goal": "打电话找人,对方不在时留言",
|
||||
"itemIds": [
|
||||
"A1-U20-P01",
|
||||
"A1-U20-P02",
|
||||
"A1-U20-P06",
|
||||
"A1-U20-P07",
|
||||
"A1-U20-P11"
|
||||
],
|
||||
"slots": [
|
||||
"你是谁",
|
||||
"找谁",
|
||||
"留言内容"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-T2",
|
||||
"sceneId": "A1-U20-C2",
|
||||
"goal": "帮别人接电话并记下对方信息",
|
||||
"itemIds": [
|
||||
"A1-U20-P03",
|
||||
"A1-U20-P04",
|
||||
"A1-U20-P05",
|
||||
"A1-U20-P08",
|
||||
"A1-U20-P09"
|
||||
],
|
||||
"slots": [
|
||||
"对方是谁",
|
||||
"手机号"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-T3",
|
||||
"sceneId": "A1-U20-C3",
|
||||
"goal": "处理打错电话,并请朋友发短信",
|
||||
"itemIds": [
|
||||
"A1-U20-P01",
|
||||
"A1-U20-P02",
|
||||
"A1-U20-P10",
|
||||
"A1-U20-P11",
|
||||
"A1-U20-P12",
|
||||
"A1-U20-P07"
|
||||
],
|
||||
"slots": [
|
||||
"打错",
|
||||
"短信",
|
||||
"回电"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U20-M1",
|
||||
"mode": "listening",
|
||||
"title": "一条电话留言",
|
||||
"text": "Hello, this is a telephone message for Mr Brown. This is Chen from the hotel. Your phone rang, but I couldn't reach you, so I'll leave a short message. Sorry if my voice is not clear. I'm calling about your reservation for next week. My grandma and grandpa will visit, so I moved your room. Please call me back for a short conversation, or text me. My mobile number is 138 — let me repeat, one, three, eight. I have an English class and a haircut this afternoon, so if I don't answer, call again later. Never mind if it's late. Thank you. Bye.",
|
||||
"glosses": {
|
||||
"reach": "联系到",
|
||||
"moved": "换了",
|
||||
"couldn't": "不能(过去式)",
|
||||
"clear": "清楚的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这条留言是谁留给谁的?",
|
||||
"options": [
|
||||
"Chen 留给 Mr Brown",
|
||||
"Mr Brown 留给 Chen",
|
||||
"Anna 留给 Ben"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "留言的人希望对方做什么?",
|
||||
"options": [
|
||||
"给他回电话",
|
||||
"来酒店",
|
||||
"发邮件"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U20-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:打电话留言",
|
||||
"text": "Chen: Hello, this is Chen. Can I speak to Mr Brown?\nWorker: Hold on, please... Sorry, he's not here right now. This is his co-worker.\nChen: Can I leave a message? I couldn't reach him by mail.\nWorker: Of course. I'm the office worker today.\nChen: Please ask him to call me back. It's about a college course and a club test next week.\nWorker: Sorry, I can't hear you. Your voice is not clear. Can you say the number again?\nChen: Sure. My mobile is 139... I'll text you too. Is Mr Brown the owner?\nWorker: Yes, he owns the group. Never mind, I'll tell him after his class.\nChen: Thank you. When is he back?\nWorker: Maybe this afternoon, after his lesson.\nChen: OK, I'll call again later. Goodbye.",
|
||||
"glosses": {
|
||||
"staff": "员工,服务员",
|
||||
"goodbye": "再见",
|
||||
"co-worker": "同事",
|
||||
"owns": "拥有",
|
||||
"office": "办公室",
|
||||
"reach": "联系到",
|
||||
"clear": "清楚的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Mr Brown 在吗?",
|
||||
"options": [
|
||||
"不在",
|
||||
"在,正在开会",
|
||||
"在,马上来"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "Chen 希望 Mr Brown 做什么?",
|
||||
"options": [
|
||||
"给他回电话",
|
||||
"来酒店",
|
||||
"发邮件给他"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "voicemail",
|
||||
"reason": "不在 CEFR-J 表中;A1 用 leave a message 表达"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,953 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "A1-U21",
|
||||
"revision": 1,
|
||||
"level": "A1",
|
||||
"title": "周末计划",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说将要做的事、邀请朋友",
|
||||
"status": "draft",
|
||||
"meta": {
|
||||
"draftedBy": "claude-opus-5",
|
||||
"draftedAt": "2026-09-17",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5",
|
||||
"CEFR-J Grammar Profile 20180315"
|
||||
],
|
||||
"reviews": [],
|
||||
"frozenAt": null
|
||||
},
|
||||
"names": [],
|
||||
"grammar": [
|
||||
{
|
||||
"id": "61",
|
||||
"use": "active",
|
||||
"reason": "用 What are you doing this weekend? 说已经定好的安排"
|
||||
},
|
||||
{
|
||||
"id": "142",
|
||||
"use": "active",
|
||||
"reason": "Would you like to ...? 是邀请别人时的固定句型"
|
||||
},
|
||||
{
|
||||
"id": "120",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "123",
|
||||
"use": "active"
|
||||
},
|
||||
{
|
||||
"id": "150",
|
||||
"use": "active"
|
||||
}
|
||||
],
|
||||
"coreItems": [
|
||||
{
|
||||
"id": "A1-U21-P01",
|
||||
"type": "phrase",
|
||||
"en": "What are you doing this weekend?",
|
||||
"zh": "你这个周末做什么?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"what are you doing"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "What are you doing on Saturday?",
|
||||
"zh": "你周六做什么?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问朋友这个周末做什么。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P02",
|
||||
"type": "phrase",
|
||||
"en": "I'm going to the beach.",
|
||||
"zh": "我要去海滩。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"going to the"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'm going to the mountains with my family.",
|
||||
"zh": "我要和家人去山里。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说你周六要去海滩。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P03",
|
||||
"type": "phrase",
|
||||
"en": "Do you want to come?",
|
||||
"zh": "你想一起来吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"want to come"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "We're going to a concert. Do you want to come?",
|
||||
"zh": "我们要去听音乐会,你想来吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "问朋友想不想一起来。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P04",
|
||||
"type": "phrase",
|
||||
"en": "Would you like to join us?",
|
||||
"zh": "你愿意和我们一起吗?",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"would you like to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Would you like to play tennis?",
|
||||
"zh": "你想打网球吗?"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "礼貌地邀请朋友加入你们。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P05",
|
||||
"type": "phrase",
|
||||
"en": "That sounds great!",
|
||||
"zh": "听起来很棒!",
|
||||
"sceneWord": true,
|
||||
"sceneReason": "sound 为 A2 动词,但回应邀请时最自然的说法就是它",
|
||||
"match": [
|
||||
[
|
||||
"sounds great",
|
||||
"sounds good",
|
||||
"sounds fun"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "A picnic? That sounds good!",
|
||||
"zh": "野餐?听起来不错!"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "朋友邀请你,你很高兴地回应。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P06",
|
||||
"type": "phrase",
|
||||
"en": "I'd love to.",
|
||||
"zh": "我很乐意。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"i'd love to"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I'd love to come!",
|
||||
"zh": "我很想来!"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "接受朋友的邀请。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P07",
|
||||
"type": "phrase",
|
||||
"en": "Sorry, I can't.",
|
||||
"zh": "抱歉,我去不了。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"sorry, i can't"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Sorry, I can't. I'm busy on Sunday.",
|
||||
"zh": "抱歉,我去不了,我周日没空。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "礼貌地拒绝邀请。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P08",
|
||||
"type": "phrase",
|
||||
"en": "Maybe next time.",
|
||||
"zh": "下次吧。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"next time"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "OK, maybe next time.",
|
||||
"zh": "好吧,下次吧。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "拒绝邀请后说下次吧。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P09",
|
||||
"type": "phrase",
|
||||
"en": "Let's go together.",
|
||||
"zh": "我们一起去吧。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"go together"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Let's go together on Sunday.",
|
||||
"zh": "我们周日一起去吧。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "提议和朋友一起去。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P10",
|
||||
"type": "phrase",
|
||||
"en": "Let's play tennis.",
|
||||
"zh": "我们打网球吧。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"play tennis",
|
||||
"play football",
|
||||
"play basketball",
|
||||
"play the guitar",
|
||||
"play the piano"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "I play football on Sundays.",
|
||||
"zh": "我周日踢足球。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "约朋友打篮球。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P11",
|
||||
"type": "phrase",
|
||||
"en": "We can have a picnic.",
|
||||
"zh": "我们可以去野餐。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"have a picnic"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "We can have a picnic in the park.",
|
||||
"zh": "我们可以在公园野餐。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "提议去野餐。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-P12",
|
||||
"type": "phrase",
|
||||
"en": "It's fun.",
|
||||
"zh": "很好玩。",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"fun"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "Dancing is really fun.",
|
||||
"zh": "跳舞真的很好玩。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说爬山很好玩。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-W01",
|
||||
"type": "word",
|
||||
"en": "hobby",
|
||||
"zh": "爱好",
|
||||
"sceneWord": false,
|
||||
"match": [
|
||||
[
|
||||
"hobby",
|
||||
"hobbies"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"en": "My hobby is taking photos.",
|
||||
"zh": "我的爱好是拍照。"
|
||||
},
|
||||
"review": {
|
||||
"prompt": "说说你的爱好。"
|
||||
}
|
||||
}
|
||||
],
|
||||
"receptiveWords": [
|
||||
{
|
||||
"id": "A1-U21-R01",
|
||||
"en": "concert",
|
||||
"zh": "音乐会"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R02",
|
||||
"en": "football",
|
||||
"zh": "足球"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R03",
|
||||
"en": "basketball",
|
||||
"zh": "篮球"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R04",
|
||||
"en": "soccer",
|
||||
"zh": "足球(美式说法)"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R05",
|
||||
"en": "sport",
|
||||
"zh": "运动"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R06",
|
||||
"en": "game",
|
||||
"zh": "比赛;游戏"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R07",
|
||||
"en": "team",
|
||||
"zh": "队"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R08",
|
||||
"en": "player",
|
||||
"zh": "选手"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R09",
|
||||
"en": "ball",
|
||||
"zh": "球"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R10",
|
||||
"en": "fan",
|
||||
"zh": "迷;粉丝"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R11",
|
||||
"en": "activity",
|
||||
"zh": "活动"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R12",
|
||||
"en": "art",
|
||||
"zh": "艺术"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R13",
|
||||
"en": "drama",
|
||||
"zh": "戏剧"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R14",
|
||||
"en": "theater",
|
||||
"zh": "剧院"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R15",
|
||||
"en": "event",
|
||||
"zh": "活动;项目"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R16",
|
||||
"en": "painting",
|
||||
"zh": "画;绘画"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R17",
|
||||
"en": "photo",
|
||||
"zh": "照片"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R18",
|
||||
"en": "piano",
|
||||
"zh": "钢琴"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R19",
|
||||
"en": "guitar",
|
||||
"zh": "吉他"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R20",
|
||||
"en": "singer",
|
||||
"zh": "歌手"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R21",
|
||||
"en": "musician",
|
||||
"zh": "音乐家"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R22",
|
||||
"en": "actor",
|
||||
"zh": "演员"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R23",
|
||||
"en": "band",
|
||||
"zh": "乐队"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R24",
|
||||
"en": "camp",
|
||||
"zh": "营地;露营"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R25",
|
||||
"en": "magazine",
|
||||
"zh": "杂志"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R26",
|
||||
"en": "mountain",
|
||||
"zh": "山"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R27",
|
||||
"en": "climb",
|
||||
"zh": "爬"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R28",
|
||||
"en": "exciting",
|
||||
"zh": "令人兴奋的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R29",
|
||||
"en": "excellent",
|
||||
"zh": "极好的"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R30",
|
||||
"en": "win",
|
||||
"zh": "赢"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R31",
|
||||
"en": "spend",
|
||||
"zh": "度过;花"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-R32",
|
||||
"en": "choose",
|
||||
"zh": "选择"
|
||||
}
|
||||
],
|
||||
"segments": [
|
||||
{
|
||||
"id": "A1-U21-S1",
|
||||
"title": "问周末安排",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U21-P01",
|
||||
"A1-U21-P02",
|
||||
"A1-U21-P12",
|
||||
"A1-U21-W01"
|
||||
],
|
||||
"listening": {
|
||||
"text": "What are you doing this weekend? I'm going to the beach on Saturday. And on Sunday? I'm going to the mountains. I love climbing. It's fun!",
|
||||
"question": "说话人周日去哪里?",
|
||||
"options": [
|
||||
"山里",
|
||||
"海滩",
|
||||
"公园"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "What are you doing this weekend?"
|
||||
},
|
||||
{
|
||||
"text": "I'm going to the beach."
|
||||
},
|
||||
{
|
||||
"text": "It's fun."
|
||||
},
|
||||
{
|
||||
"text": "My hobby is taking photos.",
|
||||
"tip": "hobby 读 /ˈhɒbi/"
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "My hobbies\nMia: I play the piano and the guitar. I'm in a band, and I love music. My favorite singer is from London.\nLeo: I like sports. I play football and basketball. My team wins most games! I'm a big fan of soccer too.\nNina: I'm interested in art. I go to the theater and look at paintings. I read an art magazine every month.\nBen: Fishing and taking photos. I spend both days of the weekend at the river.",
|
||||
"question": "谁对艺术感兴趣?",
|
||||
"options": [
|
||||
"Nina",
|
||||
"Mia",
|
||||
"Leo"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:问朋友周末做什么,再说你要去海滩。",
|
||||
"example": "What are you doing this weekend? I'm going to the beach.",
|
||||
"itemIds": [
|
||||
"A1-U21-P01",
|
||||
"A1-U21-P02"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语说说你这个周末的安排和你的爱好。",
|
||||
"itemIds": [
|
||||
"A1-U21-P02",
|
||||
"A1-U21-W01",
|
||||
"A1-U21-P12"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-S2",
|
||||
"title": "邀请和回应",
|
||||
"minutes": 13,
|
||||
"itemIds": [
|
||||
"A1-U21-P03",
|
||||
"A1-U21-P04",
|
||||
"A1-U21-P05",
|
||||
"A1-U21-P06",
|
||||
"A1-U21-P07",
|
||||
"A1-U21-P08"
|
||||
],
|
||||
"listening": {
|
||||
"text": "We're going to a concert on Friday. Would you like to join us? That sounds great! I'd love to. And you, Sam? Do you want to come? Sorry, I can't. I'm busy. Maybe next time.",
|
||||
"question": "Sam 为什么不去?",
|
||||
"options": [
|
||||
"他没空",
|
||||
"他不喜欢音乐会",
|
||||
"他生病了"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Do you want to come?"
|
||||
},
|
||||
{
|
||||
"text": "Would you like to join us?",
|
||||
"tip": "Would you 连读成 /wʊdʒu/"
|
||||
},
|
||||
{
|
||||
"text": "That sounds great!"
|
||||
},
|
||||
{
|
||||
"text": "I'd love to."
|
||||
},
|
||||
{
|
||||
"text": "Sorry, I can't."
|
||||
},
|
||||
{
|
||||
"text": "Maybe next time."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "City events this weekend\nSaturday\n· Excellent music: a band and musicians from ten countries. 7 pm, City Park.\n· Kids' drama: a play by young actors. 3 pm, the theater.\nSunday\n· Football game: our city team and the Tokyo team. Come and watch the players! 2 pm.\n· Camp by the river: fishing and dancing activities. Choose one or do both!\nAn exciting weekend for everyone. Which event would you like to go to?",
|
||||
"question": "周六下午有什么活动?",
|
||||
"options": [
|
||||
"儿童戏剧",
|
||||
"足球比赛",
|
||||
"音乐会"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:邀请朋友一起来,再替他说“我很乐意”。",
|
||||
"example": "Would you like to join us? I'd love to.",
|
||||
"itemIds": [
|
||||
"A1-U21-P04",
|
||||
"A1-U21-P06"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "朋友邀请你周日去看足球,你没空,用英语礼貌拒绝。",
|
||||
"itemIds": [
|
||||
"A1-U21-P07",
|
||||
"A1-U21-P08"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-S3",
|
||||
"title": "一起去吧",
|
||||
"minutes": 12,
|
||||
"itemIds": [
|
||||
"A1-U21-P09",
|
||||
"A1-U21-P10",
|
||||
"A1-U21-P11"
|
||||
],
|
||||
"listening": {
|
||||
"text": "Let's play tennis on Saturday. Hmm, it's going to be sunny. We can have a picnic too. Great idea! Let's go together.",
|
||||
"question": "他们周六要做什么?",
|
||||
"options": [
|
||||
"打网球和野餐",
|
||||
"去海滩",
|
||||
"看音乐会"
|
||||
]
|
||||
},
|
||||
"speaking": [
|
||||
{
|
||||
"text": "Let's go together."
|
||||
},
|
||||
{
|
||||
"text": "Let's play tennis."
|
||||
},
|
||||
{
|
||||
"text": "We can have a picnic."
|
||||
},
|
||||
{
|
||||
"text": "Let's play the piano."
|
||||
}
|
||||
],
|
||||
"reading": {
|
||||
"text": "A note from Kate\nHi Lucy,\nAre you free on Sunday? Let's go to the river together. We can have a picnic and play ball. Bring your camera: you can take photos for your hobby. My brother is coming, and he's bringing his guitar. We can play the guitar and sing.\nIt's going to be fun! Text me.\nKate",
|
||||
"question": "Kate 的哥哥要带什么?",
|
||||
"options": [
|
||||
"吉他",
|
||||
"相机",
|
||||
"球"
|
||||
]
|
||||
},
|
||||
"writing": {
|
||||
"prompt": "写两句话:提议一起去,并说可以去野餐。",
|
||||
"example": "Let's go together. We can have a picnic.",
|
||||
"itemIds": [
|
||||
"A1-U21-P09",
|
||||
"A1-U21-P11"
|
||||
]
|
||||
},
|
||||
"independent": {
|
||||
"prompt": "用英语约朋友周末做一项运动,并说一起去。",
|
||||
"itemIds": [
|
||||
"A1-U21-P10",
|
||||
"A1-U21-P09"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"scenes": [
|
||||
{
|
||||
"id": "A1-U21-C1",
|
||||
"kind": "main",
|
||||
"title": "约朋友周末出去玩",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "朋友",
|
||||
"setting": "周五下班后,你和朋友聊周末的计划。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "So, what are you doing this weekend?",
|
||||
"goal": "说你要去海滩",
|
||||
"model": "I'm going to the beach.",
|
||||
"zh": "我要去海滩。",
|
||||
"itemIds": [
|
||||
"A1-U21-P02"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "The beach? That's nice.",
|
||||
"goal": "邀请朋友一起来",
|
||||
"model": "Do you want to come?",
|
||||
"zh": "你想一起来吗?",
|
||||
"itemIds": [
|
||||
"A1-U21-P03"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "That sounds great! What can we do there?",
|
||||
"goal": "说可以野餐,很好玩",
|
||||
"model": "We can have a picnic. It's fun.",
|
||||
"zh": "我们可以野餐,很好玩。",
|
||||
"itemIds": [
|
||||
"A1-U21-P11",
|
||||
"A1-U21-P12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Can my sister come too?",
|
||||
"goal": "说当然,一起去吧",
|
||||
"model": "Sure. Let's go together.",
|
||||
"zh": "当然。我们一起去吧。",
|
||||
"itemIds": [
|
||||
"A1-U21-P09"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Great. And after the picnic?",
|
||||
"goal": "提议打网球",
|
||||
"model": "Let's play tennis.",
|
||||
"zh": "我们打网球吧。",
|
||||
"itemIds": [
|
||||
"A1-U21-P10"
|
||||
]
|
||||
}
|
||||
],
|
||||
"practice": {}
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-C2",
|
||||
"kind": "variant",
|
||||
"title": "接受和拒绝邀请",
|
||||
"learnerRole": "你",
|
||||
"aiRole": "同事",
|
||||
"setting": "同事邀请你参加周末的活动。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! What are you doing this weekend?",
|
||||
"goal": "反问对方周末做什么",
|
||||
"model": "Nothing. What are you doing this weekend?",
|
||||
"zh": "没什么安排。你这个周末做什么?",
|
||||
"itemIds": [
|
||||
"A1-U21-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "We're going to a concert on Saturday. Would you like to join us?",
|
||||
"goal": "高兴地接受",
|
||||
"model": "That sounds great! I'd love to.",
|
||||
"zh": "听起来很棒!我很乐意。",
|
||||
"itemIds": [
|
||||
"A1-U21-P05",
|
||||
"A1-U21-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "And on Sunday, we're going to play football.",
|
||||
"goal": "说抱歉周日去不了",
|
||||
"model": "Sorry, I can't. I'm busy on Sunday.",
|
||||
"zh": "抱歉,我周日去不了,我没空。",
|
||||
"itemIds": [
|
||||
"A1-U21-P07"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Oh, OK.",
|
||||
"goal": "说下次吧",
|
||||
"model": "Maybe next time.",
|
||||
"zh": "下次吧。",
|
||||
"itemIds": [
|
||||
"A1-U21-P08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "What's your hobby, then?",
|
||||
"goal": "说你的爱好",
|
||||
"model": "My hobby is dancing.",
|
||||
"zh": "我的爱好是跳舞。",
|
||||
"itemIds": [
|
||||
"A1-U21-W01"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-C3",
|
||||
"kind": "variant",
|
||||
"title": "在青年旅舍认识新朋友",
|
||||
"learnerRole": "游客",
|
||||
"aiRole": "旅舍里的新朋友",
|
||||
"setting": "你住在青年旅舍,和另一位游客聊明天的安排。",
|
||||
"turns": [
|
||||
{
|
||||
"ai": "Hi! Are you here on holiday too?",
|
||||
"goal": "说是的,问对方周末做什么",
|
||||
"model": "Yes. What are you doing this weekend?",
|
||||
"zh": "是的。你这个周末做什么?",
|
||||
"itemIds": [
|
||||
"A1-U21-P01"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "I'm going to the mountains. Would you like to join us?",
|
||||
"goal": "高兴地接受",
|
||||
"model": "I'd love to!",
|
||||
"zh": "我很乐意!",
|
||||
"itemIds": [
|
||||
"A1-U21-P06"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "We can climb and have a picnic.",
|
||||
"goal": "说听起来很好玩",
|
||||
"model": "That sounds great! It's fun.",
|
||||
"zh": "听起来很棒!很好玩。",
|
||||
"itemIds": [
|
||||
"A1-U21-P05",
|
||||
"A1-U21-P12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "Do you like sports?",
|
||||
"goal": "说你的爱好是打网球",
|
||||
"model": "Yes. My hobby is tennis. Let's play tennis later.",
|
||||
"zh": "喜欢。我的爱好是网球。之后我们打网球吧。",
|
||||
"itemIds": [
|
||||
"A1-U21-W01",
|
||||
"A1-U21-P10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ai": "OK! See you tomorrow at seven.",
|
||||
"goal": "邀请对方晚上和你们吃饭,并说一起去",
|
||||
"model": "Great. Would you like to join us for dinner too? Let's go together.",
|
||||
"zh": "太好了。你也愿意和我们一起吃晚饭吗?我们一起去吧。",
|
||||
"itemIds": [
|
||||
"A1-U21-P04",
|
||||
"A1-U21-P09"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"id": "A1-U21-T1",
|
||||
"sceneId": "A1-U21-C1",
|
||||
"goal": "和朋友定下周末去海滩的计划",
|
||||
"itemIds": [
|
||||
"A1-U21-P02",
|
||||
"A1-U21-P03",
|
||||
"A1-U21-P11",
|
||||
"A1-U21-P12",
|
||||
"A1-U21-P09",
|
||||
"A1-U21-P10"
|
||||
],
|
||||
"slots": [
|
||||
"去哪里",
|
||||
"做什么",
|
||||
"谁一起"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-T2",
|
||||
"sceneId": "A1-U21-C2",
|
||||
"goal": "接受一个邀请,礼貌拒绝另一个邀请",
|
||||
"itemIds": [
|
||||
"A1-U21-P01",
|
||||
"A1-U21-P05",
|
||||
"A1-U21-P06",
|
||||
"A1-U21-P07",
|
||||
"A1-U21-P08",
|
||||
"A1-U21-W01"
|
||||
],
|
||||
"slots": [
|
||||
"接受",
|
||||
"拒绝",
|
||||
"爱好"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-T3",
|
||||
"sceneId": "A1-U21-C3",
|
||||
"goal": "和新朋友约好一起去山里",
|
||||
"itemIds": [
|
||||
"A1-U21-P01",
|
||||
"A1-U21-P06",
|
||||
"A1-U21-P05",
|
||||
"A1-U21-P12",
|
||||
"A1-U21-W01",
|
||||
"A1-U21-P10",
|
||||
"A1-U21-P09",
|
||||
"A1-U21-P04"
|
||||
],
|
||||
"slots": [
|
||||
"安排",
|
||||
"爱好",
|
||||
"约定"
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"id": "A1-U21-M1",
|
||||
"mode": "listening",
|
||||
"title": "朋友发来周末邀请",
|
||||
"text": "Hi everyone! What are you doing this weekend? There is a big sports event on Saturday. My favorite basketball team has a game, and their best player is excellent. I'm a big fan! We can watch the ball game, and it will be exciting. Do you want to come? On Sunday there is a concert with a band, a singer, a guitar and a piano — a real music activity. Or we can go to the theater for a drama, see some art and paintings, or climb a mountain and camp. Let's choose one and spend the day together. It's fun! Tell me if you'd like to join us.",
|
||||
"glosses": {},
|
||||
"questions": [
|
||||
{
|
||||
"type": "gist",
|
||||
"question": "这段话主要在做什么?",
|
||||
"options": [
|
||||
"邀请朋友周末一起活动",
|
||||
"问路",
|
||||
"报告问题"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "星期六有什么活动?",
|
||||
"options": [
|
||||
"篮球比赛",
|
||||
"看电影",
|
||||
"在家学习"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1-U21-M2",
|
||||
"mode": "reading",
|
||||
"title": "读一段对话:约周末踢球",
|
||||
"text": "Ben: What are you doing this weekend?\nMia: Nothing special. Why?\nBen: We're going to play soccer on Sunday. Our team needs one more player. Do you want to come?\nMia: That sounds great! I'd love to. I'm a big football fan. What time?\nBen: At two in the afternoon, in the park near the mountain.\nMia: I hope we win the game! Can I bring a friend? He plays the guitar in a band.\nBen: Sure! After the game, there's a concert and a drama at the theater. It's an exciting event.\nMia: Perfect. Let's spend the whole day together. I'll bring my camera for some photos.\nBen: Great idea. Choose good shoes — we can climb the hill too. It's an excellent activity!\nMia: Sounds fun. See you on Sunday!\nBen: See you!",
|
||||
"glosses": {
|
||||
"perfect": "完美的",
|
||||
"needs": "需要",
|
||||
"whole": "整个的"
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"type": "detail",
|
||||
"question": "他们周末打算做什么?",
|
||||
"options": [
|
||||
"踢足球",
|
||||
"打网球",
|
||||
"去海边"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "inference",
|
||||
"question": "从对话看,Mia 想不想去?",
|
||||
"options": [
|
||||
"想去,还想带朋友",
|
||||
"不想去",
|
||||
"还没决定"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"excluded": [
|
||||
{
|
||||
"en": "hiking",
|
||||
"reason": "A2;A1 用 go to the mountains / climb 表达"
|
||||
},
|
||||
{
|
||||
"en": "camping",
|
||||
"reason": "A2;A1 只作为 camp 理解"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,730 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"goal": "日常生活与旅行",
|
||||
"sources": [
|
||||
"CEFR-J Vocabulary Profile 1.5(東京外国語大学投野由紀夫研究室)",
|
||||
"CEFR-J Grammar Profile 20180315(東京外国語大学投野由紀夫研究室)"
|
||||
],
|
||||
"minimumPoolCoverage": 0.85,
|
||||
"alwaysAllowedWords": [
|
||||
"oh",
|
||||
"um",
|
||||
"ok",
|
||||
"okay",
|
||||
"wow",
|
||||
"mm",
|
||||
"hmm",
|
||||
"ah",
|
||||
"uh",
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"l",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z"
|
||||
],
|
||||
"names": [
|
||||
"tom",
|
||||
"mia",
|
||||
"alex",
|
||||
"shen",
|
||||
"anna",
|
||||
"ben",
|
||||
"lily",
|
||||
"sam",
|
||||
"emma",
|
||||
"leo",
|
||||
"nina",
|
||||
"david",
|
||||
"lucy",
|
||||
"mark",
|
||||
"sara",
|
||||
"jack",
|
||||
"kate",
|
||||
"max",
|
||||
"ella",
|
||||
"chen",
|
||||
"wang",
|
||||
"li",
|
||||
"zhang",
|
||||
"liu",
|
||||
"london",
|
||||
"paris",
|
||||
"tokyo",
|
||||
"sydney",
|
||||
"beijing",
|
||||
"shanghai",
|
||||
"china",
|
||||
"chinese",
|
||||
"english",
|
||||
"america",
|
||||
"american",
|
||||
"japan",
|
||||
"british",
|
||||
"rome",
|
||||
"bangkok",
|
||||
"singapore",
|
||||
"seoul",
|
||||
"berlin",
|
||||
"mr",
|
||||
"mrs",
|
||||
"ms",
|
||||
"dr",
|
||||
"usd",
|
||||
"cm",
|
||||
"km",
|
||||
"wi-fi",
|
||||
"wifi",
|
||||
"atm"
|
||||
],
|
||||
"levels": [
|
||||
{
|
||||
"id": "A0",
|
||||
"title": "零基础起步",
|
||||
"assessment": "A0/assessment.json",
|
||||
"units": [
|
||||
{
|
||||
"id": "a0-01",
|
||||
"file": "A0/a0-01.json",
|
||||
"title": "你好,我是……",
|
||||
"categories": [],
|
||||
"canDo": "介绍姓名并回应问候"
|
||||
},
|
||||
{
|
||||
"id": "a0-02",
|
||||
"file": "A0/a0-02.json",
|
||||
"title": "拼读我的名字",
|
||||
"categories": [],
|
||||
"canDo": "听懂拼写,并说出名字字母"
|
||||
},
|
||||
{
|
||||
"id": "a0-03",
|
||||
"file": "A0/a0-03.json",
|
||||
"title": "你好吗?",
|
||||
"categories": [],
|
||||
"canDo": "问候并表达简单状态"
|
||||
},
|
||||
{
|
||||
"id": "a0-04",
|
||||
"file": "A0/a0-04.json",
|
||||
"title": "数字与电话号码",
|
||||
"categories": [],
|
||||
"canDo": "说 0–10 与虚拟号码"
|
||||
},
|
||||
{
|
||||
"id": "a0-05",
|
||||
"file": "A0/a0-05.json",
|
||||
"title": "这是什么?",
|
||||
"categories": [],
|
||||
"canDo": "询问并说出常见物品"
|
||||
},
|
||||
{
|
||||
"id": "a0-06",
|
||||
"file": "A0/a0-06.json",
|
||||
"title": "我来自哪里?",
|
||||
"categories": [],
|
||||
"canDo": "说明来自哪里并反问"
|
||||
},
|
||||
{
|
||||
"id": "a0-07",
|
||||
"file": "A0/a0-07.json",
|
||||
"title": "我的家人",
|
||||
"categories": [],
|
||||
"canDo": "介绍一位家人"
|
||||
},
|
||||
{
|
||||
"id": "a0-08",
|
||||
"file": "A0/a0-08.json",
|
||||
"title": "星期与时间",
|
||||
"categories": [],
|
||||
"canDo": "说今天和整点"
|
||||
},
|
||||
{
|
||||
"id": "a0-09",
|
||||
"file": "A0/a0-09.json",
|
||||
"title": "我喜欢……",
|
||||
"categories": [],
|
||||
"canDo": "表达喜好并回答"
|
||||
},
|
||||
{
|
||||
"id": "a0-10",
|
||||
"file": "A0/a0-10.json",
|
||||
"title": "A0 综合任务",
|
||||
"categories": [],
|
||||
"canDo": "没听清时请对方重复,并完成自我介绍、喜好和反问"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "A1",
|
||||
"title": "固定的生活与旅行互动",
|
||||
"coreRange": [
|
||||
10,
|
||||
15
|
||||
],
|
||||
"receptiveRange": [
|
||||
20,
|
||||
35
|
||||
],
|
||||
"units": [
|
||||
{
|
||||
"id": "A1-U01",
|
||||
"file": "A1/A1-U01.json",
|
||||
"title": "开口求助与数字",
|
||||
"categories": [
|
||||
"旅行",
|
||||
"日常"
|
||||
],
|
||||
"canDo": "没听懂时礼貌求助,说 11–100 的数字,拼读字母"
|
||||
},
|
||||
{
|
||||
"id": "A1-U02",
|
||||
"file": "A1/A1-U02.json",
|
||||
"title": "价格与付款",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "问价、说 100 以内的金额、选择现金或刷卡"
|
||||
},
|
||||
{
|
||||
"id": "A1-U03",
|
||||
"file": "A1/A1-U03.json",
|
||||
"title": "在咖啡店点单",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "点饮料、说大小杯、礼貌确认"
|
||||
},
|
||||
{
|
||||
"id": "A1-U04",
|
||||
"file": "A1/A1-U04.json",
|
||||
"title": "问路",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "问地点、听懂左右和距离"
|
||||
},
|
||||
{
|
||||
"id": "A1-U05",
|
||||
"file": "A1/A1-U05.json",
|
||||
"title": "在餐厅点餐",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "点餐、问推荐、要账单"
|
||||
},
|
||||
{
|
||||
"id": "A1-U06",
|
||||
"file": "A1/A1-U06.json",
|
||||
"title": "家人与住处",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "介绍家人,说住在哪里和家里有什么"
|
||||
},
|
||||
{
|
||||
"id": "A1-U07",
|
||||
"file": "A1/A1-U07.json",
|
||||
"title": "公交与地铁",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "买票、问站点、确认下车"
|
||||
},
|
||||
{
|
||||
"id": "A1-U08",
|
||||
"file": "A1/A1-U08.json",
|
||||
"title": "超市与便利店",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "找商品、说数量"
|
||||
},
|
||||
{
|
||||
"id": "A1-U09",
|
||||
"file": "A1/A1-U09.json",
|
||||
"title": "描述人和感受",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说一个人的外貌和性格,说自己的心情"
|
||||
},
|
||||
{
|
||||
"id": "A1-U10",
|
||||
"file": "A1/A1-U10.json",
|
||||
"title": "打车",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说目的地、确认价格或路线"
|
||||
},
|
||||
{
|
||||
"id": "A1-U11",
|
||||
"file": "A1/A1-U11.json",
|
||||
"title": "我的一天",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说作息和常做的事"
|
||||
},
|
||||
{
|
||||
"id": "A1-U12",
|
||||
"file": "A1/A1-U12.json",
|
||||
"title": "买衣服",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "问尺码和颜色、试穿"
|
||||
},
|
||||
{
|
||||
"id": "A1-U13",
|
||||
"file": "A1/A1-U13.json",
|
||||
"title": "日期与约时间",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说日期、约见面、改时间"
|
||||
},
|
||||
{
|
||||
"id": "A1-U14",
|
||||
"file": "A1/A1-U14.json",
|
||||
"title": "在机场",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "值机、问登机口、回答入境的简单问题"
|
||||
},
|
||||
{
|
||||
"id": "A1-U15",
|
||||
"file": "A1/A1-U15.json",
|
||||
"title": "入住酒店",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说明预订信息、办理入住"
|
||||
},
|
||||
{
|
||||
"id": "A1-U16",
|
||||
"file": "A1/A1-U16.json",
|
||||
"title": "天气与穿着",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "问天气、说该穿什么"
|
||||
},
|
||||
{
|
||||
"id": "A1-U17",
|
||||
"file": "A1/A1-U17.json",
|
||||
"title": "酒店里的需求",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "要物品、报告简单问题"
|
||||
},
|
||||
{
|
||||
"id": "A1-U18",
|
||||
"file": "A1/A1-U18.json",
|
||||
"title": "药店与不舒服",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说一个症状、听懂基本建议"
|
||||
},
|
||||
{
|
||||
"id": "A1-U19",
|
||||
"file": "A1/A1-U19.json",
|
||||
"title": "遇到麻烦时求助",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说明遇到的麻烦、请求帮忙、感谢与回应"
|
||||
},
|
||||
{
|
||||
"id": "A1-U20",
|
||||
"file": "A1/A1-U20.json",
|
||||
"title": "打电话与留言",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "留下姓名、号码和简短信息"
|
||||
},
|
||||
{
|
||||
"id": "A1-U21",
|
||||
"file": "A1/A1-U21.json",
|
||||
"title": "周末计划",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说将要做的事、邀请朋友"
|
||||
}
|
||||
],
|
||||
"materials": {
|
||||
"count": [
|
||||
2,
|
||||
2
|
||||
],
|
||||
"listeningWords": [
|
||||
60,
|
||||
120
|
||||
],
|
||||
"readingWords": [
|
||||
80,
|
||||
150
|
||||
],
|
||||
"unknownTolerance": 0.08
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "A2",
|
||||
"title": "讲经历、做安排、处理小问题",
|
||||
"coreRange": [
|
||||
10,
|
||||
15
|
||||
],
|
||||
"receptiveRange": [
|
||||
40,
|
||||
65
|
||||
],
|
||||
"materials": {
|
||||
"count": [
|
||||
2,
|
||||
3
|
||||
],
|
||||
"listeningWords": [
|
||||
120,
|
||||
280
|
||||
],
|
||||
"readingWords": [
|
||||
150,
|
||||
250
|
||||
]
|
||||
},
|
||||
"units": [
|
||||
{
|
||||
"id": "A2-U01",
|
||||
"file": "A2/A2-U01.json",
|
||||
"title": "上个周末",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "用过去时讲做了什么"
|
||||
},
|
||||
{
|
||||
"id": "A2-U02",
|
||||
"file": "A2/A2-U02.json",
|
||||
"title": "上一次旅行",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "讲去了哪里、怎么样、感受如何"
|
||||
},
|
||||
{
|
||||
"id": "A2-U03",
|
||||
"file": "A2/A2-U03.json",
|
||||
"title": "预订酒店与餐厅",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "电话或当面预订:日期、人数、要求"
|
||||
},
|
||||
{
|
||||
"id": "A2-U04",
|
||||
"file": "A2/A2-U04.json",
|
||||
"title": "改期与取消",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说明原因、提出新时间"
|
||||
},
|
||||
{
|
||||
"id": "A2-U05",
|
||||
"file": "A2/A2-U05.json",
|
||||
"title": "餐厅里的问题",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说明忌口或过敏、菜品有问题、分开结账"
|
||||
},
|
||||
{
|
||||
"id": "A2-U06",
|
||||
"file": "A2/A2-U06.json",
|
||||
"title": "比较与选择",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "比较商品、房间或路线并做选择"
|
||||
},
|
||||
{
|
||||
"id": "A2-U07",
|
||||
"file": "A2/A2-U07.json",
|
||||
"title": "退换货",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "说明问题、要求退换"
|
||||
},
|
||||
{
|
||||
"id": "A2-U08",
|
||||
"file": "A2/A2-U08.json",
|
||||
"title": "行程出问题",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "误车、延误、换乘、改票"
|
||||
},
|
||||
{
|
||||
"id": "A2-U09",
|
||||
"file": "A2/A2-U09.json",
|
||||
"title": "住宿里的问题",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "报修、换房、提出要求"
|
||||
},
|
||||
{
|
||||
"id": "A2-U10",
|
||||
"file": "A2/A2-U10.json",
|
||||
"title": "看病",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说症状和持续时间、听懂用药说明"
|
||||
},
|
||||
{
|
||||
"id": "A2-U11",
|
||||
"file": "A2/A2-U11.json",
|
||||
"title": "聊兴趣与家乡",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "围绕一个话题连续聊几轮"
|
||||
},
|
||||
{
|
||||
"id": "A2-U12",
|
||||
"file": "A2/A2-U12.json",
|
||||
"title": "建议与意见",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "给建议、征求意见"
|
||||
},
|
||||
{
|
||||
"id": "A2-U13",
|
||||
"file": "A2/A2-U13.json",
|
||||
"title": "介绍城市与景点",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "描述地方、推荐去处"
|
||||
},
|
||||
{
|
||||
"id": "A2-U14",
|
||||
"file": "A2/A2-U14.json",
|
||||
"title": "丢东西与求助",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "在服务台说明丢了什么、在哪里丢的"
|
||||
},
|
||||
{
|
||||
"id": "A2-U15",
|
||||
"file": "A2/A2-U15.json",
|
||||
"title": "消息与短邮件",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "写邀请、感谢、说明迟到"
|
||||
}
|
||||
],
|
||||
"openTaskRubric": true
|
||||
},
|
||||
{
|
||||
"id": "B1",
|
||||
"title": "独立应对大多数情况",
|
||||
"coreRange": [
|
||||
10,
|
||||
15
|
||||
],
|
||||
"receptiveRange": [
|
||||
70,
|
||||
110
|
||||
],
|
||||
"materials": {
|
||||
"count": [
|
||||
2,
|
||||
3
|
||||
],
|
||||
"listeningWords": [
|
||||
250,
|
||||
550
|
||||
],
|
||||
"readingWords": [
|
||||
300,
|
||||
500
|
||||
]
|
||||
},
|
||||
"units": [
|
||||
{
|
||||
"id": "B1-U01",
|
||||
"file": "B1/B1-U01.json",
|
||||
"title": "讲一段完整的旅行经历",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "按顺序叙述,加入背景和感受"
|
||||
},
|
||||
{
|
||||
"id": "B1-U02",
|
||||
"file": "B1/B1-U02.json",
|
||||
"title": "规划行程",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "比较交通和住宿方案,说明理由和预算"
|
||||
},
|
||||
{
|
||||
"id": "B1-U03",
|
||||
"file": "B1/B1-U03.json",
|
||||
"title": "入境与行李",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "回答入境问题,报告行李延误或丢失"
|
||||
},
|
||||
{
|
||||
"id": "B1-U04",
|
||||
"file": "B1/B1-U04.json",
|
||||
"title": "听懂广播与讲解",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "抓住车站、机场广播和导览讲解的要点"
|
||||
},
|
||||
{
|
||||
"id": "B1-U05",
|
||||
"file": "B1/B1-U05.json",
|
||||
"title": "紧急情况",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "证件或钱包丢失、报警、求助"
|
||||
},
|
||||
{
|
||||
"id": "B1-U06",
|
||||
"file": "B1/B1-U06.json",
|
||||
"title": "就医",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "说明症状变化和病史,理解医嘱要点"
|
||||
},
|
||||
{
|
||||
"id": "B1-U07",
|
||||
"file": "B1/B1-U07.json",
|
||||
"title": "投诉与协商",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "要求退款或补偿,谈出解决办法"
|
||||
},
|
||||
{
|
||||
"id": "B1-U08",
|
||||
"file": "B1/B1-U08.json",
|
||||
"title": "租住与长住",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "沟通租房或短租,理解规则要点"
|
||||
},
|
||||
{
|
||||
"id": "B1-U09",
|
||||
"file": "B1/B1-U09.json",
|
||||
"title": "生活服务",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "办银行和手机卡业务、寄收快递、预约维修"
|
||||
},
|
||||
{
|
||||
"id": "B1-U10",
|
||||
"file": "B1/B1-U10.json",
|
||||
"title": "观点与理由",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "推荐并说明理由,回应不同意见"
|
||||
},
|
||||
{
|
||||
"id": "B1-U11",
|
||||
"file": "B1/B1-U11.json",
|
||||
"title": "习俗与文化",
|
||||
"categories": [
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "谈小费、礼仪和节日,比较不同做法"
|
||||
},
|
||||
{
|
||||
"id": "B1-U12",
|
||||
"file": "B1/B1-U12.json",
|
||||
"title": "深入聊天",
|
||||
"categories": [
|
||||
"日常"
|
||||
],
|
||||
"canDo": "聊经历、计划和感受,维持 5 分钟对话"
|
||||
},
|
||||
{
|
||||
"id": "B1-U13",
|
||||
"file": "B1/B1-U13.json",
|
||||
"title": "读懂生活文本",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "读懂评价、说明、规则和通知"
|
||||
},
|
||||
{
|
||||
"id": "B1-U14",
|
||||
"file": "B1/B1-U14.json",
|
||||
"title": "写说明与投诉",
|
||||
"categories": [
|
||||
"日常",
|
||||
"旅行"
|
||||
],
|
||||
"canDo": "写邮件说明经过并提出要求"
|
||||
}
|
||||
],
|
||||
"openTaskRubric": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/// Frozen A0 v1 upgrade denominator from A0-STAGE-STANDARD.md.
|
||||
/// IDs are intentionally separate from generated wording and never change
|
||||
/// when a course uses an accepted slot value or contraction.
|
||||
final Map<String, String> a0CoreItems = Map.unmodifiable({
|
||||
for (final entry in _numbered('A0-W', const [
|
||||
'zero',
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
'Sunday',
|
||||
'book',
|
||||
'phone',
|
||||
'pen',
|
||||
'bag',
|
||||
'key',
|
||||
'water',
|
||||
'mother',
|
||||
'father',
|
||||
'sister',
|
||||
'brother',
|
||||
'coffee',
|
||||
'tea',
|
||||
'music',
|
||||
'movies',
|
||||
'good',
|
||||
'okay',
|
||||
'tired',
|
||||
'hello',
|
||||
'hi',
|
||||
'yes',
|
||||
'no',
|
||||
'thanks',
|
||||
]))
|
||||
entry.key: entry.value,
|
||||
for (final entry in _numbered('A0-P', const [
|
||||
"I'm [name].",
|
||||
"What's your name?",
|
||||
'Nice to meet you.',
|
||||
'How do you spell that?',
|
||||
'How are you?',
|
||||
"I'm [state].",
|
||||
"What's your phone number?",
|
||||
'My number is [digits].',
|
||||
"What's this?",
|
||||
"It's a [object].",
|
||||
'Where are you from?',
|
||||
"I'm from [place].",
|
||||
'This is my [person].',
|
||||
'What day is it today?',
|
||||
"It's [weekday].",
|
||||
'What time is it?',
|
||||
"It's [hour] o'clock.",
|
||||
'I like [thing].',
|
||||
'Do you like [thing]?',
|
||||
'Please say that again.',
|
||||
]))
|
||||
entry.key: entry.value,
|
||||
});
|
||||
|
||||
Iterable<MapEntry<String, String>> _numbered(
|
||||
String prefix,
|
||||
List<String> values,
|
||||
) sync* {
|
||||
for (var index = 0; index < values.length; index++) {
|
||||
yield MapEntry(
|
||||
'$prefix${(index + 1).toString().padLeft(2, '0')}',
|
||||
values[index],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CoreReviewTemplate {
|
||||
const CoreReviewTemplate({
|
||||
required this.prompt,
|
||||
required this.hint,
|
||||
required this.skill,
|
||||
});
|
||||
|
||||
final String prompt;
|
||||
final String hint;
|
||||
final String skill;
|
||||
}
|
||||
|
||||
/// Offline, reviewed prompts keep initial reviews usable without an AI service.
|
||||
/// The prompt supplies a meaning or situation, never the English target.
|
||||
CoreReviewTemplate coreReviewTemplate(String id) {
|
||||
final target = a0CoreItems[id] ?? id;
|
||||
const meanings = {
|
||||
'A0-W01': '零',
|
||||
'A0-W02': '一',
|
||||
'A0-W03': '二',
|
||||
'A0-W04': '三',
|
||||
'A0-W05': '四',
|
||||
'A0-W06': '五',
|
||||
'A0-W07': '六',
|
||||
'A0-W08': '七',
|
||||
'A0-W09': '八',
|
||||
'A0-W10': '九',
|
||||
'A0-W11': '十',
|
||||
'A0-W12': '星期一',
|
||||
'A0-W13': '星期二',
|
||||
'A0-W14': '星期三',
|
||||
'A0-W15': '星期四',
|
||||
'A0-W16': '星期五',
|
||||
'A0-W17': '星期六',
|
||||
'A0-W18': '星期日',
|
||||
'A0-W19': '书',
|
||||
'A0-W20': '电话',
|
||||
'A0-W21': '笔',
|
||||
'A0-W22': '包',
|
||||
'A0-W23': '钥匙',
|
||||
'A0-W24': '水',
|
||||
'A0-W25': '妈妈',
|
||||
'A0-W26': '爸爸',
|
||||
'A0-W27': '姐妹',
|
||||
'A0-W28': '兄弟',
|
||||
'A0-W29': '咖啡',
|
||||
'A0-W30': '茶',
|
||||
'A0-W31': '音乐',
|
||||
'A0-W32': '电影',
|
||||
'A0-W33': '好',
|
||||
'A0-W34': '还可以',
|
||||
'A0-W35': '累',
|
||||
'A0-W36': '你好',
|
||||
'A0-W37': '嗨',
|
||||
'A0-W38': '是',
|
||||
'A0-W39': '不',
|
||||
'A0-W40': '谢谢',
|
||||
};
|
||||
final phrasePrompt = switch (id) {
|
||||
'A0-P01' => '向新同学介绍你的名字。',
|
||||
'A0-P02' => '问对方叫什么名字。',
|
||||
'A0-P03' => '对初次见面的人说“很高兴认识你”。',
|
||||
'A0-P04' => '问对方一个词怎样拼写。',
|
||||
'A0-P05' => '问对方今天怎么样。',
|
||||
'A0-P06' => '说出你今天的状态。',
|
||||
'A0-P07' => '问对方的电话号码。',
|
||||
'A0-P08' => '报出一个虚构的三位号码。',
|
||||
'A0-P09' => '指着身边的东西问“这是什么”。',
|
||||
'A0-P10' => '说出“这是一件物品”。',
|
||||
'A0-P11' => '问对方来自哪里。',
|
||||
'A0-P12' => '说出你来自哪里。',
|
||||
'A0-P13' => '介绍一位家人。',
|
||||
'A0-P14' => '问今天星期几。',
|
||||
'A0-P15' => '说出今天星期几。',
|
||||
'A0-P16' => '问现在几点。',
|
||||
'A0-P17' => '说出一个整点时间。',
|
||||
'A0-P18' => '说出你喜欢的一样东西。',
|
||||
'A0-P19' => '问对方是否喜欢一样东西。',
|
||||
'A0-P20' => '请对方再说一次。',
|
||||
_ => null,
|
||||
};
|
||||
if (phrasePrompt != null) {
|
||||
return CoreReviewTemplate(
|
||||
prompt: phrasePrompt,
|
||||
hint: target,
|
||||
skill: '回忆表达',
|
||||
);
|
||||
}
|
||||
return CoreReviewTemplate(
|
||||
prompt: '把“${meanings[id] ?? '这个已学词'}”写成英文。',
|
||||
hint: target,
|
||||
skill: '词汇回忆',
|
||||
);
|
||||
}
|
||||
|
||||
/// Rotates an approved offline prompt family without changing the core target
|
||||
/// ID. These are a safe fallback when a dynamic AI variant is unavailable.
|
||||
CoreReviewTemplate coreReviewVariant(String id, int variantIndex) {
|
||||
final base = coreReviewTemplate(id);
|
||||
final lead = switch (variantIndex % 3) {
|
||||
0 => '换一个人物或地点,',
|
||||
1 => '在新的生活情境中,',
|
||||
_ => '不看上次答案,',
|
||||
};
|
||||
return CoreReviewTemplate(
|
||||
prompt: '$lead${base.prompt}',
|
||||
hint: base.hint,
|
||||
skill: base.skill,
|
||||
);
|
||||
}
|
||||
@@ -27,11 +27,10 @@ class AiConfigFile {
|
||||
(p) => p.name.toLowerCase() == providerStr.toLowerCase(),
|
||||
orElse: () => AiProviderType.compatible,
|
||||
);
|
||||
final effort =
|
||||
(json['reasoningEffort'] as String? ??
|
||||
json['reasoning_effort'] as String? ??
|
||||
'low')
|
||||
.trim();
|
||||
final effort = (json['reasoningEffort'] as String? ??
|
||||
json['reasoning_effort'] as String? ??
|
||||
'low')
|
||||
.trim();
|
||||
return AiConfigFile(
|
||||
provider: provider,
|
||||
endpoint: (json['endpoint'] as String? ?? '').trim(),
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'sherpa_stt_service.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
@@ -140,17 +139,12 @@ class AiService {
|
||||
if (key == null || key.isEmpty || uri == null) return null;
|
||||
|
||||
final ext = filePath.split('.').last.toLowerCase();
|
||||
final format =
|
||||
(ext == 'wav' || ext == 'mp3' || ext == 'm4a' || ext == 'aac')
|
||||
? ext
|
||||
: 'm4a';
|
||||
final format = (ext == 'wav' || ext == 'mp3' || ext == 'm4a' || ext == 'aac') ? ext : 'm4a';
|
||||
final base64Data = base64Encode(bytes);
|
||||
|
||||
try {
|
||||
if (provider == AiProviderType.gemini) {
|
||||
final mimeType = format == 'wav'
|
||||
? 'audio/wav'
|
||||
: (format == 'mp3' ? 'audio/mp3' : 'audio/mp4');
|
||||
final mimeType = format == 'wav' ? 'audio/wav' : (format == 'mp3' ? 'audio/mp3' : 'audio/mp4');
|
||||
final response = await _postJson(
|
||||
provider: provider,
|
||||
key: key,
|
||||
@@ -161,17 +155,21 @@ class AiService {
|
||||
{
|
||||
'parts': [
|
||||
{
|
||||
'text':
|
||||
'Transcribe the spoken English speech in this audio file accurately. Return ONLY the transcribed English words. If silence or unintelligible, output nothing.',
|
||||
'text': 'Transcribe the spoken English speech in this audio file accurately. Return ONLY the transcribed English words. If silence or unintelligible, output nothing.',
|
||||
},
|
||||
{
|
||||
'inline_data': {'mime_type': mimeType, 'data': base64Data},
|
||||
},
|
||||
],
|
||||
},
|
||||
'inline_data': {
|
||||
'mime_type': mimeType,
|
||||
'data': base64Data,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'generationConfig': {
|
||||
'thinkingConfig': {'thinkingBudget': 1024},
|
||||
'thinkingConfig': {
|
||||
'thinkingBudget': 1024,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -191,15 +189,17 @@ class AiService {
|
||||
'content': [
|
||||
{
|
||||
'type': 'text',
|
||||
'text':
|
||||
'Transcribe the spoken English speech in this audio file accurately. Output ONLY the raw transcribed English words without quotes, punctuation tags, or commentary. If silence or noise, return nothing.',
|
||||
'text': 'Transcribe the spoken English speech in this audio file accurately. Output ONLY the raw transcribed English words without quotes, punctuation tags, or commentary. If silence or noise, return nothing.',
|
||||
},
|
||||
{
|
||||
'type': 'input_audio',
|
||||
'input_audio': {'data': base64Data, 'format': format},
|
||||
},
|
||||
'input_audio': {
|
||||
'data': base64Data,
|
||||
'format': format,
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
'reasoning_effort': 'low',
|
||||
'temperature': 0.1,
|
||||
@@ -219,13 +219,7 @@ class AiService {
|
||||
}
|
||||
}
|
||||
|
||||
static bool _isHttpUri(Uri uri) =>
|
||||
uri.scheme == 'https' || uri.scheme == 'http';
|
||||
|
||||
static bool _isDeepSeek(Uri uri) {
|
||||
final host = uri.host.toLowerCase();
|
||||
return host == 'deepseek.com' || host.endsWith('.deepseek.com');
|
||||
}
|
||||
static bool _isHttpUri(Uri uri) => uri.scheme == 'https' || uri.scheme == 'http';
|
||||
|
||||
static bool _isSuccess(http.Response response) =>
|
||||
response.statusCode >= 200 && response.statusCode < 300;
|
||||
@@ -296,31 +290,12 @@ class AiService {
|
||||
),
|
||||
);
|
||||
if (!_isSuccess(response)) return null;
|
||||
if (kDebugMode) _logUsage(response.body);
|
||||
return _extractResponseContent(provider, response.body);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug-only prompt cache report. DeepSeek reports hits and misses
|
||||
/// directly; OpenAI-style endpoints report cached tokens in the details.
|
||||
static void _logUsage(String body) {
|
||||
try {
|
||||
final usage = (jsonDecode(body) as Map<String, dynamic>)['usage'];
|
||||
if (usage is! Map) return;
|
||||
final hit =
|
||||
usage['prompt_cache_hit_tokens'] ??
|
||||
(usage['prompt_tokens_details'] as Map?)?['cached_tokens'];
|
||||
final miss = usage['prompt_cache_miss_tokens'];
|
||||
final prompt = usage['prompt_tokens'] ?? usage['input_tokens'];
|
||||
debugPrint(
|
||||
'AI usage: prompt=$prompt cacheHit=$hit cacheMiss=$miss '
|
||||
'completion=${usage['completion_tokens'] ?? usage['output_tokens']}',
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<String?> _requestPrompt({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
@@ -398,35 +373,6 @@ class AiService {
|
||||
String reasoningEffort = 'low',
|
||||
}) {
|
||||
final isResponses = uri.path.endsWith('/responses');
|
||||
// DeepSeek thinks by default. Every request here is a short JSON answer
|
||||
// with a small token cap, so reasoning only adds cost and can exhaust the
|
||||
// cap before the JSON is written. Other providers may reject these
|
||||
// DeepSeek-specific switches, so they keep the original payload.
|
||||
//
|
||||
// Every prompt asks for JSON, but without JSON Output DeepSeek copies the
|
||||
// plain-text assistant turns in a dialogue history and answers in plain
|
||||
// text about half the time, which the app then treats as a failed call.
|
||||
if (_isDeepSeek(uri)) {
|
||||
return isResponses
|
||||
? {
|
||||
'model': model,
|
||||
'input': messages,
|
||||
'reasoning': {'effort': 'none'},
|
||||
'text': {
|
||||
'format': {'type': 'json_object'},
|
||||
},
|
||||
'temperature': ?temperature,
|
||||
'max_output_tokens': ?maxTokens,
|
||||
}
|
||||
: {
|
||||
'model': model,
|
||||
'messages': messages,
|
||||
'thinking': {'type': 'disabled'},
|
||||
'response_format': {'type': 'json_object'},
|
||||
'temperature': ?temperature,
|
||||
'max_tokens': ?maxTokens,
|
||||
};
|
||||
}
|
||||
if (isResponses) {
|
||||
return {
|
||||
'model': model,
|
||||
@@ -456,7 +402,9 @@ class AiService {
|
||||
'temperature': ?temperature,
|
||||
'maxOutputTokens': ?maxOutputTokens,
|
||||
'responseMimeType': ?responseMimeType,
|
||||
'thinkingConfig': {'thinkingBudget': thinkingBudget},
|
||||
'thinkingConfig': {
|
||||
'thinkingBudget': thinkingBudget,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -736,9 +684,15 @@ class AiService {
|
||||
if (_isSuccess(response)) {
|
||||
final content = _extractResponseContent(provider, response.body);
|
||||
if (_decodeDialogueResponse(content) != null) {
|
||||
return const AiConnectionResult(ok: true, message: '连接成功,AI 对话服务可用!');
|
||||
return const AiConnectionResult(
|
||||
ok: true,
|
||||
message: '连接成功,AI 对话服务可用!',
|
||||
);
|
||||
}
|
||||
return const AiConnectionResult(ok: true, message: '连接成功,接口响应正常。');
|
||||
return const AiConnectionResult(
|
||||
ok: true,
|
||||
message: '连接成功,接口响应正常。',
|
||||
);
|
||||
}
|
||||
if (response.statusCode == 401 || response.statusCode == 403) {
|
||||
return AiConnectionResult(
|
||||
@@ -773,10 +727,6 @@ class AiService {
|
||||
/// [aiGoal] is what Mia's own next line has to do; [learnerTask] is what the
|
||||
/// learner has to say afterwards. They used to be the same string, so the
|
||||
/// model was told to perform the learner's job.
|
||||
///
|
||||
/// The system prompt holds only what stays fixed for the whole conversation
|
||||
/// and the per-turn goal goes at the very end, so each turn repeats the
|
||||
/// previous request as a prefix and DeepSeek can serve it from its cache.
|
||||
Future<DialogueAiResponse?> dialogueReply({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
@@ -784,85 +734,40 @@ class AiService {
|
||||
required List<Map<String, String>> history,
|
||||
required String aiGoal,
|
||||
required String learnerTask,
|
||||
String level = 'A0',
|
||||
List<String> allowedLanguage = const [],
|
||||
}) async {
|
||||
if (provider == AiProviderType.mock) {
|
||||
return null;
|
||||
}
|
||||
final system = dialogueSystemPrompt(
|
||||
level: level,
|
||||
allowedLanguage: allowedLanguage,
|
||||
);
|
||||
final turnNote =
|
||||
'[Turn] Your next line must do this: $aiGoal '
|
||||
'After your line the learner has to: $learnerTask.';
|
||||
// Earlier AI lines are stored as plain English. Sent that way they teach
|
||||
// the model to answer in plain text (or, in JSON mode, with blanks), so
|
||||
// they are replayed in the JSON shape the system prompt asks for.
|
||||
final messages = <Map<String, String>>[
|
||||
for (final message in history)
|
||||
message['role'] == 'assistant'
|
||||
? {
|
||||
'role': 'assistant',
|
||||
'content': jsonEncode({'reply': message['content']}),
|
||||
}
|
||||
: message,
|
||||
];
|
||||
if (messages.isNotEmpty && messages.last['role'] == 'user') {
|
||||
messages.last = {
|
||||
'role': 'user',
|
||||
'content': '${messages.last['content']}\n\n$turnNote',
|
||||
};
|
||||
} else {
|
||||
messages.add({'role': 'user', 'content': turnNote});
|
||||
}
|
||||
final vocabularyRule = allowedLanguage.isEmpty
|
||||
? ''
|
||||
: 'Build your reply from your goal wording, names, numbers and this '
|
||||
'taught language: ${allowedLanguage.join('; ')}. '
|
||||
'At most one word outside it per reply, and only if unavoidable. ';
|
||||
final system =
|
||||
'You are Mia, a patient A0 English conversation partner for a Chinese beginner. '
|
||||
'Your own next line must do this: $aiGoal '
|
||||
'After your line the learner has to: $learnerTask. '
|
||||
'Do not say the learner sentence for them, and do not ask for anything else. '
|
||||
'$vocabularyRule'
|
||||
'Reply with one short sentence or question, at most 20 English words, in English only. '
|
||||
'Do not explain grammar. '
|
||||
'Return JSON only: {"reply": "your English line", '
|
||||
'"translation": "reply 的简体中文翻译", '
|
||||
'"feedback": "一句中文点评学习者上一句英文,没有要说的就用 null"}. '
|
||||
'Only reply is required.';
|
||||
final content = await _requestContent(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
system: system,
|
||||
messages: messages,
|
||||
messages: history,
|
||||
temperature: 0.3,
|
||||
maxTokens: 300,
|
||||
);
|
||||
return _decodeDialogueResponse(content);
|
||||
}
|
||||
|
||||
/// The conversation-wide dialogue instructions. The long word list comes
|
||||
/// last so the rules before it are shared across units as well.
|
||||
@visibleForTesting
|
||||
static String dialogueSystemPrompt({
|
||||
required String level,
|
||||
required List<String> allowedLanguage,
|
||||
}) {
|
||||
final learner = level == 'A0'
|
||||
? 'a patient A0 English conversation partner for a Chinese beginner'
|
||||
: 'a patient English conversation partner for a Chinese learner at '
|
||||
'CEFR $level';
|
||||
final vocabularyRule = allowedLanguage.isEmpty
|
||||
? ''
|
||||
: level == 'A0'
|
||||
? 'Build your reply from your goal wording, names, numbers and this '
|
||||
'taught language. At most one word outside it per reply, and '
|
||||
'only if unavoidable. Taught language: '
|
||||
'${allowedLanguage.join('; ')}'
|
||||
: 'Prefer your goal wording and this recently taught language; '
|
||||
'beyond it use only simple, common $level English. '
|
||||
'Taught language: ${allowedLanguage.join('; ')}';
|
||||
return 'You are Mia, $learner. '
|
||||
'Each user message ends with a [Turn] note saying what your next line '
|
||||
'must do and what the learner has to say after it. '
|
||||
'Do not say the learner sentence for them, and do not ask for anything else. '
|
||||
'Reply with one short sentence or question, at most 20 English words, in English only. '
|
||||
'Do not explain grammar. '
|
||||
'Return JSON only: {"reply": "your English line", '
|
||||
'"translation": "reply 的简体中文翻译", '
|
||||
'"feedback": "一句中文点评学习者上一句英文(不含 [Turn] 说明),没有要说的就用 null"}. '
|
||||
'Only reply is required. '
|
||||
'$vocabularyRule';
|
||||
}
|
||||
|
||||
/// Generates only a bounded variant of an existing review target. A network
|
||||
/// response is never exposed unless [decodeGeneratedReviewVariant] accepts it.
|
||||
Future<GeneratedReviewVariant?> generateReviewVariant({
|
||||
@@ -874,20 +779,16 @@ class AiService {
|
||||
bool repairAttempt = false,
|
||||
}) async {
|
||||
if (provider == AiProviderType.mock) return null;
|
||||
// Fixed rules first, the item-specific part last, so repeated requests
|
||||
// share a cacheable prefix.
|
||||
final instruction =
|
||||
'Generate one A0 English review variant of an existing review item. '
|
||||
'Generate one A0 English review variant for item $targetItemId based on prompt "$basePrompt". '
|
||||
'Return JSON only with exactly these five fields and nothing else: '
|
||||
'schemaVersion (must be "review-variant-1"), '
|
||||
'variantId (short id such as "ai-<target item id in lower case>-1", maximum 80 characters), '
|
||||
'targetItemId (must be the target item id below), '
|
||||
'variantId (short id such as "ai-${targetItemId.toLowerCase()}-1", maximum 80 characters), '
|
||||
'targetItemId (must be "$targetItemId"), '
|
||||
'prompt (a new short Chinese situation asking the learner to say the same target expression, maximum 120 Chinese characters), '
|
||||
'expectedAnswer (the English reference answer, maximum 12 words; use [place], [name] or [number] for learner-specific details). '
|
||||
'Stay strictly within A0. Do not introduce new vocabulary or change the target expression.\n'
|
||||
'Target item id: $targetItemId\n'
|
||||
'Base prompt: $basePrompt'
|
||||
'${repairAttempt ? '\nPrevious response failed schema or constraint validation: repair all errors.' : ''}';
|
||||
'Stay strictly within A0. Do not introduce new vocabulary or change the target expression.'
|
||||
'${repairAttempt ? ' Previous response failed schema or constraint validation: repair all errors.' : ''}';
|
||||
final content = await _requestPrompt(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
@@ -922,15 +823,13 @@ class AiService {
|
||||
required String lessonId,
|
||||
required String taskPrompt,
|
||||
required String answer,
|
||||
String level = 'A0',
|
||||
}) async {
|
||||
if (provider == AiProviderType.mock) return null;
|
||||
final instruction =
|
||||
'''Return JSON only with exactly these fields: schemaVersion, verdict, feedback, suggestion, missing, lessonId.
|
||||
schemaVersion must be "writing-feedback-1" and lessonId must be the lesson id given below.
|
||||
verdict must be accepted, rewrite, or uncertain. feedback is one short helpful Chinese sentence (max 80 Chinese characters). suggestion is null or one simple $level English rewrite (max 18 words). missing is an array of at most 3 short Chinese descriptions.
|
||||
Assess only whether the learner expressed the task. Do not claim pronunciation, do not introduce grammar beyond $level, and do not invent facts the learner did not write.
|
||||
Lesson id: $lessonId
|
||||
schemaVersion must be "writing-feedback-1" and lessonId must be "$lessonId".
|
||||
verdict must be accepted, rewrite, or uncertain. feedback is one short helpful Chinese sentence (max 80 Chinese characters). suggestion is null or one simple A0 English rewrite (max 18 words). missing is an array of at most 3 short Chinese descriptions.
|
||||
Assess only whether the learner expressed the task. Do not claim pronunciation, do not introduce grammar beyond A0, and do not invent facts the learner did not write.
|
||||
Task: $taskPrompt
|
||||
Learner wrote: $answer''';
|
||||
final content = await _requestPrompt(
|
||||
@@ -942,46 +841,10 @@ Learner wrote: $answer''';
|
||||
maxTokens: 300,
|
||||
);
|
||||
if (content == null) return null;
|
||||
return decodeWritingAiFeedback(content, expectedLessonId: lessonId);
|
||||
}
|
||||
|
||||
/// Checks a review answer or dialogue turn for spelling and grammar. It
|
||||
/// reuses the writing feedback schema, keyed by [answerId], and stays
|
||||
/// advisory: the local check still decides whether the answer counts.
|
||||
Future<WritingAiFeedback?> answerFeedback({
|
||||
required AiProviderType provider,
|
||||
required String endpoint,
|
||||
required String model,
|
||||
required String answerId,
|
||||
required String target,
|
||||
required String taskPrompt,
|
||||
required String answer,
|
||||
String level = 'A0',
|
||||
}) async {
|
||||
if (provider == AiProviderType.mock) return null;
|
||||
final instruction =
|
||||
'''Return JSON only with exactly these fields: schemaVersion, verdict, feedback, suggestion, missing, lessonId.
|
||||
schemaVersion must be "writing-feedback-1" and lessonId must be the answer id given below.
|
||||
Check the learner's English for spelling mistakes, grammar mistakes, and whether it answers the task using the target expression.
|
||||
verdict must be accepted (no spelling or grammar mistakes and the task is answered), rewrite (at least one mistake), or uncertain.
|
||||
feedback is one short helpful Chinese sentence (max 80 Chinese characters) summarising the result.
|
||||
suggestion is null when verdict is accepted, otherwise the learner's own sentence minimally corrected at $level level (max 18 words); keep their names, places and meaning.
|
||||
missing is an array of at most 3 short Chinese notes, one per mistake, each naming the wrong word and its correction, e.g. "Chna 拼写应为 China".
|
||||
Do not claim pronunciation, do not introduce grammar beyond $level, do not flag capitalisation or final punctuation alone, and do not invent facts the learner did not write.
|
||||
Answer id: $answerId
|
||||
Target expression: $target
|
||||
Task: $taskPrompt
|
||||
Learner wrote: $answer''';
|
||||
final content = await _requestPrompt(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
model: model,
|
||||
prompt: instruction,
|
||||
temperature: 0,
|
||||
maxTokens: 300,
|
||||
return decodeWritingAiFeedback(
|
||||
content,
|
||||
expectedLessonId: lessonId,
|
||||
);
|
||||
if (content == null) return null;
|
||||
return decodeWritingAiFeedback(content, expectedLessonId: answerId);
|
||||
}
|
||||
|
||||
/// Requests a 4-skill adaptive mini-lesson that re-teaches a failed target.
|
||||
@@ -996,11 +859,7 @@ Learner wrote: $answer''';
|
||||
if (provider == AiProviderType.mock) return null;
|
||||
final lessonId = 'ai-a0-${targetItemId.toLowerCase()}-1';
|
||||
final instruction =
|
||||
'Return JSON only with exactly: schemaVersion, lessonId, revision, stageVersion, source, status, abilityIds, prerequisiteIds, targetItemIds, receptiveChunks, newItemIds, previewItemIds, estimatedMinutes, tasks. Use schemaVersion lesson-2, the lessonId given below, revision 1, stageVersion A0-1.0, source aiGenerated, status validated, targetItemIds [target item id], and empty receptiveChunks, newItemIds, previewItemIds. Create exactly four tasks, one listening listenChoice, speaking repeat, reading readAnswer, writing writeAnswer. Every task has exactly taskId, skill, type, prompt, stimulus, answer, targetItemIds, answerSpec and targets [target item id]. answerSpec has exactly requiredAnyPhrases (1-4 lists, each contains 1-4 accepted English phrases), acceptedAnswers (1-4 complete accepted English answers), forbiddenPhrases (possibly empty list). Make answer satisfy its answerSpec. Lesson duration is 8 to 15 minutes. Use only very simple A0 English for the target expression. No new vocabulary, markdown, real phone numbers, or personal data.\n'
|
||||
'lessonId: $lessonId\n'
|
||||
'Target item id: $targetItemId\n'
|
||||
'Target expression: $targetLabel'
|
||||
'${repairAttempt ? '\nPrevious response was invalid: repair all constraints.' : ''}';
|
||||
'Return JSON only with exactly: schemaVersion, lessonId, revision, stageVersion, source, status, abilityIds, prerequisiteIds, targetItemIds, receptiveChunks, newItemIds, previewItemIds, estimatedMinutes, tasks. Use schemaVersion lesson-2, lessonId $lessonId, revision 1, stageVersion A0-1.0, source aiGenerated, status validated, targetItemIds [$targetItemId], and empty receptiveChunks, newItemIds, previewItemIds. Create exactly four tasks, one listening listenChoice, speaking repeat, reading readAnswer, writing writeAnswer. Every task has exactly taskId, skill, type, prompt, stimulus, answer, targetItemIds, answerSpec and targets [$targetItemId]. answerSpec has exactly requiredAnyPhrases (1-4 lists, each contains 1-4 accepted English phrases), acceptedAnswers (1-4 complete accepted English answers), forbiddenPhrases (possibly empty list). Make answer satisfy its answerSpec. Lesson duration is 8 to 15 minutes. Use only very simple A0 English for $targetLabel. No new vocabulary, markdown, real phone numbers, or personal data.${repairAttempt ? ' Previous response was invalid: repair all constraints.' : ''}';
|
||||
final content = await _requestPrompt(
|
||||
provider: provider,
|
||||
endpoint: endpoint,
|
||||
@@ -1097,13 +956,11 @@ Learner wrote: $answer''';
|
||||
rawContent = (parts?.firstOrNull as Map?)?['text'] as String?;
|
||||
} else {
|
||||
final choice = (data['choices'] as List?)?.firstOrNull as Map?;
|
||||
final choiceContent =
|
||||
(choice?['message'] as Map?)?['content'] as String? ??
|
||||
final choiceContent = (choice?['message'] as Map?)?['content'] as String? ??
|
||||
choice?['text'] as String?;
|
||||
if (choiceContent != null && choiceContent.isNotEmpty) {
|
||||
rawContent = choiceContent;
|
||||
} else if (data['output_text'] is String &&
|
||||
(data['output_text'] as String).isNotEmpty) {
|
||||
} else if (data['output_text'] is String && (data['output_text'] as String).isNotEmpty) {
|
||||
rawContent = data['output_text'] as String;
|
||||
} else {
|
||||
final outputList = data['output'] as List?;
|
||||
@@ -1126,14 +983,11 @@ Learner wrote: $answer''';
|
||||
}
|
||||
}
|
||||
if (rawContent == null) {
|
||||
if (data['response'] is String &&
|
||||
(data['response'] as String).isNotEmpty) {
|
||||
if (data['response'] is String && (data['response'] as String).isNotEmpty) {
|
||||
rawContent = data['response'] as String;
|
||||
} else if (data['text'] is String &&
|
||||
(data['text'] as String).isNotEmpty) {
|
||||
} else if (data['text'] is String && (data['text'] as String).isNotEmpty) {
|
||||
rawContent = data['text'] as String;
|
||||
} else if (data['content'] is String &&
|
||||
(data['content'] as String).isNotEmpty) {
|
||||
} else if (data['content'] is String && (data['content'] as String).isNotEmpty) {
|
||||
rawContent = data['content'] as String;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'models.dart';
|
||||
import 'courses/courses.dart';
|
||||
import 'app_theme.dart';
|
||||
import 'a0_core.dart';
|
||||
import 'generated_content.dart';
|
||||
import 'local_store.dart';
|
||||
import 'courses/course_repository.dart';
|
||||
import 'seed_courses.dart';
|
||||
import 'ai_config.dart';
|
||||
import 'ai_service.dart';
|
||||
import 'sherpa_stt_service.dart';
|
||||
@@ -31,13 +30,8 @@ abstract class _AppStateData extends ChangeNotifier {
|
||||
bool onboardingComplete = false;
|
||||
bool showChineseHints = true;
|
||||
bool keepRecordings = false;
|
||||
AppThemeMode themeMode = AppThemeMode.system;
|
||||
int completedLessons = 0;
|
||||
String activeLessonId = 'a0-01';
|
||||
|
||||
/// Lesson recommended by the placement check; it and earlier lessons are
|
||||
/// open without finishing the lesson before them.
|
||||
String placementStartLessonId = 'a0-01';
|
||||
final Set<String> completedLessonIds = {};
|
||||
final Set<String> completedSegmentIds = {};
|
||||
final Set<String> reportedAiVariantKeys = {};
|
||||
@@ -92,7 +86,7 @@ abstract class _AppStateData extends ChangeNotifier {
|
||||
}
|
||||
|
||||
class AppState extends _AppStateData
|
||||
with _ReviewAndMastery, _AssessmentProgress, _LessonProgress, _AiContent {
|
||||
with _ReviewAndMastery, _LessonProgress, _AssessmentProgress, _AiContent {
|
||||
static const _storageKey = 'learning_state_v1';
|
||||
bool isLoaded = false;
|
||||
bool _writing = false;
|
||||
@@ -111,9 +105,6 @@ class AppState extends _AppStateData
|
||||
Future<void> load() async {
|
||||
try {
|
||||
unawaited(SherpaSttService.instance.initialize());
|
||||
// Adapt the A1–B1 JSON packs into the runtime registries before the saved
|
||||
// snapshot is restored, so progress that points at those lessons resolves.
|
||||
await CourseRepository.instance.load();
|
||||
final config = await AiConfigFile.loadFromAsset();
|
||||
if (config != null) {
|
||||
if (config.apiKey != null && config.apiKey!.trim().isNotEmpty) {
|
||||
@@ -221,16 +212,6 @@ class AppState extends _AppStateData
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Applies the placement result. The start may land on any loaded lesson
|
||||
/// across A0–B1, and placement never counts as passing a lesson.
|
||||
void setPlacementStartLesson(String lessonId) {
|
||||
if (!allLessons.any((lesson) => lesson.id == lessonId)) return;
|
||||
placementStartLessonId = lessonId;
|
||||
activeLessonId = lessonId;
|
||||
_resetLessonFlow();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setAiProvider(AiProviderType value) {
|
||||
aiProvider = value;
|
||||
notifyListeners();
|
||||
@@ -252,16 +233,9 @@ class AppState extends _AppStateData
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setThemeMode(AppThemeMode value) {
|
||||
if (themeMode == value) return;
|
||||
themeMode = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearProgress() {
|
||||
completedLessons = 0;
|
||||
activeLessonId = 'a0-01';
|
||||
placementStartLessonId = 'a0-01';
|
||||
completedLessonIds.clear();
|
||||
completedSegmentIds.clear();
|
||||
reportedAiVariantKeys.clear();
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
part of 'app_state.dart';
|
||||
|
||||
/// A0 exit criteria: core items that are usable, and that are mastered.
|
||||
const a0UsableGoal = 48;
|
||||
const a0MasteredGoal = 30;
|
||||
|
||||
/// Stage assessments and the A0 pass criteria.
|
||||
mixin _AssessmentProgress on _AppStateData, _ReviewAndMastery {
|
||||
bool get hasTwoValidAssessmentPasses {
|
||||
@@ -27,60 +23,25 @@ mixin _AssessmentProgress on _AppStateData, _ReviewAndMastery {
|
||||
);
|
||||
}
|
||||
|
||||
/// Enough core items are usable and mastered to sit the stage assessment.
|
||||
bool get a0AssessmentReady =>
|
||||
coreUsableCount >= a0UsableGoal && coreMasteredCount >= a0MasteredGoal;
|
||||
bool get a0Passed =>
|
||||
coreUsableCount >= 48 &&
|
||||
coreMasteredCount >= 30 &&
|
||||
hasTwoValidAssessmentPasses;
|
||||
|
||||
bool get a0Passed => a0AssessmentReady && hasTwoValidAssessmentPasses;
|
||||
|
||||
/// Distinct assessment packs passed so far, counted toward the A0 exit.
|
||||
int get passedAssessmentPackCount => hasTwoValidAssessmentPasses
|
||||
? 2
|
||||
: assessments
|
||||
.where((record) => record.passed)
|
||||
.map((record) => record.packId)
|
||||
.toSet()
|
||||
.length
|
||||
.clamp(0, 1);
|
||||
|
||||
/// Overall progress toward leaving A0, averaging the three exit criteria.
|
||||
double get a0StageProgress {
|
||||
double part(int value, int goal) => (value / goal).clamp(0, 1).toDouble();
|
||||
return (part(coreUsableCount, a0UsableGoal) +
|
||||
part(coreMasteredCount, a0MasteredGoal) +
|
||||
part(passedAssessmentPackCount, 2)) /
|
||||
3;
|
||||
}
|
||||
|
||||
/// The first assessment pack the learner can start and has not passed yet.
|
||||
String? get nextAssessmentPackId {
|
||||
for (final pack in a0AssessmentPacks) {
|
||||
final passed = assessments.any(
|
||||
(record) => record.packId == pack.id && record.passed,
|
||||
);
|
||||
if (!passed && canStartAssessmentPack(pack.id)) return pack.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// The first pack is always open; each later one opens 24 hours after the
|
||||
/// pack before it was passed.
|
||||
bool canStartAssessmentPack(String packId) {
|
||||
final packs = a0AssessmentPacks;
|
||||
final index = packs.indexWhere((pack) => pack.id == packId);
|
||||
if (index <= 0) return true;
|
||||
final previous = assessments
|
||||
.where(
|
||||
(record) => record.packId == packs[index - 1].id && record.passed,
|
||||
)
|
||||
if (packId != 'A0-E2') return true;
|
||||
final first = assessments
|
||||
.where((record) => record.packId == 'A0-E1' && record.passed)
|
||||
.firstOrNull;
|
||||
return previous != null &&
|
||||
DateTime.now().difference(previous.completedAt) >=
|
||||
return first != null &&
|
||||
DateTime.now().difference(first.completedAt) >=
|
||||
const Duration(hours: 24);
|
||||
}
|
||||
|
||||
AssessmentRecord recordAssessment(AssessmentRecord record) {
|
||||
final canonicalPackId = canonicalAssessmentPackId(record.packId);
|
||||
final canonicalPackId = record.packId.endsWith('R')
|
||||
? record.packId.substring(0, record.packId.length - 1)
|
||||
: record.packId;
|
||||
final normalized = AssessmentRecord(
|
||||
packId: canonicalPackId,
|
||||
completedAt: record.completedAt,
|
||||
|
||||
@@ -2,7 +2,7 @@ part of 'app_state.dart';
|
||||
|
||||
/// Position in the seed course, the in-lesson step flow, and the evidence
|
||||
/// lesson tasks produce.
|
||||
mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
mixin _LessonProgress on _AppStateData, _ReviewAndMastery {
|
||||
bool get hasResumableLessonDialogue =>
|
||||
dialogueDraft != null &&
|
||||
dialogueDraft!.lessonId == activeLessonId &&
|
||||
@@ -82,43 +82,14 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
int _lessonIndex(String id) =>
|
||||
allLessons.indexWhere((lesson) => lesson.id == id);
|
||||
|
||||
bool isLessonUnlocked(String id) {
|
||||
final lessons = allLessons;
|
||||
final index = lessons.indexWhere((lesson) => lesson.id == id);
|
||||
if (index < 0) return false;
|
||||
// The placement start and everything before it are open, so a learner
|
||||
// placed further ahead can still go back to easier lessons.
|
||||
if (index == 0 || index <= _lessonIndex(placementStartLessonId)) {
|
||||
return true;
|
||||
}
|
||||
// Normal progression: the previous lesson has to be finished.
|
||||
if (!completedLessonIds.contains(lessons[index - 1].id)) return false;
|
||||
// Crossing out of A0 into A1 additionally requires passing the A0 stage
|
||||
// assessment; higher levels then chain linearly.
|
||||
if (lessons[index - 1].level == 'A0' && lessons[index].level != 'A0') {
|
||||
return a0Passed;
|
||||
}
|
||||
return true;
|
||||
final index = a0SeedLessons.indexWhere((lesson) => lesson.id == id);
|
||||
return index == 0 ||
|
||||
(index > 0 && completedLessonIds.contains(a0SeedLessons[index - 1].id));
|
||||
}
|
||||
|
||||
/// Every A0 seed lesson is finished, which opens the A0 stage assessment.
|
||||
/// Distinct from [allLessonsComplete], which spans all loaded levels.
|
||||
bool get a0LessonsComplete =>
|
||||
a0SeedLessons.every((lesson) => completedLessonIds.contains(lesson.id));
|
||||
|
||||
bool get allLessonsComplete =>
|
||||
allLessons.every((lesson) => completedLessonIds.contains(lesson.id));
|
||||
|
||||
void openLesson(String id) {
|
||||
if (!isLessonUnlocked(id)) return;
|
||||
if (id != activeLessonId) {
|
||||
// Step flags belong to the lesson they were earned in; carrying them
|
||||
// over would let another lesson finish without its own tasks.
|
||||
_resetLessonFlow();
|
||||
}
|
||||
activeLessonId = id;
|
||||
lessonStep = LessonStep.preview;
|
||||
previewIndex = 0;
|
||||
@@ -133,74 +104,35 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// [recognized] is true when the learner picked the right meaning on the
|
||||
/// first try, which is recognition evidence for the segment's targets.
|
||||
void completeListening({bool recognized = false}) {
|
||||
void completeListening() {
|
||||
_introduceLessonTargets();
|
||||
if (recognized && !lessonListeningComplete) {
|
||||
_recordLessonTaskEvidence(
|
||||
targetIds: _activeTargetItemIds,
|
||||
taskSuffix: 'listening-check',
|
||||
skill: '听辨识别',
|
||||
outcome: EvidenceKind.independentSuccess,
|
||||
);
|
||||
}
|
||||
lessonListeningComplete = true;
|
||||
lessonStep = LessonStep.speaking;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Follow-reading repeats a model sentence, so it is always assisted
|
||||
/// practice and never independent speaking evidence.
|
||||
void completeSpeaking() {
|
||||
void completeSpeaking({bool assisted = false}) {
|
||||
lessonSpeakingComplete = true;
|
||||
lessonStep = LessonStep.reading;
|
||||
_recordLessonTaskEvidence(
|
||||
targetIds: _activeTargetItemIds,
|
||||
targetIds: [_primaryTargetId],
|
||||
taskSuffix: 'speaking',
|
||||
skill: '口语表达',
|
||||
outcome: EvidenceKind.assisted,
|
||||
assisted: true,
|
||||
outcome: assisted
|
||||
? EvidenceKind.assisted
|
||||
: EvidenceKind.independentSuccess,
|
||||
assisted: assisted,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void completeReading() {
|
||||
lessonReadingComplete = true;
|
||||
// The unit's listening/reading materials are a comprehension step shown
|
||||
// once, at the end of the last segment. Units without materials (A0) skip
|
||||
// straight to writing.
|
||||
lessonStep = _shouldShowMaterialStep
|
||||
? LessonStep.material
|
||||
: LessonStep.writing;
|
||||
_recordLessonTaskEvidence(
|
||||
targetIds: _activeTargetItemIds,
|
||||
taskSuffix: 'reading',
|
||||
skill: '阅读理解',
|
||||
outcome: EvidenceKind.exposure,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// True when the active lesson is a JSON unit with materials and the learner
|
||||
/// is on its final segment, so the comprehension step is shown just once.
|
||||
bool get _shouldShowMaterialStep {
|
||||
final lesson = lessonById(activeLessonId);
|
||||
final isLastSegment =
|
||||
activeSegmentIndexFor(activeLessonId) == lesson.segments.length - 1;
|
||||
if (!isLastSegment) return false;
|
||||
final unit = CourseRepository.instance.unitById(activeLessonId);
|
||||
return unit != null && unit.materials.isNotEmpty;
|
||||
}
|
||||
|
||||
/// The listening/reading comprehension step is exposure to the unit's
|
||||
/// receptive language; it never changes produced-item mastery.
|
||||
void completeMaterial() {
|
||||
lessonStep = LessonStep.writing;
|
||||
_recordLessonTaskEvidence(
|
||||
targetIds: _activeTargetItemIds,
|
||||
taskSuffix: 'material',
|
||||
skill: '听读理解',
|
||||
targetIds: [_primaryTargetId],
|
||||
taskSuffix: 'reading',
|
||||
skill: '阅读理解',
|
||||
outcome: EvidenceKind.exposure,
|
||||
);
|
||||
notifyListeners();
|
||||
@@ -211,7 +143,7 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
lessonWritingDraft = '';
|
||||
lessonStep = LessonStep.dialogue;
|
||||
_recordLessonTaskEvidence(
|
||||
targetIds: _answerTargets(assisted: assisted, rawAnswer: rawAnswer),
|
||||
targetIds: [_primaryTargetId],
|
||||
taskSuffix: 'writing',
|
||||
skill: '写作表达',
|
||||
outcome: assisted
|
||||
@@ -227,7 +159,7 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
lessonDialogueComplete = true;
|
||||
lessonStep = LessonStep.independent;
|
||||
_recordLessonTaskEvidence(
|
||||
targetIds: _activeTargetItemIds,
|
||||
targetIds: [_primaryTargetId],
|
||||
taskSuffix: 'dialogue',
|
||||
skill: '受控对话',
|
||||
outcome: EvidenceKind.assisted,
|
||||
@@ -248,7 +180,7 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
independentAttemptSpoken = spoken && !assisted;
|
||||
lessonStep = LessonStep.complete;
|
||||
_recordLessonTaskEvidence(
|
||||
targetIds: _answerTargets(assisted: assisted, rawAnswer: rawAnswer),
|
||||
targetIds: [_primaryTargetId],
|
||||
taskSuffix: 'independent',
|
||||
skill: spoken && !assisted ? '口语表达' : '写作表达',
|
||||
inputMode: spoken && !assisted ? 'speech-unedited-transcript' : 'text',
|
||||
@@ -266,8 +198,12 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
if (!lessonCanComplete) return;
|
||||
completedLessonIds.add(activeLessonId);
|
||||
completedLessons = completedLessonIds.length;
|
||||
final next = _nextIncompleteLessonId();
|
||||
if (next != null) activeLessonId = next;
|
||||
final currentIndex = a0SeedLessons.indexWhere(
|
||||
(lesson) => lesson.id == activeLessonId,
|
||||
);
|
||||
if (currentIndex >= 0 && currentIndex < a0SeedLessons.length - 1) {
|
||||
activeLessonId = a0SeedLessons[currentIndex + 1].id;
|
||||
}
|
||||
_resetLessonFlow();
|
||||
notifyListeners();
|
||||
_syncInBackground();
|
||||
@@ -296,9 +232,8 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
final lessons = allLessons;
|
||||
int lessonIndex(String id) =>
|
||||
lessons.indexWhere((lesson) => lesson.id == id);
|
||||
a0SeedLessons.indexWhere((lesson) => lesson.id == id);
|
||||
|
||||
var targetIndex = lessonIndex(activeLessonId);
|
||||
final remoteIndex = lessonIndex(remoteActiveLessonId);
|
||||
@@ -309,19 +244,19 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
// became complete through this pull, but leave a lesson alone when the
|
||||
// learner deliberately reopened it locally after finishing it.
|
||||
while (targetIndex >= 0 &&
|
||||
targetIndex < lessons.length - 1 &&
|
||||
!completedBefore.contains(lessons[targetIndex].id) &&
|
||||
completedLessonIds.contains(lessons[targetIndex].id)) {
|
||||
targetIndex < a0SeedLessons.length - 1 &&
|
||||
!completedBefore.contains(a0SeedLessons[targetIndex].id) &&
|
||||
completedLessonIds.contains(a0SeedLessons[targetIndex].id)) {
|
||||
targetIndex++;
|
||||
}
|
||||
|
||||
var positionChanged = false;
|
||||
if (targetIndex >= 0 && lessons[targetIndex].id != activeLessonId) {
|
||||
activeLessonId = lessons[targetIndex].id;
|
||||
if (targetIndex >= 0 && a0SeedLessons[targetIndex].id != activeLessonId) {
|
||||
activeLessonId = a0SeedLessons[targetIndex].id;
|
||||
positionChanged = true;
|
||||
}
|
||||
|
||||
for (final lesson in lessons) {
|
||||
for (final lesson in a0SeedLessons) {
|
||||
final segments = lesson.segments;
|
||||
var firstOpen = segments.indexWhere(
|
||||
(segment) => !completedSegmentIds.contains(segment.id),
|
||||
@@ -341,18 +276,6 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
return changed;
|
||||
}
|
||||
|
||||
/// The first unfinished lesson from the placement start onward, falling back
|
||||
/// to any earlier unfinished lesson. Null once every lesson is finished.
|
||||
String? _nextIncompleteLessonId() {
|
||||
final lessons = allLessons;
|
||||
final start = _lessonIndex(placementStartLessonId).clamp(0, lessons.length);
|
||||
final ordered = [...lessons.skip(start), ...lessons.take(start)];
|
||||
return ordered
|
||||
.where((lesson) => !completedLessonIds.contains(lesson.id))
|
||||
.firstOrNull
|
||||
?.id;
|
||||
}
|
||||
|
||||
void _resetLessonFlow() {
|
||||
lessonStep = LessonStep.preview;
|
||||
previewIndex = 0;
|
||||
@@ -382,79 +305,31 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
_syncInBackground();
|
||||
}
|
||||
|
||||
/// Records one dialogue turn.
|
||||
///
|
||||
/// A lesson dialogue follows a model, so its turns stay assisted or pending.
|
||||
/// A free scene ([sceneId] set) is use outside the lesson: every taught core
|
||||
/// item the learner actually used counts as context use.
|
||||
void recordDialogueAttempt({
|
||||
required String taskId,
|
||||
required String rawAnswer,
|
||||
required bool assisted,
|
||||
bool spoken = false,
|
||||
String? recordingPath,
|
||||
String? sceneId,
|
||||
}) {
|
||||
final now = DateTime.now();
|
||||
final inScene = sceneId != null;
|
||||
final itemIds = inScene
|
||||
? [
|
||||
for (final id in allCoreItemIds)
|
||||
if (mastery[id]?.firstTaughtAt != null &&
|
||||
coreItemUsedIn(id, rawAnswer))
|
||||
id,
|
||||
]
|
||||
: [
|
||||
for (final id in _activeTargetItemIds)
|
||||
if (coreItemUsedIn(id, rawAnswer)) id,
|
||||
];
|
||||
if (itemIds.isEmpty && !inScene) itemIds.add(_primaryTargetId);
|
||||
final outcome = assisted
|
||||
? EvidenceKind.assisted
|
||||
: (inScene ? EvidenceKind.independentSuccess : EvidenceKind.pending);
|
||||
for (var index = 0; index < itemIds.length; index++) {
|
||||
final id = itemIds[index];
|
||||
attemptEvidence.add(
|
||||
AttemptEvidence(
|
||||
id: 'dialogue-$taskId-$id-${now.microsecondsSinceEpoch}-$index',
|
||||
itemId: id,
|
||||
taskId: taskId,
|
||||
skill: inScene ? '情境使用' : '受控对话',
|
||||
inputMode: spoken ? 'speech-unedited-transcript' : 'text',
|
||||
outcome: outcome,
|
||||
createdAt: now,
|
||||
rawAnswer: rawAnswer,
|
||||
recordingPath: recordingPath,
|
||||
assisted: assisted,
|
||||
),
|
||||
);
|
||||
if (outcome != EvidenceKind.pending) _recordEvidence(id, outcome);
|
||||
}
|
||||
attemptEvidence.add(
|
||||
AttemptEvidence(
|
||||
id: 'dialogue-$taskId-${now.microsecondsSinceEpoch}',
|
||||
itemId: _primaryTargetId,
|
||||
taskId: taskId,
|
||||
skill: '受控对话',
|
||||
inputMode: spoken ? 'speech-unedited-transcript' : 'text',
|
||||
outcome: assisted ? EvidenceKind.assisted : EvidenceKind.pending,
|
||||
createdAt: now,
|
||||
rawAnswer: rawAnswer,
|
||||
recordingPath: recordingPath,
|
||||
assisted: assisted,
|
||||
),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// A scene the learner has the language for.
|
||||
bool isSceneUnlocked(DialogueScene scene) =>
|
||||
scene.unlockAfterLessonId == null ||
|
||||
completedLessonIds.contains(scene.unlockAfterLessonId);
|
||||
|
||||
/// The open scene practised least recently, judged by its pending recaps.
|
||||
DialogueScene get recommendedScene {
|
||||
final scenes = allScenes;
|
||||
final open = scenes.where(isSceneUnlocked).toList();
|
||||
int recaps(DialogueScene scene) => reviewQueue
|
||||
.where((item) => item.id.startsWith('dialogue-${scene.id}-'))
|
||||
.length;
|
||||
open.sort((left, right) {
|
||||
final byRecaps = recaps(left).compareTo(recaps(right));
|
||||
// Prefer the newest scene when both have been practised equally.
|
||||
return byRecaps != 0
|
||||
? byRecaps
|
||||
: scenes.indexOf(right).compareTo(scenes.indexOf(left));
|
||||
});
|
||||
return open.first;
|
||||
}
|
||||
|
||||
List<String> get _activeTargetItemIds {
|
||||
final lesson = lessonById(activeLessonId);
|
||||
return lesson.segments[activeSegmentIndexFor(activeLessonId)].targetItemIds;
|
||||
@@ -462,17 +337,6 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
|
||||
String get _primaryTargetId => _activeTargetItemIds.lastOrNull ?? 'A0-P02';
|
||||
|
||||
/// An assisted answer is assisted practice for every target; an unaided one
|
||||
/// only proves the targets it actually uses.
|
||||
List<String> _answerTargets({required bool assisted, String? rawAnswer}) {
|
||||
if (assisted) return _activeTargetItemIds;
|
||||
final answer = rawAnswer ?? '';
|
||||
return [
|
||||
for (final id in _activeTargetItemIds)
|
||||
if (coreItemUsedIn(id, answer)) id,
|
||||
];
|
||||
}
|
||||
|
||||
/// Records one evidence row for every target actually attached to a local
|
||||
/// task. A task can be displayed once but must never silently award its
|
||||
/// result to unrelated core items.
|
||||
@@ -489,6 +353,7 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
final now = DateTime.now();
|
||||
for (var index = 0; index < targetIds.length; index++) {
|
||||
final id = targetIds[index];
|
||||
_recordEvidence(id, outcome);
|
||||
attemptEvidence.add(
|
||||
AttemptEvidence(
|
||||
id: 'lesson-$_activeSegmentId-$taskSuffix-$id-${now.microsecondsSinceEpoch}-$index',
|
||||
@@ -503,8 +368,6 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
assisted: assisted,
|
||||
),
|
||||
);
|
||||
// The row goes in first: status is derived from the attempt history.
|
||||
_recordEvidence(id, outcome);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +388,7 @@ mixin _LessonProgress on _AppStateData, _ReviewAndMastery, _AssessmentProgress {
|
||||
reviewQueue.add(
|
||||
ReviewItem(
|
||||
id: id,
|
||||
target: coreItemLabel(id),
|
||||
target: a0CoreItems[id] ?? id,
|
||||
prompt: template.prompt,
|
||||
hint: template.hint,
|
||||
dueAt: DateTime.now().add(const Duration(days: 1)),
|
||||
|
||||
@@ -25,13 +25,10 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
|
||||
int get knownItemCount => mastery.length;
|
||||
|
||||
// Scoped to A0: these feed the A0 stage-assessment thresholds (48 usable /
|
||||
// 30 mastered) and the matching "/48 · /30" labels. Loaded A1+ items are
|
||||
// tracked separately and must not shift the A0 graduation gate.
|
||||
int get coreUsableCount => mastery.entries
|
||||
.where(
|
||||
(entry) =>
|
||||
isA0CoreItem(entry.key) &&
|
||||
a0CoreItems.containsKey(entry.key) &&
|
||||
(entry.value.status == MasteryStatus.use ||
|
||||
entry.value.status == MasteryStatus.master) &&
|
||||
!entry.value.needsReview,
|
||||
@@ -41,7 +38,7 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
int get coreMasteredCount => mastery.entries
|
||||
.where(
|
||||
(entry) =>
|
||||
isA0CoreItem(entry.key) &&
|
||||
a0CoreItems.containsKey(entry.key) &&
|
||||
entry.value.status == MasteryStatus.master &&
|
||||
!entry.value.needsReview,
|
||||
)
|
||||
@@ -84,9 +81,6 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
ReviewItem item, {
|
||||
required bool assisted,
|
||||
String? rawAnswer,
|
||||
String inputMode = 'text',
|
||||
String? originalTranscript,
|
||||
String? recordingPath,
|
||||
}) {
|
||||
final index = reviewQueue.indexWhere(
|
||||
(candidate) => candidate.id == item.id,
|
||||
@@ -108,10 +102,7 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
final nextSuccesses = canProgress
|
||||
? current.successfulReviews + 1
|
||||
: current.successfulReviews;
|
||||
// Checkpoints fall on days 1, 3, 7 and 14 after first teaching: the first
|
||||
// review is due one day after teaching, then 2, 4 and 7 days apart, and
|
||||
// every 30 days once all four checkpoints are passed.
|
||||
const intervals = [2, 4, 7];
|
||||
const intervals = [1, 2, 4];
|
||||
final intervalIndex = (nextSuccesses - 1)
|
||||
.clamp(0, intervals.length - 1)
|
||||
.toInt();
|
||||
@@ -124,22 +115,17 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
: nextSuccesses >= 4
|
||||
? 30
|
||||
: intervals[intervalIndex];
|
||||
// The next check of a core item uses a different reviewed situation, so
|
||||
// later successes also show the item works outside the lesson's context.
|
||||
// An AI variant keeps its index so it can still be reported afterwards.
|
||||
final nextTemplate = isCoreItem(current.id) && !current.isAiGenerated
|
||||
? coreReviewVariant(current.id, current.variantIndex + 1)
|
||||
: null;
|
||||
reviewQueue[index] = current.copyWith(
|
||||
dueAt: DateTime.now().add(Duration(days: days)),
|
||||
attempts: current.attempts + 1,
|
||||
successfulReviews: nextSuccesses,
|
||||
lastProgressedAt: canProgress ? now : current.lastProgressedAt,
|
||||
prompt: nextTemplate?.prompt,
|
||||
hint: nextTemplate?.hint,
|
||||
skill: nextTemplate?.skill,
|
||||
variantIndex: nextTemplate == null ? null : current.variantIndex + 1,
|
||||
);
|
||||
if (assisted) {
|
||||
_recordEvidence(current.id, EvidenceKind.assisted);
|
||||
} else {
|
||||
_recordReviewSuccess(current.id, nextSuccesses);
|
||||
}
|
||||
_addAttemptEvidence(
|
||||
current,
|
||||
outcome: assisted
|
||||
@@ -147,15 +133,7 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
: EvidenceKind.independentSuccess,
|
||||
assisted: assisted,
|
||||
rawAnswer: rawAnswer,
|
||||
inputMode: inputMode,
|
||||
originalTranscript: originalTranscript,
|
||||
recordingPath: recordingPath,
|
||||
);
|
||||
if (assisted) {
|
||||
_recordEvidence(current.id, EvidenceKind.assisted);
|
||||
} else {
|
||||
_recordReviewSuccess(current.id, nextSuccesses);
|
||||
}
|
||||
notifyListeners();
|
||||
_syncInBackground();
|
||||
}
|
||||
@@ -178,9 +156,8 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
? (existing.checkpoint - 1).clamp(0, 4).toInt()
|
||||
: existing.checkpoint;
|
||||
mastery[item.id] = existing.copyWith(
|
||||
// Mastery needs all four checkpoints; losing one caps the item at use.
|
||||
status: secondFailure && nextCheckpoint < 4
|
||||
? _lowerStatus(existing.status, MasteryStatus.use)
|
||||
status: secondFailure
|
||||
? _statusForCheckpoint(nextCheckpoint)
|
||||
: existing.status,
|
||||
checkpoint: nextCheckpoint,
|
||||
needsReview: true,
|
||||
@@ -215,28 +192,20 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
required EvidenceKind outcome,
|
||||
bool assisted = false,
|
||||
String? rawAnswer,
|
||||
String inputMode = 'text',
|
||||
String? originalTranscript,
|
||||
String? recordingPath,
|
||||
}) {
|
||||
final now = DateTime.now();
|
||||
final spoken = originalTranscript != null;
|
||||
attemptEvidence.add(
|
||||
AttemptEvidence(
|
||||
id: 'review-${item.id}-${now.microsecondsSinceEpoch}',
|
||||
itemId: item.id,
|
||||
taskId: 'review-${item.id}',
|
||||
skill: item.skill,
|
||||
inputMode: inputMode,
|
||||
inputMode: 'text',
|
||||
outcome: outcome,
|
||||
createdAt: now,
|
||||
rawAnswer: rawAnswer,
|
||||
recordingPath: recordingPath,
|
||||
assisted: assisted,
|
||||
variantIndex: item.variantIndex,
|
||||
originalTranscript: originalTranscript,
|
||||
transcriptConfirmed: spoken && originalTranscript == rawAnswer,
|
||||
transcriptEdited: spoken && originalTranscript != rawAnswer,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -258,17 +227,17 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
|
||||
/// Adds one low-priority, non-core recap based on a completed independent
|
||||
/// dialogue. It is deliberately separate from A0 denominator items.
|
||||
void addDialogueRecap(String sentence, {String sceneId = 'a0-meet'}) {
|
||||
void addDialogueRecap(String sentence) {
|
||||
final now = DateTime.now();
|
||||
final day =
|
||||
'${now.year}${now.month.toString().padLeft(2, '0')}${now.day.toString().padLeft(2, '0')}';
|
||||
final id = 'dialogue-$sceneId-$day';
|
||||
final id = 'dialogue-a0-meet-$day';
|
||||
if (reviewQueue.any((item) => item.id == id)) return;
|
||||
reviewQueue.add(
|
||||
ReviewItem(
|
||||
id: id,
|
||||
target: sentence,
|
||||
prompt: sceneById(sceneId).recapPrompt,
|
||||
prompt: '再用英语介绍一次自己。',
|
||||
hint: '试着不用提示,说出你刚才表达的内容。',
|
||||
dueAt: now.add(const Duration(days: 1)),
|
||||
skill: '情境复练',
|
||||
@@ -310,7 +279,7 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
reviewQueue.add(
|
||||
ReviewItem(
|
||||
id: id,
|
||||
target: coreItemEnglish(id),
|
||||
target: a0CoreItems[id]!,
|
||||
prompt: template.prompt,
|
||||
hint: template.hint,
|
||||
dueAt: DateTime.now(),
|
||||
@@ -331,10 +300,6 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
reviewQueue[index] = current.copyWith(
|
||||
prompt: variant.prompt,
|
||||
hint: variant.expectedAnswer,
|
||||
// An AI prompt is a written/spoken recall task, never dictation.
|
||||
skill: isCoreItem(current.id)
|
||||
? coreReviewTemplate(current.id).skill
|
||||
: null,
|
||||
variantIndex: current.variantIndex + 1,
|
||||
isAiGenerated: true,
|
||||
);
|
||||
@@ -409,6 +374,7 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
..sort((left, right) => left.createdAt.compareTo(right.createdAt));
|
||||
var checkpoint = 0;
|
||||
var needsReview = forceReview;
|
||||
var nonReviewSuccesses = 0;
|
||||
DateTime? firstTaughtAt;
|
||||
final progressedDays = <String>{};
|
||||
for (final event in events) {
|
||||
@@ -418,6 +384,10 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
(firstTaughtAt == null || event.createdAt.isBefore(firstTaughtAt))) {
|
||||
firstTaughtAt = event.createdAt;
|
||||
}
|
||||
if (!isReview && event.outcome == EvidenceKind.independentSuccess) {
|
||||
nonReviewSuccesses++;
|
||||
continue;
|
||||
}
|
||||
if (!isReview) continue;
|
||||
if (event.outcome == EvidenceKind.independentSuccess) {
|
||||
final day =
|
||||
@@ -429,10 +399,12 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
needsReview = true;
|
||||
}
|
||||
}
|
||||
final independentLevel = nonReviewSuccesses.clamp(0, 3);
|
||||
final level = checkpoint > independentLevel ? checkpoint : independentLevel;
|
||||
mastery[id] = MasteryItem(
|
||||
id: id,
|
||||
label: existing?.label ?? coreItemLabel(id),
|
||||
status: _statusFromEvidence(id, checkpoint),
|
||||
label: existing?.label ?? a0CoreItems[id] ?? id,
|
||||
status: _statusForCheckpoint(level),
|
||||
checkpoint: checkpoint,
|
||||
needsReview: needsReview,
|
||||
evidence: events.map((event) => event.outcome).toList(),
|
||||
@@ -445,17 +417,22 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
mastery[id] ??
|
||||
MasteryItem(
|
||||
id: id,
|
||||
label: coreItemLabel(id),
|
||||
label: id,
|
||||
status: MasteryStatus.newItem,
|
||||
evidence: const [],
|
||||
);
|
||||
mastery[id] = existing.copyWith(
|
||||
status: _higherStatus(
|
||||
existing.status,
|
||||
_statusFromEvidence(id, existing.checkpoint),
|
||||
),
|
||||
evidence: [...existing.evidence, evidence],
|
||||
);
|
||||
final allEvidence = [...existing.evidence, evidence];
|
||||
MasteryStatus next = existing.status;
|
||||
if (evidence == EvidenceKind.independentSuccess) {
|
||||
next = switch (existing.status) {
|
||||
MasteryStatus.newItem => MasteryStatus.recognize,
|
||||
MasteryStatus.recognize => MasteryStatus.recall,
|
||||
MasteryStatus.recall => MasteryStatus.use,
|
||||
MasteryStatus.use || MasteryStatus.master => existing.status,
|
||||
MasteryStatus.needsReview => MasteryStatus.recall,
|
||||
};
|
||||
}
|
||||
mastery[id] = existing.copyWith(status: next, evidence: allEvidence);
|
||||
}
|
||||
|
||||
void _recordReviewSuccess(String id, int successes) {
|
||||
@@ -463,104 +440,24 @@ mixin _ReviewAndMastery on _AppStateData {
|
||||
mastery[id] ??
|
||||
MasteryItem(
|
||||
id: id,
|
||||
label: coreItemLabel(id),
|
||||
label: a0CoreItems[id] ?? id,
|
||||
status: MasteryStatus.newItem,
|
||||
evidence: const [],
|
||||
);
|
||||
final checkpoint = successes.clamp(0, 4).toInt();
|
||||
// A success never lowers what earlier evidence already showed.
|
||||
mastery[id] = existing.copyWith(
|
||||
status: _higherStatus(
|
||||
existing.status,
|
||||
_statusFromEvidence(id, checkpoint),
|
||||
),
|
||||
status: _statusForCheckpoint(checkpoint),
|
||||
checkpoint: checkpoint,
|
||||
needsReview: false,
|
||||
evidence: [...existing.evidence, EvidenceKind.independentSuccess],
|
||||
);
|
||||
}
|
||||
|
||||
/// Skills whose success only shows the learner recognises the item.
|
||||
static const _recognitionSkills = {
|
||||
'听辨识别',
|
||||
'阅读识别',
|
||||
'听力理解',
|
||||
'阅读理解',
|
||||
dictationSkill,
|
||||
MasteryStatus _statusForCheckpoint(int checkpoint) => switch (checkpoint) {
|
||||
0 => MasteryStatus.newItem,
|
||||
1 => MasteryStatus.recognize,
|
||||
2 => MasteryStatus.recall,
|
||||
3 => MasteryStatus.use,
|
||||
_ => MasteryStatus.master,
|
||||
};
|
||||
|
||||
/// Controlled lesson steps (follow-reading, scripted dialogue) are practice,
|
||||
/// not attempts to recall, so they never count as a valid answer.
|
||||
static bool _isControlledPractice(AttemptEvidence entry) =>
|
||||
entry.taskId.startsWith('lesson-') &&
|
||||
(entry.taskId.endsWith('-speaking') ||
|
||||
entry.taskId.endsWith('-dialogue'));
|
||||
|
||||
/// Use outside the lesson it was taught in: a free scene dialogue or an AI
|
||||
/// adaptive task. A review only re-asks the item on its own, so however its
|
||||
/// prompt is worded it is recall, not use in a situation.
|
||||
static bool _isContextUse(AttemptEvidence entry) =>
|
||||
entry.taskId.startsWith('dialogue-scene-') ||
|
||||
entry.id.startsWith('adaptive-');
|
||||
|
||||
/// Learning engine 3.1: recognise = a recognition success; recall = an
|
||||
/// unaided spoken or written success; use = unaided use in a different
|
||||
/// situation; master = four checkpoints, all three kinds of evidence, and
|
||||
/// the latest two valid answers both unaided.
|
||||
MasteryStatus _statusFromEvidence(String id, int checkpoint) {
|
||||
var recognized = false;
|
||||
var recalled = false;
|
||||
var usedInContext = false;
|
||||
final validAnswers = <EvidenceKind>[];
|
||||
final events = attemptEvidence.where((entry) => entry.itemId == id).toList()
|
||||
..sort((left, right) => left.createdAt.compareTo(right.createdAt));
|
||||
for (final event in events) {
|
||||
final recognition = _recognitionSkills.contains(event.skill);
|
||||
if (event.outcome == EvidenceKind.independentSuccess) {
|
||||
if (recognition) {
|
||||
recognized = true;
|
||||
} else {
|
||||
recalled = true;
|
||||
if (_isContextUse(event)) usedInContext = true;
|
||||
}
|
||||
}
|
||||
if (!recognition &&
|
||||
!_isControlledPractice(event) &&
|
||||
(event.outcome == EvidenceKind.independentSuccess ||
|
||||
event.outcome == EvidenceKind.assisted ||
|
||||
event.outcome == EvidenceKind.languageError)) {
|
||||
validAnswers.add(event.outcome);
|
||||
}
|
||||
}
|
||||
final latestTwoUnaided =
|
||||
validAnswers.length >= 2 &&
|
||||
validAnswers
|
||||
.skip(validAnswers.length - 2)
|
||||
.every((outcome) => outcome == EvidenceKind.independentSuccess);
|
||||
if (checkpoint >= 4 &&
|
||||
recognized &&
|
||||
recalled &&
|
||||
usedInContext &&
|
||||
latestTwoUnaided) {
|
||||
return MasteryStatus.master;
|
||||
}
|
||||
if (recalled && usedInContext) return MasteryStatus.use;
|
||||
if (recalled) return MasteryStatus.recall;
|
||||
if (recognized) return MasteryStatus.recognize;
|
||||
return MasteryStatus.newItem;
|
||||
}
|
||||
|
||||
static int _statusRank(MasteryStatus status) => switch (status) {
|
||||
MasteryStatus.newItem => 0,
|
||||
MasteryStatus.recognize => 1,
|
||||
MasteryStatus.recall || MasteryStatus.needsReview => 2,
|
||||
MasteryStatus.use => 3,
|
||||
MasteryStatus.master => 4,
|
||||
};
|
||||
|
||||
static MasteryStatus _higherStatus(MasteryStatus a, MasteryStatus b) =>
|
||||
_statusRank(b) > _statusRank(a) ? b : a;
|
||||
|
||||
static MasteryStatus _lowerStatus(MasteryStatus a, MasteryStatus b) =>
|
||||
_statusRank(b) < _statusRank(a) ? b : a;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@ extension _AppStateSnapshot on AppState {
|
||||
dailyMinutes = data['dailyMinutes'] as int? ?? dailyMinutes;
|
||||
showChineseHints = data['showChineseHints'] as bool? ?? showChineseHints;
|
||||
keepRecordings = data['keepRecordings'] as bool? ?? keepRecordings;
|
||||
themeMode =
|
||||
AppThemeMode.values.asNameMap()[data['themeMode'] as String?] ??
|
||||
themeMode;
|
||||
aiEndpoint = data['aiEndpoint'] as String? ?? aiEndpoint;
|
||||
aiModel = data['aiModel'] as String? ?? aiModel;
|
||||
cachedAdaptiveLessonRaw = data['cachedAdaptiveLessonRaw'] as String?;
|
||||
@@ -110,8 +107,6 @@ extension _AppStateSnapshot on AppState {
|
||||
previewIndex = data['previewIndex'] as int? ?? previewIndex;
|
||||
completedLessons = data['completedLessons'] as int? ?? completedLessons;
|
||||
activeLessonId = data['activeLessonId'] as String? ?? activeLessonId;
|
||||
placementStartLessonId =
|
||||
data['placementStartLessonId'] as String? ?? placementStartLessonId;
|
||||
completedLessonIds
|
||||
..clear()
|
||||
..addAll(
|
||||
@@ -287,7 +282,6 @@ extension _AppStateSnapshot on AppState {
|
||||
'dailyMinutes': dailyMinutes,
|
||||
'showChineseHints': showChineseHints,
|
||||
'keepRecordings': keepRecordings,
|
||||
'themeMode': themeMode.name,
|
||||
'aiEndpoint': aiEndpoint,
|
||||
'aiModel': aiModel,
|
||||
'cachedAdaptiveLessonRaw': cachedAdaptiveLessonRaw,
|
||||
@@ -333,7 +327,6 @@ extension _AppStateSnapshot on AppState {
|
||||
'previewIndex': previewIndex,
|
||||
'completedLessons': completedLessons,
|
||||
'activeLessonId': activeLessonId,
|
||||
'placementStartLessonId': placementStartLessonId,
|
||||
'completedLessonIds': completedLessonIds.toList(),
|
||||
'completedSegmentIds': completedSegmentIds.toList(),
|
||||
'reportedAiVariantKeys': reportedAiVariantKeys.toList(),
|
||||
|
||||
@@ -1,133 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// User's theme preference, persisted in the learning snapshot.
|
||||
enum AppThemeMode { system, light, dark }
|
||||
|
||||
/// One brightness worth of brand colours. Two const instances below hold the
|
||||
/// light and dark values; [AppColors] mirrors the active one so the many
|
||||
/// widgets that read `AppColors.x` directly recolour when the theme changes.
|
||||
class _Palette {
|
||||
const _Palette({
|
||||
required this.paper,
|
||||
required this.surface,
|
||||
required this.surfaceMuted,
|
||||
required this.ink,
|
||||
required this.muted,
|
||||
required this.line,
|
||||
required this.green,
|
||||
required this.softGreen,
|
||||
required this.warm,
|
||||
required this.warmInk,
|
||||
});
|
||||
|
||||
final Color paper;
|
||||
final Color surface;
|
||||
final Color surfaceMuted;
|
||||
final Color ink;
|
||||
final Color muted;
|
||||
final Color line;
|
||||
final Color green;
|
||||
final Color softGreen;
|
||||
final Color warm;
|
||||
final Color warmInk;
|
||||
}
|
||||
|
||||
const _light = _Palette(
|
||||
paper: Color(0xFFF5F7F3),
|
||||
surface: Colors.white,
|
||||
surfaceMuted: Color(0xFFF1F5F0),
|
||||
ink: Color(0xFF19211B),
|
||||
muted: Color(0xFF647268),
|
||||
line: Color(0xFFD8E1D9),
|
||||
green: Color(0xFF176B46),
|
||||
softGreen: Color(0xFFE2F3E8),
|
||||
warm: Color(0xFFFFF0E3),
|
||||
warmInk: Color(0xFF9E4C18),
|
||||
);
|
||||
|
||||
const _dark = _Palette(
|
||||
paper: Color(0xFF11150F),
|
||||
surface: Color(0xFF1B211A),
|
||||
surfaceMuted: Color(0xFF232B22),
|
||||
ink: Color(0xFFE8EEE6),
|
||||
muted: Color(0xFF9CA99D),
|
||||
line: Color(0xFF33402F),
|
||||
green: Color(0xFF2FA36B),
|
||||
softGreen: Color(0xFF17311F),
|
||||
warm: Color(0xFF352618),
|
||||
warmInk: Color(0xFFEBAA78),
|
||||
);
|
||||
|
||||
/// Brand colours for the active brightness. Values are reassigned by
|
||||
/// [AppColors.applyBrightness]; call it before building the widget tree.
|
||||
abstract final class AppColors {
|
||||
static Color paper = _light.paper;
|
||||
static Color surface = _light.surface;
|
||||
static Color surfaceMuted = _light.surfaceMuted;
|
||||
static Color ink = _light.ink;
|
||||
static Color muted = _light.muted;
|
||||
static Color line = _light.line;
|
||||
static Color green = _light.green;
|
||||
static Color softGreen = _light.softGreen;
|
||||
static Color warm = _light.warm;
|
||||
static Color warmInk = _light.warmInk;
|
||||
|
||||
static Brightness brightness = Brightness.light;
|
||||
|
||||
static void applyBrightness(Brightness value) {
|
||||
final p = value == Brightness.dark ? _dark : _light;
|
||||
brightness = value;
|
||||
paper = p.paper;
|
||||
surface = p.surface;
|
||||
surfaceMuted = p.surfaceMuted;
|
||||
ink = p.ink;
|
||||
muted = p.muted;
|
||||
line = p.line;
|
||||
green = p.green;
|
||||
softGreen = p.softGreen;
|
||||
warm = p.warm;
|
||||
warmInk = p.warmInk;
|
||||
}
|
||||
static const paper = Color(0xFFF5F7F3);
|
||||
static const surface = Colors.white;
|
||||
static const surfaceMuted = Color(0xFFF1F5F0);
|
||||
static const ink = Color(0xFF19211B);
|
||||
static const muted = Color(0xFF647268);
|
||||
static const line = Color(0xFFD8E1D9);
|
||||
static const green = Color(0xFF176B46);
|
||||
static const softGreen = Color(0xFFE2F3E8);
|
||||
static const warm = Color(0xFFFFF0E3);
|
||||
static const warmInk = Color(0xFF9E4C18);
|
||||
}
|
||||
|
||||
ThemeData buildAppTheme(Brightness brightness) {
|
||||
final p = brightness == Brightness.dark ? _dark : _light;
|
||||
ThemeData buildAppTheme() {
|
||||
final scheme =
|
||||
ColorScheme.fromSeed(seedColor: p.green, brightness: brightness).copyWith(
|
||||
surface: p.surface,
|
||||
onSurface: p.ink,
|
||||
primary: p.green,
|
||||
ColorScheme.fromSeed(
|
||||
seedColor: AppColors.green,
|
||||
brightness: Brightness.light,
|
||||
).copyWith(
|
||||
surface: AppColors.surface,
|
||||
onSurface: AppColors.ink,
|
||||
primary: AppColors.green,
|
||||
onPrimary: Colors.white,
|
||||
outline: p.line,
|
||||
outline: AppColors.line,
|
||||
);
|
||||
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: brightness,
|
||||
colorScheme: scheme,
|
||||
scaffoldBackgroundColor: p.paper,
|
||||
scaffoldBackgroundColor: AppColors.paper,
|
||||
fontFamily: 'PingFang SC',
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: p.surface,
|
||||
foregroundColor: p.ink,
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: AppColors.surface,
|
||||
foregroundColor: AppColors.ink,
|
||||
elevation: 0,
|
||||
centerTitle: false,
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
textTheme: const TextTheme(
|
||||
headlineMedium: TextStyle(
|
||||
color: p.ink,
|
||||
color: AppColors.ink,
|
||||
fontSize: 26,
|
||||
height: 1.25,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.6,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
color: p.ink,
|
||||
color: AppColors.ink,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
bodyLarge: TextStyle(color: p.ink, fontSize: 16, height: 1.5),
|
||||
bodyMedium: TextStyle(color: p.muted, fontSize: 14, height: 1.5),
|
||||
bodyLarge: TextStyle(color: AppColors.ink, fontSize: 16, height: 1.5),
|
||||
bodyMedium: TextStyle(color: AppColors.muted, fontSize: 14, height: 1.5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
import 'models.dart';
|
||||
import 'writing_feedback.dart';
|
||||
|
||||
class AssessmentTask {
|
||||
const AssessmentTask({
|
||||
required this.id,
|
||||
required this.skill,
|
||||
required this.prompt,
|
||||
this.audio,
|
||||
this.choices = const [],
|
||||
this.answerIndex,
|
||||
this.requiredWords = const [],
|
||||
required this.abilityId,
|
||||
});
|
||||
final String id;
|
||||
final AssessmentSkill skill;
|
||||
final String prompt;
|
||||
final String? audio;
|
||||
final List<String> choices;
|
||||
final int? answerIndex;
|
||||
final List<String> requiredWords;
|
||||
final String abilityId;
|
||||
}
|
||||
|
||||
class AssessmentPack {
|
||||
const AssessmentPack({required this.id, required this.tasks});
|
||||
final String id;
|
||||
final List<AssessmentTask> tasks;
|
||||
List<AssessmentTask> forSkill(AssessmentSkill skill) =>
|
||||
tasks.where((task) => task.skill == skill).toList();
|
||||
}
|
||||
|
||||
final a0AssessmentPacks = [
|
||||
AssessmentPack(
|
||||
id: 'A0-E1',
|
||||
tasks: _tasks(
|
||||
'A',
|
||||
const [
|
||||
'Hello. I’m Mia.|自我介绍|买东西|问时间|0',
|
||||
'How do you spell your name?|问姓名拼写|问地点|问时间|0',
|
||||
'I’m tired today.|很好|累|来自哪里|1',
|
||||
'seven|6|7|8|1',
|
||||
'one-three-eight|138|183|318|0',
|
||||
'It’s a key.|书|钥匙|包|1',
|
||||
'I’m from Hong Kong.|香港|伦敦|北京|0',
|
||||
'This is my mother.|妈妈|姐姐|朋友|0',
|
||||
'It is Monday.|星期一|星期二|星期三|0',
|
||||
'I like tea.|茶|咖啡|电影|0',
|
||||
],
|
||||
const [
|
||||
'Mia: I’m from Hong Kong.\nShen: I’m good.\nMia: My number is one-three-eight.|Mia 来自哪里?|Hong Kong|London|0',
|
||||
'Mia: It is Monday.\nShen: It is three o’clock.|几点?|three|one|0',
|
||||
'Mia: This is my brother.\nShen: Nice to meet you.|谁是家人?|brother|mother|0',
|
||||
'Mia: I like music.\nShen: I like tea.|Shen 喜欢什么?|tea|music|0',
|
||||
'Mia: What’s this?\nShen: It’s a pen.|这是什么?|pen|bag|0',
|
||||
],
|
||||
const [
|
||||
'用英语介绍自己。|i,m',
|
||||
'用英语说明你来自哪里。|i,m,from',
|
||||
'用英语说一个喜好。|i,like',
|
||||
'用英语说“这是一支笔”。|it,s,pen',
|
||||
'用英语说明星期或整点。|it,s',
|
||||
],
|
||||
const [
|
||||
'介绍姓名、地点、状态/喜好并反问。|hello,i,m,from',
|
||||
'完整拼读一个四字母姓名。|a,l,e,x',
|
||||
'说一个三位虚拟号码。|one,three,eight',
|
||||
'介绍一位家人。|this,my',
|
||||
'说明星期。|it,s,monday',
|
||||
'说明整点。|it,s,o,clock',
|
||||
'请求对方重复或放慢。|please',
|
||||
'说出一个物品。|it,s',
|
||||
],
|
||||
),
|
||||
),
|
||||
AssessmentPack(
|
||||
id: 'A0-E2',
|
||||
tasks: _tasks(
|
||||
'B',
|
||||
const [
|
||||
'Hi. My name is Leo.|自我介绍|买东西|问号码|0',
|
||||
'How do you spell that?|问姓名拼写|问喜好|问家人|0',
|
||||
'I’m okay.|很好|还可以|累|1',
|
||||
'four|3|4|5|1',
|
||||
'two-nine-six|296|269|926|0',
|
||||
'It’s a bag.|笔|包|水|1',
|
||||
'I’m from London.|伦敦|香港|北京|0',
|
||||
'This is my sister.|姐姐|妈妈|朋友|0',
|
||||
'It is Friday.|星期五|星期一|星期日|0',
|
||||
'I like coffee.|茶|咖啡|音乐|1',
|
||||
],
|
||||
const [
|
||||
'Mia: I’m from London.\nLeo: I’m okay.\nMia: My number is two-nine-six.|号码是多少?|296|269|0',
|
||||
'Mia: It is Friday.\nLeo: It is six o’clock.|几点?|six|three|0',
|
||||
'Mia: This is my father.\nLeo: Hi!|谁是家人?|father|brother|0',
|
||||
'Mia: I like movies.\nLeo: I like coffee.|Leo 喜欢什么?|coffee|movies|0',
|
||||
'Mia: What’s this?\nLeo: It’s water.|这是什么?|water|book|0',
|
||||
],
|
||||
const [
|
||||
'用英语介绍自己。|my,name,is',
|
||||
'用英语说明你来自哪里。|i,m,from',
|
||||
'用英语说一个喜好。|i,like',
|
||||
'用英语说“这是一个包”。|it,s,bag',
|
||||
'用英语说明星期或整点。|it,s',
|
||||
],
|
||||
const [
|
||||
'介绍姓名、地点、状态/喜好并反问。|hi,i,m,from',
|
||||
'完整拼读一个四字母姓名。|l,e,o,n',
|
||||
'说一个三位虚拟号码。|two,nine,six',
|
||||
'介绍一位家人。|this,my',
|
||||
'说明星期。|it,s,friday',
|
||||
'说明整点。|it,s,o,clock',
|
||||
'请求对方重复或放慢。|please',
|
||||
'说出一个物品。|it,s',
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
/// Replacement packs use different people, places, numbers, days and likes.
|
||||
/// They keep the same assessed ability but are not immediate replays.
|
||||
final a0ReplacementPacks = [
|
||||
_replacementPack(a0AssessmentPacks[0], 'A0-E1R', const {
|
||||
'Mia': 'Nora',
|
||||
'Shen': 'Kai',
|
||||
'Hong Kong': 'London',
|
||||
'Monday': 'Friday',
|
||||
'one-three-eight': 'two-four-seven',
|
||||
'138': '247',
|
||||
'three o’clock': 'six o’clock',
|
||||
'tea': 'coffee',
|
||||
'key': 'bag',
|
||||
'mother': 'father',
|
||||
}),
|
||||
_replacementPack(a0AssessmentPacks[1], 'A0-E2R', const {
|
||||
'Mia': 'Ava',
|
||||
'Leo': 'Bo',
|
||||
'London': 'Hong Kong',
|
||||
'Friday': 'Tuesday',
|
||||
'two-nine-six': 'five-one-four',
|
||||
'296': '514',
|
||||
'six o’clock': 'two o’clock',
|
||||
'coffee': 'music',
|
||||
'bag': 'book',
|
||||
'sister': 'brother',
|
||||
}),
|
||||
];
|
||||
|
||||
AssessmentPack? replacementFor(String packId) => switch (packId) {
|
||||
'A0-E1' => a0ReplacementPacks[0],
|
||||
'A0-E2' => a0ReplacementPacks[1],
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// Local checks for the frozen A0 exit tasks. They accept variable names and
|
||||
/// places, but require the communicative information named by each task.
|
||||
bool checkOpenAssessmentAnswer(AssessmentTask task, String input) {
|
||||
final text = input
|
||||
.toLowerCase()
|
||||
.replaceAll('’', "'")
|
||||
.replaceAll('‘', "'")
|
||||
.replaceAll(RegExp(r"\s+"), " ")
|
||||
.trim();
|
||||
final words = RegExp(
|
||||
r"[a-z]+(?:'[a-z]+)?",
|
||||
).allMatches(text).map((match) => match.group(0)!).toSet();
|
||||
bool has(String word) => words.contains(
|
||||
word
|
||||
.toLowerCase()
|
||||
.replaceAll('’', "'")
|
||||
.replaceAll('‘', "'")
|
||||
.trim(),
|
||||
);
|
||||
bool phrase(String value) {
|
||||
final normVal = value
|
||||
.toLowerCase()
|
||||
.replaceAll('’', "'")
|
||||
.replaceAll('‘', "'")
|
||||
.replaceAll(RegExp(r"\s+"), " ")
|
||||
.trim();
|
||||
return text.contains(normVal);
|
||||
}
|
||||
bool hasAny(Iterable<String> values) => values.any(has);
|
||||
final introduction = phrase("i'm") || phrase('i am') || phrase('my name is');
|
||||
final itIs = phrase("it's") || phrase('it is');
|
||||
final numberWords = RegExp(
|
||||
r'\b(zero|one|two|three|four|five|six|seven|eight|nine|ten)\b',
|
||||
).allMatches(text).length;
|
||||
final position = int.tryParse(
|
||||
RegExp(r'(\d+)$').firstMatch(task.id)?.group(1) ?? '',
|
||||
);
|
||||
if (position == null) return false;
|
||||
|
||||
if (task.skill == AssessmentSkill.writing) {
|
||||
const lessonIds = ['a0-01', 'a0-06', 'a0-09', 'a0-05', 'a0-08'];
|
||||
return position >= 1 &&
|
||||
position <= lessonIds.length &&
|
||||
WritingFeedback.check(lessonIds[position - 1], input).complete;
|
||||
}
|
||||
if (task.skill != AssessmentSkill.speaking) return false;
|
||||
return switch (position) {
|
||||
1 =>
|
||||
hasAny(['hello', 'hi']) &&
|
||||
introduction &&
|
||||
has('from') &&
|
||||
(has('like') || hasAny(['good', 'okay', 'tired'])) &&
|
||||
hasAny(['what', 'where', 'how', 'do']),
|
||||
2 => RegExp(r'[a-z](?:[ -]?[a-z]){3,}').hasMatch(text),
|
||||
3 => numberWords >= 3,
|
||||
4 =>
|
||||
phrase('this is my') && hasAny(['mother', 'father', 'sister', 'brother']),
|
||||
5 =>
|
||||
itIs &&
|
||||
hasAny([
|
||||
'monday',
|
||||
'tuesday',
|
||||
'wednesday',
|
||||
'thursday',
|
||||
'friday',
|
||||
'saturday',
|
||||
'sunday',
|
||||
]),
|
||||
6 =>
|
||||
itIs &&
|
||||
(has('clock') ||
|
||||
has("o'clock") ||
|
||||
has('oclock') ||
|
||||
phrase("o'clock")),
|
||||
7 => has('please') && hasAny(['say', 'speak', 'repeat', 'slow', 'slowly']),
|
||||
8 => itIs && hasAny(['book', 'pen', 'bag', 'key', 'water']),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
AssessmentPack _replacementPack(
|
||||
AssessmentPack source,
|
||||
String id,
|
||||
Map<String, String> values,
|
||||
) {
|
||||
String replace(String value) {
|
||||
var result = value;
|
||||
for (final entry in values.entries) {
|
||||
result = result.replaceAll(entry.key, entry.value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return AssessmentPack(
|
||||
id: id,
|
||||
tasks: source.tasks
|
||||
.map(
|
||||
(task) => AssessmentTask(
|
||||
id: task.id.replaceFirst(
|
||||
source.id.split('-').last,
|
||||
id.split('-').last,
|
||||
),
|
||||
skill: task.skill,
|
||||
prompt: replace(task.prompt),
|
||||
audio: task.audio == null ? null : replace(task.audio!),
|
||||
choices: task.choices.map(replace).toList(),
|
||||
answerIndex: task.answerIndex,
|
||||
requiredWords: task.requiredWords.map(replace).toList(),
|
||||
abilityId: task.abilityId,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
List<AssessmentTask> _tasks(
|
||||
String version,
|
||||
List<String> listening,
|
||||
List<String> reading,
|
||||
List<String> writing,
|
||||
List<String> speaking,
|
||||
) {
|
||||
AssessmentTask choice(String raw, AssessmentSkill skill, int index) {
|
||||
final fields = raw.split('|');
|
||||
return AssessmentTask(
|
||||
id: 'A0-$version-${skill.name[0].toUpperCase()}${index + 1}',
|
||||
skill: skill,
|
||||
audio: skill == AssessmentSkill.listening ? fields[0] : null,
|
||||
prompt: skill == AssessmentSkill.listening ? '选择正确答案' : fields[0],
|
||||
choices: skill == AssessmentSkill.listening
|
||||
? fields.sublist(1, 4)
|
||||
: fields.sublist(1, 3),
|
||||
answerIndex: int.parse(fields.last),
|
||||
abilityId: 'A0-C${(index % 10 + 1).toString().padLeft(2, '0')}',
|
||||
);
|
||||
}
|
||||
|
||||
AssessmentTask open(String raw, AssessmentSkill skill, int index) {
|
||||
final fields = raw.split('|');
|
||||
return AssessmentTask(
|
||||
id: 'A0-$version-${skill.name[0].toUpperCase()}${index + 1}',
|
||||
skill: skill,
|
||||
prompt: fields[0],
|
||||
requiredWords: fields[1].split(','),
|
||||
abilityId: 'A0-C${(index % 10 + 1).toString().padLeft(2, '0')}',
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
...List.generate(
|
||||
listening.length,
|
||||
(i) => choice(listening[i], AssessmentSkill.listening, i),
|
||||
),
|
||||
...List.generate(
|
||||
reading.length,
|
||||
(i) => choice(reading[i], AssessmentSkill.reading, i),
|
||||
),
|
||||
...List.generate(
|
||||
writing.length,
|
||||
(i) => open(writing[i], AssessmentSkill.writing, i),
|
||||
),
|
||||
...List.generate(
|
||||
speaking.length,
|
||||
(i) => open(speaking[i], AssessmentSkill.speaking, i),
|
||||
),
|
||||
];
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import 'answer_rules.dart';
|
||||
import 'core_items.dart';
|
||||
import 'course_catalog.dart';
|
||||
import 'course_models.dart';
|
||||
|
||||
/// A reply that is at least one real word; turns without a declared rule
|
||||
/// accept it.
|
||||
bool _isRealReply(String text) => RegExp(r'[a-z]{2,}').hasMatch(text);
|
||||
|
||||
/// Whether [input] actually contains core item [id], by the item's `match`
|
||||
/// rule. Lessons, reviews and dialogues use the same rule, so evidence only
|
||||
/// goes to the items a learner really produced, never to every target a task
|
||||
/// happens to be attached to.
|
||||
bool coreItemUsedIn(String id, String input) {
|
||||
final rule = coreItemMatch[id];
|
||||
if (rule == null || rule.isEmpty) return false;
|
||||
return matchesRule(input, rule);
|
||||
}
|
||||
|
||||
bool matchesSegmentDialogue(String segmentId, int stage, String response) {
|
||||
final segment = findLessonSegment(segmentId);
|
||||
if (segment == null || stage >= segment.dialogueRequiredTerms.length) {
|
||||
return false;
|
||||
}
|
||||
final text = normalizeAnswer(response);
|
||||
final terms = segment.dialogueRequiredTerms[stage];
|
||||
// A turn that declares no reviewed term (adapted units) just needs a real
|
||||
// reply, matching how free-scene turns are validated.
|
||||
if (terms.isEmpty) return _isRealReply(text);
|
||||
return groupSatisfied(text, terms);
|
||||
}
|
||||
|
||||
bool matchesSegmentIndependent(String segmentId, String response) {
|
||||
final rule = findLessonSegment(segmentId)?.independentRequiredTerms;
|
||||
if (rule == null) return false;
|
||||
// A segment without a reviewed rule accepts any real attempt.
|
||||
if (rule.isEmpty) return _isRealReply(normalizeAnswer(response));
|
||||
return matchesRule(response, rule);
|
||||
}
|
||||
|
||||
/// Explains in Chinese what an independent attempt still lacks, or returns
|
||||
/// null when [matchesSegmentIndependent] accepts it.
|
||||
String? independentShortfall(String segmentId, String response) {
|
||||
if (matchesSegmentIndependent(segmentId, response)) return null;
|
||||
final rule = findLessonSegment(segmentId)?.independentRequiredTerms;
|
||||
if (rule == null) return '这次还没有用上本段要练的内容。查看帮助后补充一次。';
|
||||
if (rule.isEmpty) return '请写一个完整的英文句子。';
|
||||
final text = normalizeAnswer(response);
|
||||
if (rule.length == 1) {
|
||||
final group = rule.single;
|
||||
final minimum = groupMinimum(group);
|
||||
final terms = groupTerms(group);
|
||||
if (minimum != null) {
|
||||
return '需要用上至少 $minimum 个本段的表达(${terms.take(6).join(' / ')}),'
|
||||
'目前用上 ${groupTermsUsed(text, group)} 个。';
|
||||
}
|
||||
return '需要用上本段的表达之一:${terms.take(4).join(' / ')}。';
|
||||
}
|
||||
final missing = rule
|
||||
.where((group) => !groupSatisfied(text, group))
|
||||
.map(termGroupLabel);
|
||||
return '还缺:${missing.join(';')}。';
|
||||
}
|
||||
|
||||
/// Whole-lesson and free-scene dialogues validate the same way segment
|
||||
/// dialogues do: the turn has to contain the language the turn is teaching.
|
||||
bool matchesDialogueStage(LessonDialogue script, int stage, String response) {
|
||||
final text = normalizeAnswer(response);
|
||||
if (stage < 0 || stage >= script.requiredTerms.length) {
|
||||
// No declared requirement: accept anything that is actually a word.
|
||||
return _isRealReply(text);
|
||||
}
|
||||
final terms = script.requiredTerms[stage];
|
||||
if (terms.isEmpty) return _isRealReply(text);
|
||||
return groupSatisfied(text, terms);
|
||||
}
|
||||
|
||||
String dialogueTaskLabel(LessonDialogue script, int stage) =>
|
||||
stage >= 0 && stage < script.taskLabels.length
|
||||
? script.taskLabels[stage]
|
||||
: '完成本轮任务';
|
||||
@@ -1,174 +0,0 @@
|
||||
/// The one rule language every offline answer check uses: lesson writing,
|
||||
/// independent attempts, controlled dialogues and core-item reviews. Rules
|
||||
/// live in the course JSON; this file only interprets them.
|
||||
///
|
||||
/// A rule is a list of groups. Every group must be satisfied (AND); a group
|
||||
/// is satisfied when any of its terms appears (OR). A group that starts with
|
||||
/// `#min:N` instead needs N different terms of the group.
|
||||
///
|
||||
/// Terms:
|
||||
/// * `text` — whole words, after [normalizeAnswer] on both sides, so
|
||||
/// "I'm" and "I am" are the same term.
|
||||
/// * `a + b` — every part has to appear.
|
||||
/// * `/regex/` — a regular expression over the normalized answer.
|
||||
/// * `#spelling`, `#digit`, `#word`, `#question` — structures, see
|
||||
/// [_matchesStructure]; `#numbers:N` needs N English number words and
|
||||
/// `#words:N` needs N different words.
|
||||
library;
|
||||
|
||||
/// Lowercases, unifies quotes and spaces, and expands contractions, so an
|
||||
/// answer and a rule term compare the same way however either was typed.
|
||||
String normalizeAnswer(String input) {
|
||||
var text = input
|
||||
.toLowerCase()
|
||||
.replaceAll('’', "'")
|
||||
.replaceAll('‘', "'")
|
||||
.replaceAll(RegExp(r'\s+'), ' ')
|
||||
.trim();
|
||||
for (final (pattern, replacement) in _contractions) {
|
||||
text = text.replaceAll(pattern, replacement);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
final List<(RegExp, String)> _contractions = [
|
||||
(RegExp(r"\bwon't\b"), 'will not'),
|
||||
(RegExp(r"\bcan't\b"), 'can not'),
|
||||
(RegExp(r'\bcannot\b'), 'can not'),
|
||||
(RegExp(r"n't\b"), ' not'),
|
||||
(RegExp(r"'m\b"), ' am'),
|
||||
(RegExp(r"'re\b"), ' are'),
|
||||
(RegExp(r"'s\b"), ' is'),
|
||||
(RegExp(r"'ll\b"), ' will'),
|
||||
(RegExp(r"'ve\b"), ' have'),
|
||||
(RegExp(r"'d\b"), ' would'),
|
||||
];
|
||||
|
||||
/// Whether [input] satisfies every group of [rule].
|
||||
bool matchesRule(String input, List<List<String>> rule) {
|
||||
final text = normalizeAnswer(input);
|
||||
return rule.every((group) => groupSatisfied(text, group));
|
||||
}
|
||||
|
||||
/// Whether the normalized [text] satisfies [group].
|
||||
bool groupSatisfied(String text, List<String> group) {
|
||||
final minimum = groupMinimum(group);
|
||||
if (minimum == null) return group.any((term) => containsTerm(text, term));
|
||||
return groupTermsUsed(text, group) >= minimum;
|
||||
}
|
||||
|
||||
/// N for a `#min:N` group, otherwise null.
|
||||
int? groupMinimum(List<String> group) => group.isNotEmpty
|
||||
? int.tryParse(
|
||||
RegExp(r'^#min:(\d+)$').firstMatch(group.first)?.group(1) ?? '',
|
||||
)
|
||||
: null;
|
||||
|
||||
/// The terms of [group] a learner can pick from, without a `#min:N` marker.
|
||||
List<String> groupTerms(List<String> group) =>
|
||||
groupMinimum(group) == null ? group : group.sublist(1);
|
||||
|
||||
/// How many different terms of [group] appear in the normalized [text].
|
||||
int groupTermsUsed(String text, List<String> group) =>
|
||||
groupTerms(group).where((term) => containsTerm(text, term)).length;
|
||||
|
||||
/// Whether the normalized [text] contains [term] (see the library docs).
|
||||
bool containsTerm(String text, String term) {
|
||||
if (term.contains(' + ')) {
|
||||
// Structures are checked on what is left after the literal parts, so in
|
||||
// "i'm + #word" the "am" of the frame is not taken for the name.
|
||||
var rest = text;
|
||||
final parts = term.split(' + ').map((part) => part.trim()).toList();
|
||||
for (final part in parts.where((part) => !part.startsWith('#'))) {
|
||||
if (!containsTerm(rest, part)) return false;
|
||||
rest = rest.replaceFirst(_termPattern(part), ' ');
|
||||
}
|
||||
return parts
|
||||
.where((part) => part.startsWith('#'))
|
||||
.every((part) => _matchesStructure(rest, part));
|
||||
}
|
||||
if (term.startsWith('#')) return _matchesStructure(text, term);
|
||||
return _termPattern(term).hasMatch(text);
|
||||
}
|
||||
|
||||
RegExp _termPattern(String term) {
|
||||
if (term.length > 2 && term.startsWith('/') && term.endsWith('/')) {
|
||||
return RegExp(term.substring(1, term.length - 1));
|
||||
}
|
||||
final escaped = RegExp.escape(normalizeAnswer(term));
|
||||
return RegExp('(?<![a-z])$escaped(?![a-z])');
|
||||
}
|
||||
|
||||
final _numberWord = RegExp(
|
||||
r'(?<![a-z])(zero|one|two|three|four|five|six|seven|eight|nine|ten)(?![a-z])',
|
||||
);
|
||||
final _wordToken = RegExp(r"[a-z]+(?:'[a-z]+)?");
|
||||
|
||||
bool _matchesStructure(String text, String token) {
|
||||
final counted = RegExp(r'^#(numbers|words):(\d+)$').firstMatch(token);
|
||||
if (counted != null) {
|
||||
final needed = int.parse(counted.group(2)!);
|
||||
return switch (counted.group(1)) {
|
||||
'numbers' => _numberWord.allMatches(text).length >= needed,
|
||||
_ =>
|
||||
_wordToken.allMatches(text).map((m) => m.group(0)).toSet().length >=
|
||||
needed,
|
||||
};
|
||||
}
|
||||
return switch (token) {
|
||||
// Letters said one by one ("S-H-E-N", "s h e n", "L-I"), or the spelled
|
||||
// name typed as one word ("Shen", "aaa"). Case and hyphens are ignored.
|
||||
'#spelling' =>
|
||||
RegExp(
|
||||
r'(?:^|[^a-z])[a-z](?:\s*[-‐-―]\s*[a-z]|\s[a-z])+(?![a-z])',
|
||||
).hasMatch(text) ||
|
||||
RegExp(r'^[a-z]{2,}$').hasMatch(
|
||||
text
|
||||
.replaceAll(RegExp(r'[-‐-―]'), '')
|
||||
.replaceAll(RegExp(r'^[^a-z]+|[^a-z]+$'), ''),
|
||||
),
|
||||
// Any spoken or written digit.
|
||||
'#digit' => _numberWord.hasMatch(text) || RegExp('[0-9]').hasMatch(text),
|
||||
// A real word after the frame, so "I'm" alone is not a name.
|
||||
'#word' => RegExp(r'[a-z]{2,}').hasMatch(text),
|
||||
// Any question addressed to the partner.
|
||||
'#question' =>
|
||||
text.contains('?') ||
|
||||
RegExp(
|
||||
r"(?<![a-z])(what|where|who|when|how|why|do you|are you|can you)(?![a-z])",
|
||||
).hasMatch(text),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// A short Chinese label for [group], used when telling a learner what an
|
||||
/// attempt still lacks.
|
||||
String termGroupLabel(List<String> group) {
|
||||
final labels = <String>[];
|
||||
for (final term in groupTerms(group)) {
|
||||
final label = _termLabel(term);
|
||||
if (!labels.contains(label)) labels.add(label);
|
||||
}
|
||||
return labels.take(4).join(' / ');
|
||||
}
|
||||
|
||||
String _termLabel(String term) => term
|
||||
.split(' + ')
|
||||
.map((part) {
|
||||
final trimmed = part.trim();
|
||||
final counted = RegExp(r'^#(numbers|words):(\d+)$').firstMatch(trimmed);
|
||||
if (counted != null) {
|
||||
return counted.group(1) == 'numbers'
|
||||
? '至少 ${counted.group(2)} 个英文数字'
|
||||
: '至少 ${counted.group(2)} 个词';
|
||||
}
|
||||
if (trimmed.startsWith('/')) return '…';
|
||||
return switch (trimmed) {
|
||||
'#spelling' => '把名字逐个字母拼出来(如 A-L-E-X)',
|
||||
'#digit' => '一个数字',
|
||||
'#word' => '…',
|
||||
'#question' => '一句反问(如 What about you?)',
|
||||
final word => word,
|
||||
};
|
||||
})
|
||||
.join(' ');
|
||||
@@ -1,132 +0,0 @@
|
||||
import '../models.dart';
|
||||
import 'answer_rules.dart';
|
||||
|
||||
/// One stage-assessment task, from the level's `assessment` JSON. Choice
|
||||
/// tasks carry `choices` and `answerIndex`; open tasks (writing, speaking)
|
||||
/// carry an `accept` rule in the language of `answer_rules.dart` and the
|
||||
/// `guide` shown after a wrong answer.
|
||||
class AssessmentTask {
|
||||
const AssessmentTask({
|
||||
required this.id,
|
||||
required this.skill,
|
||||
required this.prompt,
|
||||
this.audio,
|
||||
this.choices = const [],
|
||||
this.answerIndex,
|
||||
this.accept = const [],
|
||||
this.guide = '',
|
||||
});
|
||||
|
||||
factory AssessmentTask.fromJson(Map<String, dynamic> json) => AssessmentTask(
|
||||
id: json['id'] as String,
|
||||
skill: AssessmentSkill.values.byName(json['skill'] as String),
|
||||
prompt: json['prompt'] as String? ?? '',
|
||||
audio: json['audio'] as String?,
|
||||
choices: [for (final choice in json['choices'] as List? ?? const []) '$choice'],
|
||||
answerIndex: json['answerIndex'] as int?,
|
||||
accept: [
|
||||
for (final group in json['accept'] as List? ?? const [])
|
||||
[for (final term in group as List) '$term'],
|
||||
],
|
||||
guide: json['guide'] as String? ?? '',
|
||||
);
|
||||
|
||||
final String id;
|
||||
final AssessmentSkill skill;
|
||||
final String prompt;
|
||||
final String? audio;
|
||||
final List<String> choices;
|
||||
final int? answerIndex;
|
||||
final List<List<String>> accept;
|
||||
final String guide;
|
||||
}
|
||||
|
||||
extension AssessmentSkillLabel on AssessmentSkill {
|
||||
String get label => switch (this) {
|
||||
AssessmentSkill.listening => '听力',
|
||||
AssessmentSkill.speaking => '口语',
|
||||
AssessmentSkill.reading => '阅读',
|
||||
AssessmentSkill.writing => '写作',
|
||||
};
|
||||
}
|
||||
|
||||
/// A stage-assessment pack. A replacement pack ([replaces] set) assesses the
|
||||
/// same abilities as its original with different people, places and values,
|
||||
/// and its results count toward the original.
|
||||
class AssessmentPack {
|
||||
const AssessmentPack({
|
||||
required this.id,
|
||||
required this.level,
|
||||
required this.tasks,
|
||||
this.replaces,
|
||||
});
|
||||
|
||||
factory AssessmentPack.fromJson(Map<String, dynamic> json) => AssessmentPack(
|
||||
id: json['id'] as String,
|
||||
level: json['level'] as String? ?? '',
|
||||
replaces: json['replaces'] as String?,
|
||||
tasks: [
|
||||
for (final task in json['tasks'] as List? ?? const [])
|
||||
AssessmentTask.fromJson(task as Map<String, dynamic>),
|
||||
],
|
||||
);
|
||||
|
||||
final String id;
|
||||
final String level;
|
||||
final String? replaces;
|
||||
final List<AssessmentTask> tasks;
|
||||
|
||||
List<AssessmentTask> forSkill(AssessmentSkill skill) =>
|
||||
tasks.where((task) => task.skill == skill).toList();
|
||||
}
|
||||
|
||||
/// Every loaded assessment pack, in file order; filled by `CourseRepository`.
|
||||
final List<AssessmentPack> assessmentPacks = <AssessmentPack>[];
|
||||
|
||||
/// The A0 stage assessments, in the order they unlock.
|
||||
List<AssessmentPack> get a0AssessmentPacks => [
|
||||
for (final pack in assessmentPacks)
|
||||
if (pack.level == 'A0' && pack.replaces == null) pack,
|
||||
];
|
||||
|
||||
/// The A0 replacement packs.
|
||||
List<AssessmentPack> get a0ReplacementPacks => [
|
||||
for (final pack in assessmentPacks)
|
||||
if (pack.level == 'A0' && pack.replaces != null) pack,
|
||||
];
|
||||
|
||||
/// The replacement pack of [packId], if it has one.
|
||||
AssessmentPack? replacementFor(String packId) =>
|
||||
assessmentPacks.where((pack) => pack.replaces == packId).firstOrNull;
|
||||
|
||||
/// The pack whose results [packId] counts toward: the original of a
|
||||
/// replacement pack, otherwise [packId] itself.
|
||||
String canonicalAssessmentPackId(String packId) =>
|
||||
assessmentPacks
|
||||
.where((pack) => pack.id == packId)
|
||||
.firstOrNull
|
||||
?.replaces ??
|
||||
packId;
|
||||
|
||||
/// Local check of an open task with the same rule engine as the lessons: it
|
||||
/// accepts variable names and places, but requires the communicative
|
||||
/// information the task names.
|
||||
bool checkOpenAssessmentAnswer(AssessmentTask task, String input) =>
|
||||
task.accept.isNotEmpty && matchesRule(input, task.accept);
|
||||
|
||||
/// What a correct answer to [task] looks like, shown when reviewing a
|
||||
/// wrong answer after the assessment. [answer] is what the learner gave.
|
||||
String assessmentAnswerGuide(AssessmentTask task, String answer) {
|
||||
final index = task.answerIndex;
|
||||
if (index != null && index >= 0 && index < task.choices.length) {
|
||||
final correct = task.choices[index];
|
||||
if (task.skill == AssessmentSkill.listening && answer == correct) {
|
||||
return '正确答案:$correct(选对了,但作答前没有播放音频,所以不计分)';
|
||||
}
|
||||
return '正确答案:$correct';
|
||||
}
|
||||
if (task.guide.isNotEmpty) return task.guide;
|
||||
return task.skill == AssessmentSkill.writing
|
||||
? '需要写一个完整的英文句子。'
|
||||
: '需要用完整英文回答题目要求。';
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import 'course_pack.dart';
|
||||
|
||||
/// A produced core item as the registries see it: the level of the pack that
|
||||
/// teaches it and its English and Chinese forms.
|
||||
typedef RegisteredCoreItem = ({String level, String en, String zh});
|
||||
|
||||
/// Every produced core item across A0–B1, in course order, filled from the
|
||||
/// packs' `coreItems` by `CourseRepository`. IDs are separate from the
|
||||
/// wording and never change when a course accepts a slot value or
|
||||
/// contraction, because saved progress refers to them.
|
||||
final Map<String, RegisteredCoreItem> coreItemRegistry =
|
||||
<String, RegisteredCoreItem>{};
|
||||
|
||||
/// Whether [id] is an A0 core item. A0 items make up the frozen A0 v1
|
||||
/// upgrade denominator from A0-STAGE-STANDARD.md.
|
||||
bool isA0CoreItem(String id) => coreItemRegistry[id]?.level == 'A0';
|
||||
|
||||
/// The A0 core items, id → English, in course order.
|
||||
Map<String, String> get a0CoreItems => {
|
||||
for (final entry in coreItemRegistry.entries)
|
||||
if (entry.value.level == 'A0') entry.key: entry.value.en,
|
||||
};
|
||||
|
||||
class CoreReviewTemplate {
|
||||
const CoreReviewTemplate({
|
||||
required this.prompt,
|
||||
required this.hint,
|
||||
required this.skill,
|
||||
});
|
||||
|
||||
final String prompt;
|
||||
final String hint;
|
||||
final String skill;
|
||||
}
|
||||
|
||||
/// Review data of every core item, straight from the course JSON: the acceptance rule (see `answer_rules.dart`), the offline prompt,
|
||||
/// the hint after a miss and an optional dictation sentence.
|
||||
final Map<String, List<List<String>>> coreItemMatch =
|
||||
<String, List<List<String>>>{};
|
||||
final Map<String, CoreReviewTemplate> coreReviewTemplates =
|
||||
<String, CoreReviewTemplate>{};
|
||||
final Map<String, String> coreReviewHints = <String, String>{};
|
||||
final Map<String, DictationSentence> coreDictationSentences =
|
||||
<String, DictationSentence>{};
|
||||
|
||||
/// Registers [item], taught at [level], with its review data.
|
||||
void registerCoreItem(CoreItem item, {required String level}) {
|
||||
coreItemRegistry[item.id] = (level: level, en: item.en, zh: item.zh);
|
||||
coreItemMatch[item.id] = item.match;
|
||||
coreReviewTemplates[item.id] = CoreReviewTemplate(
|
||||
prompt: item.reviewPrompt.isNotEmpty
|
||||
? item.reviewPrompt
|
||||
: '用英文表达“${item.zh}”。',
|
||||
hint: item.en,
|
||||
skill: item.type == 'word' ? '词汇回忆' : '回忆表达',
|
||||
);
|
||||
if (item.reviewHint.isNotEmpty) coreReviewHints[item.id] = item.reviewHint;
|
||||
if (item.dictationSentence.isNotEmpty) {
|
||||
coreDictationSentences[item.id] = (
|
||||
sentence: item.dictationSentence,
|
||||
meaning: item.dictationMeaning,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Empties every core-item registry, for `CourseRepository.resetForTest`.
|
||||
void clearCoreItems() {
|
||||
coreItemRegistry.clear();
|
||||
coreItemMatch.clear();
|
||||
coreReviewTemplates.clear();
|
||||
coreReviewHints.clear();
|
||||
coreDictationSentences.clear();
|
||||
}
|
||||
|
||||
/// The label a learner sees for a core item, falling back to its id. A0
|
||||
/// items are labelled by their English (a beginner recognises the phrase);
|
||||
/// later levels by their Chinese meaning.
|
||||
String coreItemLabel(String id) {
|
||||
final item = coreItemRegistry[id];
|
||||
if (item == null) return id;
|
||||
return item.level == 'A0' ? item.en : item.zh;
|
||||
}
|
||||
|
||||
/// The English form of any core item; prompts that constrain the AI's
|
||||
/// English use this.
|
||||
String coreItemEnglish(String id) => coreItemRegistry[id]?.en ?? id;
|
||||
|
||||
/// Whether [id] is a produced core item the course tracks for mastery.
|
||||
bool isCoreItem(String id) => coreItemRegistry.containsKey(id);
|
||||
|
||||
/// Every produced core item id across A0–B1, in course order.
|
||||
Iterable<String> get allCoreItemIds => coreItemRegistry.keys;
|
||||
|
||||
/// Offline, reviewed prompts keep initial reviews usable without an AI service.
|
||||
/// The prompt supplies a meaning or situation, never the English target.
|
||||
CoreReviewTemplate coreReviewTemplate(String id) =>
|
||||
coreReviewTemplates[id] ??
|
||||
CoreReviewTemplate(prompt: '把“这个已学词”写成英文。', hint: id, skill: '词汇回忆');
|
||||
|
||||
/// Rotates an approved offline prompt family without changing the core target
|
||||
/// ID. The rotation changes the skill, not just the wording: recall in
|
||||
/// writing, dictation from audio, then saying it aloud.
|
||||
CoreReviewTemplate coreReviewVariant(String id, int variantIndex) {
|
||||
final base = coreReviewTemplate(id);
|
||||
final dictation = coreDictationSentences[id];
|
||||
return switch (variantIndex % 3) {
|
||||
1 when dictation != null => CoreReviewTemplate(
|
||||
prompt: '听一句话,把听到的整句英文写下来。',
|
||||
hint: dictation.meaning,
|
||||
skill: dictationSkill,
|
||||
),
|
||||
2 => CoreReviewTemplate(
|
||||
prompt: '用英文说出来:${base.prompt}',
|
||||
hint: base.hint,
|
||||
skill: spokenRecallSkill,
|
||||
),
|
||||
_ => base,
|
||||
};
|
||||
}
|
||||
|
||||
const dictationSkill = '听写';
|
||||
const spokenRecallSkill = '口头回忆';
|
||||
|
||||
/// A concrete sentence built only from taught language, used to play a core
|
||||
/// item in context and to check dictation.
|
||||
typedef DictationSentence = ({String sentence, String meaning});
|
||||
|
||||
/// What review audio should play for [item]: the dictation sentence for a
|
||||
/// core item, otherwise the target itself when it has no slot to fill.
|
||||
String? reviewAudioText(String id, String target) =>
|
||||
coreDictationSentences[id]?.sentence ??
|
||||
(target.contains('[') ? null : target);
|
||||
@@ -1,129 +0,0 @@
|
||||
import 'dart:math';
|
||||
|
||||
import '../models.dart';
|
||||
import 'assessment.dart';
|
||||
import 'core_items.dart';
|
||||
import 'course_models.dart';
|
||||
|
||||
// Course registries, filled by `CourseRepository` from the content packs in
|
||||
// assets/courses. Every level, A0 included, goes through the same adapter, so
|
||||
// these hold one kind of data per key whatever level it came from.
|
||||
final List<SeedLesson> courseLessons = <SeedLesson>[];
|
||||
final Map<String, LessonSegment> courseSegments = <String, LessonSegment>{};
|
||||
final Map<String, LessonActivity> segmentActivities =
|
||||
<String, LessonActivity>{};
|
||||
final Map<String, List<VocabularyItem>> segmentVocabulary =
|
||||
<String, List<VocabularyItem>>{};
|
||||
final Map<String, LessonDialogue> segmentDialogues = <String, LessonDialogue>{};
|
||||
final Map<String, String> segmentGrammarNotes = <String, String>{};
|
||||
final List<DialogueScene> practiceScenes = <DialogueScene>[];
|
||||
|
||||
/// Empties every registry, for `CourseRepository.resetForTest`.
|
||||
void clearCourseCatalog() {
|
||||
courseLessons.clear();
|
||||
courseSegments.clear();
|
||||
segmentActivities.clear();
|
||||
segmentVocabulary.clear();
|
||||
segmentDialogues.clear();
|
||||
segmentGrammarNotes.clear();
|
||||
practiceScenes.clear();
|
||||
assessmentPacks.clear();
|
||||
}
|
||||
|
||||
/// Every lesson the app can teach, in learning order: A0 first, then the
|
||||
/// A1→A2→B1 units.
|
||||
List<SeedLesson> get allLessons => courseLessons;
|
||||
|
||||
/// The A0 lessons, which lead to the A0 stage assessment.
|
||||
List<SeedLesson> get a0SeedLessons =>
|
||||
courseLessons.where((lesson) => lesson.level == 'A0').toList();
|
||||
|
||||
/// Every free-practice scene, in course order.
|
||||
List<DialogueScene> get allScenes => practiceScenes;
|
||||
|
||||
/// Finds a teaching segment by id.
|
||||
LessonSegment? findLessonSegment(String segmentId) => courseSegments[segmentId];
|
||||
|
||||
SeedLesson lessonById(String id) => allLessons.firstWhere(
|
||||
(lesson) => lesson.id == id,
|
||||
orElse: () => allLessons.first,
|
||||
);
|
||||
|
||||
DialogueScene sceneById(String id) =>
|
||||
allScenes.where((scene) => scene.id == id).firstOrNull ?? allScenes.first;
|
||||
|
||||
LessonActivity activityBySegmentId(String segmentId) =>
|
||||
segmentActivities[segmentId] ?? _emptyActivity;
|
||||
|
||||
const LessonActivity _emptyActivity = LessonActivity(
|
||||
listening: '',
|
||||
listeningQuestion: '',
|
||||
answers: [''],
|
||||
speaking: '',
|
||||
reading: '',
|
||||
readingQuestion: '',
|
||||
readingAnswer: '',
|
||||
writingPrompt: '',
|
||||
writingExample: '',
|
||||
independentPrompt: '',
|
||||
independentHelp: '',
|
||||
);
|
||||
|
||||
List<VocabularyItem> vocabularyBySegmentId(String segmentId) =>
|
||||
segmentVocabulary[segmentId] ?? const <VocabularyItem>[];
|
||||
|
||||
/// One practical rule per segment: a reminder for using a sentence in
|
||||
/// context, not an abstract grammar lecture.
|
||||
String grammarNoteForSegment(String segmentId) =>
|
||||
segmentGrammarNotes[segmentId] ?? '';
|
||||
|
||||
LessonDialogue dialogueBySegmentId(String segmentId) =>
|
||||
segmentDialogues[segmentId] ?? _emptyDialogue;
|
||||
|
||||
const LessonDialogue _emptyDialogue = LessonDialogue(
|
||||
goal: '',
|
||||
prompts: [],
|
||||
hints: [],
|
||||
);
|
||||
|
||||
/// How many A1+ units, the current one included, the AI partner's word list
|
||||
/// spans. Earlier units stay usable as ordinary easy English; listing every
|
||||
/// unit would resend hundreds of items with each dialogue turn.
|
||||
const int taughtLanguageUnitWindow = 3;
|
||||
|
||||
/// The English a learner has actually met up to and including [lessonId].
|
||||
/// The AI partner is told to build its lines from this list so a beginner
|
||||
/// never gets an answer built from words the course has not taught yet. In A0
|
||||
/// it is everything taught so far; from A1 on it is the recent units only.
|
||||
/// The order is fixed so repeated prompts share a cacheable prefix.
|
||||
List<String> taughtLanguageUpTo(String lessonId) {
|
||||
final lessons = allLessons;
|
||||
final found = lessons.indexWhere((lesson) => lesson.id == lessonId);
|
||||
final end = found < 0 ? lessons.length - 1 : found;
|
||||
final start = lessons[end].level == 'A0'
|
||||
? 0
|
||||
: max(end - taughtLanguageUnitWindow + 1, a0SeedLessons.length);
|
||||
return <String>{
|
||||
for (final lesson in lessons.sublist(start, end + 1))
|
||||
for (final id in lesson.targetItemIds) coreItemEnglish(id),
|
||||
}.toList();
|
||||
}
|
||||
|
||||
/// Every A0 item, for the always-open free scene that mixes all A0 topics.
|
||||
List<String> get a0TaughtLanguage => a0CoreItems.values.toList();
|
||||
|
||||
/// The CEFR level of the lesson that teaches [itemId], A0 when it is unknown.
|
||||
String itemLevel(String itemId) {
|
||||
for (final lesson in allLessons) {
|
||||
if (lesson.targetItemIds.contains(itemId)) return lesson.level;
|
||||
}
|
||||
return 'A0';
|
||||
}
|
||||
|
||||
/// The CEFR level of [lessonId], A0 when it is unknown.
|
||||
String lessonLevel(String lessonId) {
|
||||
for (final lesson in allLessons) {
|
||||
if (lesson.id == lessonId) return lesson.level;
|
||||
}
|
||||
return 'A0';
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
import '../models.dart';
|
||||
|
||||
/// A topic may contain multiple independently resumable teaching segments.
|
||||
/// Segment IDs are stable even if later wording or exercises change.
|
||||
class LessonSegment {
|
||||
const LessonSegment({
|
||||
required this.id,
|
||||
required this.targetItemIds,
|
||||
required this.previewItemIds,
|
||||
this.dialogueRequiredTerms = const [],
|
||||
this.independentRequiredTerms = const [],
|
||||
this.estimatedMinutes = 12,
|
||||
});
|
||||
|
||||
|
||||
final String id;
|
||||
final List<String> targetItemIds;
|
||||
final List<String> previewItemIds;
|
||||
|
||||
/// One accepted term group per controlled dialogue turn. A learner only
|
||||
/// needs to use one term from the current group; the surrounding wording
|
||||
/// may vary naturally.
|
||||
final List<List<String>> dialogueRequiredTerms;
|
||||
|
||||
/// The rule an independent attempt has to satisfy (see
|
||||
/// `answer_rules.dart`); empty accepts any real attempt.
|
||||
final List<List<String>> independentRequiredTerms;
|
||||
final int estimatedMinutes;
|
||||
}
|
||||
|
||||
class SeedLesson {
|
||||
const SeedLesson({
|
||||
required this.id,
|
||||
required this.number,
|
||||
required this.title,
|
||||
required this.outcome,
|
||||
required this.vocabulary,
|
||||
required this.targetItemIds,
|
||||
this.schemaVersion = '1.0',
|
||||
this.revision = 1,
|
||||
this.stageVersion = 'A0-1.0',
|
||||
this.source = ContentSource.builtInOriginal,
|
||||
this.status = ContentStatus.approved,
|
||||
this.estimatedMinutes = 12,
|
||||
this.level = 'A0',
|
||||
this.explicitSegments,
|
||||
});
|
||||
final String id;
|
||||
final int number;
|
||||
final String title;
|
||||
final String outcome;
|
||||
final List<VocabularyItem> vocabulary;
|
||||
final List<String> targetItemIds;
|
||||
final String schemaVersion;
|
||||
final int revision;
|
||||
final String stageVersion;
|
||||
final ContentSource source;
|
||||
final ContentStatus status;
|
||||
final int estimatedMinutes;
|
||||
|
||||
/// CEFR level this lesson belongs to (`A0`, `A1`, `A2`, `B1`).
|
||||
final String level;
|
||||
|
||||
/// Set when the lesson is split into several teaching segments; otherwise
|
||||
/// the whole lesson is taught as one implicit segment.
|
||||
final List<LessonSegment>? explicitSegments;
|
||||
|
||||
List<String> get abilityIds => ['A0-C${number.toString().padLeft(2, '0')}'];
|
||||
List<String> get prerequisiteIds => number == 1
|
||||
? const []
|
||||
: ['a0-${(number - 1).toString().padLeft(2, '0')}'];
|
||||
List<LessonSegment> get segments =>
|
||||
explicitSegments ??
|
||||
[
|
||||
LessonSegment(
|
||||
id: '$id-a',
|
||||
targetItemIds: targetItemIds,
|
||||
previewItemIds: targetItemIds,
|
||||
estimatedMinutes: estimatedMinutes,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
class LessonActivity {
|
||||
const LessonActivity({
|
||||
required this.listening,
|
||||
required this.listeningQuestion,
|
||||
required this.answers,
|
||||
required this.speaking,
|
||||
this.speakingTip = '',
|
||||
required this.reading,
|
||||
required this.readingQuestion,
|
||||
required this.readingAnswer,
|
||||
this.readingOptions = const [],
|
||||
required this.writingPrompt,
|
||||
required this.writingExample,
|
||||
this.writingRequiredTerms = const [],
|
||||
this.writingHint = '',
|
||||
required this.independentPrompt,
|
||||
required this.independentHelp,
|
||||
});
|
||||
|
||||
|
||||
final String listening;
|
||||
final String listeningQuestion;
|
||||
final List<String> answers;
|
||||
final String speaking;
|
||||
|
||||
/// 这句跟读的发音提示;为空时不显示,避免给每一句套用同一条提示。
|
||||
final String speakingTip;
|
||||
final String reading;
|
||||
final String readingQuestion;
|
||||
final String readingAnswer;
|
||||
final List<String> readingOptions;
|
||||
final String writingPrompt;
|
||||
final String writingExample;
|
||||
|
||||
/// The rule a written answer has to satisfy (see `answer_rules.dart`);
|
||||
/// empty means any sentence of two or more words.
|
||||
final List<List<String>> writingRequiredTerms;
|
||||
|
||||
/// What to tell a learner whose written answer misses the rule.
|
||||
final String writingHint;
|
||||
final String independentPrompt;
|
||||
final String independentHelp;
|
||||
}
|
||||
|
||||
class LessonDialogue {
|
||||
const LessonDialogue({
|
||||
required this.goal,
|
||||
required this.prompts,
|
||||
required this.hints,
|
||||
this.translations = const [],
|
||||
this.requiredTerms = const [],
|
||||
this.taskLabels = const [],
|
||||
});
|
||||
|
||||
|
||||
final String goal;
|
||||
final List<String> prompts;
|
||||
final List<String> hints;
|
||||
final List<String> translations;
|
||||
|
||||
/// One accepted term group per learner turn. The learner only needs to hit
|
||||
/// one term in the current group, so natural wording still passes. A term
|
||||
/// may be a plain word or phrase, an `a + b` conjunction (both parts are
|
||||
/// required), or a structural token starting with `#`.
|
||||
final List<List<String>> requiredTerms;
|
||||
|
||||
/// Chinese label of what each learner turn has to do. It drives the
|
||||
/// "还没完成本轮任务" message and the summary of completed tasks, so it must
|
||||
/// describe the task without giving away the model answer.
|
||||
final List<String> taskLabels;
|
||||
}
|
||||
|
||||
/// A free practice scene. It opens once [unlockAfterLessonId] is complete
|
||||
/// (always open when null) and only uses language taught up to that lesson.
|
||||
class DialogueScene {
|
||||
const DialogueScene({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.summary,
|
||||
required this.script,
|
||||
required this.recapPrompt,
|
||||
this.unlockAfterLessonId,
|
||||
});
|
||||
|
||||
|
||||
final String id;
|
||||
final String title;
|
||||
final String summary;
|
||||
final LessonDialogue script;
|
||||
|
||||
/// The follow-up review prompt added after the scene is finished.
|
||||
final String recapPrompt;
|
||||
final String? unlockAfterLessonId;
|
||||
}
|
||||
@@ -1,480 +0,0 @@
|
||||
/// Runtime models for the course content packs (`assets/courses/`), A0
|
||||
/// through B1: every level uses the same structure.
|
||||
///
|
||||
/// These mirror the JSON described in `Doc/COURSE-PACK-JSON.md`. They are pure
|
||||
/// data with tolerant `fromJson` factories: a missing or malformed field never
|
||||
/// throws, it degrades to an empty value so a single bad unit can be skipped
|
||||
/// without taking down the loader.
|
||||
///
|
||||
/// Answer rules (`accept`, `match`) use the language of `answer_rules.dart`.
|
||||
/// Where a pack gives no explicit rule, the loader derives one from the core
|
||||
/// items the part teaches (`itemIds`).
|
||||
library;
|
||||
|
||||
import '../models.dart';
|
||||
|
||||
/// One multiple-choice question on a listening/reading material or segment text.
|
||||
/// The first option is always the correct answer (the UI shuffles positions).
|
||||
class PackQuestion {
|
||||
const PackQuestion({
|
||||
required this.type,
|
||||
required this.question,
|
||||
required this.options,
|
||||
});
|
||||
|
||||
/// `gist` / `detail` / `inference`.
|
||||
final String type;
|
||||
final String question;
|
||||
final List<String> options;
|
||||
|
||||
String get answer => options.isNotEmpty ? options.first : '';
|
||||
|
||||
factory PackQuestion.fromJson(Map<String, dynamic> json) => PackQuestion(
|
||||
type: json['type'] as String? ?? 'detail',
|
||||
question: json['question'] as String? ?? '',
|
||||
options: _stringList(json['options']),
|
||||
);
|
||||
}
|
||||
|
||||
/// A short listening or reading comprehension text with glosses and questions.
|
||||
class PackMaterial {
|
||||
const PackMaterial({
|
||||
required this.id,
|
||||
required this.mode,
|
||||
required this.title,
|
||||
required this.text,
|
||||
required this.glosses,
|
||||
required this.questions,
|
||||
});
|
||||
|
||||
final String id;
|
||||
|
||||
/// `listening` or `reading`.
|
||||
final String mode;
|
||||
final String title;
|
||||
final String text;
|
||||
|
||||
/// Untaught word -> Chinese gloss shown alongside the text.
|
||||
final Map<String, String> glosses;
|
||||
final List<PackQuestion> questions;
|
||||
|
||||
factory PackMaterial.fromJson(Map<String, dynamic> json) => PackMaterial(
|
||||
id: json['id'] as String? ?? '',
|
||||
mode: json['mode'] as String? ?? 'reading',
|
||||
title: json['title'] as String? ?? '',
|
||||
text: json['text'] as String? ?? '',
|
||||
glosses: _stringMap(json['glosses']),
|
||||
questions: _objectList(
|
||||
json['questions'],
|
||||
).map(PackQuestion.fromJson).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// One turn of a controlled scene. `model` is the learner's demonstrated line.
|
||||
class SceneTurn {
|
||||
const SceneTurn({
|
||||
required this.ai,
|
||||
required this.goal,
|
||||
required this.model,
|
||||
required this.zh,
|
||||
required this.itemIds,
|
||||
this.aiZh = '',
|
||||
this.accept,
|
||||
});
|
||||
|
||||
final String ai;
|
||||
final String goal;
|
||||
final String model;
|
||||
|
||||
/// Chinese of [model].
|
||||
final String zh;
|
||||
final List<String> itemIds;
|
||||
|
||||
/// Chinese of [ai], offered as the translation of the partner's line.
|
||||
final String aiZh;
|
||||
|
||||
/// The terms that pass this turn (one term group); null derives them from
|
||||
/// [itemIds].
|
||||
final List<String>? accept;
|
||||
|
||||
factory SceneTurn.fromJson(Map<String, dynamic> json) => SceneTurn(
|
||||
ai: json['ai'] as String? ?? '',
|
||||
goal: json['goal'] as String? ?? '',
|
||||
model: json['model'] as String? ?? '',
|
||||
zh: json['zh'] as String? ?? '',
|
||||
itemIds: _stringList(json['itemIds']),
|
||||
aiZh: json['aiZh'] as String? ?? '',
|
||||
accept: json['accept'] is List ? _stringList(json['accept']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Marks a scene as a free-practice scene on the practice page. Empty fields
|
||||
/// fall back to values built from the scene and its unit.
|
||||
class ScenePractice {
|
||||
const ScenePractice({
|
||||
this.title = '',
|
||||
this.summary = '',
|
||||
this.recapPrompt = '',
|
||||
this.alwaysOpen = false,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String summary;
|
||||
|
||||
/// The review prompt added after the scene is finished.
|
||||
final String recapPrompt;
|
||||
|
||||
/// Open from the start instead of after the unit is finished.
|
||||
final bool alwaysOpen;
|
||||
|
||||
factory ScenePractice.fromJson(Map<String, dynamic> json) => ScenePractice(
|
||||
title: json['title'] as String? ?? '',
|
||||
summary: json['summary'] as String? ?? '',
|
||||
recapPrompt: json['recapPrompt'] as String? ?? '',
|
||||
alwaysOpen: json['alwaysOpen'] == true,
|
||||
);
|
||||
}
|
||||
|
||||
/// A role-play scene (`kind` is `main` or `variant`).
|
||||
class PackScene {
|
||||
const PackScene({
|
||||
required this.id,
|
||||
required this.kind,
|
||||
required this.title,
|
||||
required this.learnerRole,
|
||||
required this.aiRole,
|
||||
required this.setting,
|
||||
required this.turns,
|
||||
this.goal = '',
|
||||
this.practice,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String kind;
|
||||
final String title;
|
||||
final String learnerRole;
|
||||
final String aiRole;
|
||||
final String setting;
|
||||
final List<SceneTurn> turns;
|
||||
|
||||
/// What the whole dialogue practises; falls back to [setting] or [title].
|
||||
final String goal;
|
||||
|
||||
/// Set when the scene is also offered as free practice.
|
||||
final ScenePractice? practice;
|
||||
|
||||
factory PackScene.fromJson(Map<String, dynamic> json) => PackScene(
|
||||
id: json['id'] as String? ?? '',
|
||||
kind: json['kind'] as String? ?? 'variant',
|
||||
title: json['title'] as String? ?? '',
|
||||
learnerRole: json['learnerRole'] as String? ?? '',
|
||||
aiRole: json['aiRole'] as String? ?? '',
|
||||
setting: json['setting'] as String? ?? '',
|
||||
turns: _objectList(json['turns']).map(SceneTurn.fromJson).toList(),
|
||||
goal: json['goal'] as String? ?? '',
|
||||
practice: json['practice'] is Map<String, dynamic>
|
||||
? ScenePractice.fromJson(json['practice'] as Map<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// An open production task attached to a scene.
|
||||
class PackTask {
|
||||
const PackTask({
|
||||
required this.id,
|
||||
required this.sceneId,
|
||||
required this.goal,
|
||||
required this.itemIds,
|
||||
required this.slots,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String sceneId;
|
||||
final String goal;
|
||||
final List<String> itemIds;
|
||||
final List<String> slots;
|
||||
|
||||
factory PackTask.fromJson(Map<String, dynamic> json) => PackTask(
|
||||
id: json['id'] as String? ?? '',
|
||||
sceneId: json['sceneId'] as String? ?? '',
|
||||
goal: json['goal'] as String? ?? '',
|
||||
itemIds: _stringList(json['itemIds']),
|
||||
slots: _stringList(json['slots']),
|
||||
);
|
||||
}
|
||||
|
||||
/// A resumable teaching segment with its listening/speaking/reading/writing/
|
||||
/// independent activities.
|
||||
class PackSegment {
|
||||
const PackSegment({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.minutes,
|
||||
required this.itemIds,
|
||||
required this.listeningText,
|
||||
required this.listeningQuestion,
|
||||
required this.listeningOptions,
|
||||
required this.speakingText,
|
||||
required this.speakingTip,
|
||||
required this.readingText,
|
||||
required this.readingQuestion,
|
||||
required this.readingOptions,
|
||||
required this.writingPrompt,
|
||||
required this.writingExample,
|
||||
required this.writingItemIds,
|
||||
required this.independentPrompt,
|
||||
required this.independentItemIds,
|
||||
this.grammarNote = '',
|
||||
this.vocabulary,
|
||||
this.sceneId = '',
|
||||
this.writingAccept,
|
||||
this.writingHint = '',
|
||||
this.independentAccept,
|
||||
this.independentHelp = '',
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String title;
|
||||
final int minutes;
|
||||
final List<String> itemIds;
|
||||
|
||||
final String listeningText;
|
||||
final String listeningQuestion;
|
||||
final List<String> listeningOptions;
|
||||
|
||||
final String speakingText;
|
||||
final String speakingTip;
|
||||
|
||||
final String readingText;
|
||||
final String readingQuestion;
|
||||
final List<String> readingOptions;
|
||||
|
||||
final String writingPrompt;
|
||||
final String writingExample;
|
||||
final List<String> writingItemIds;
|
||||
|
||||
final String independentPrompt;
|
||||
final List<String> independentItemIds;
|
||||
|
||||
/// One practical usage reminder shown while writing.
|
||||
final String grammarNote;
|
||||
|
||||
/// The preview words; null derives them from the core items in [itemIds].
|
||||
final List<VocabularyItem>? vocabulary;
|
||||
|
||||
/// The scene practised in this segment; empty picks the scene at the same
|
||||
/// position (or the main scene).
|
||||
final String sceneId;
|
||||
|
||||
/// Explicit writing / independent rules and messages. Null or empty values
|
||||
/// are derived from [writingItemIds] / [independentItemIds].
|
||||
final List<List<String>>? writingAccept;
|
||||
final String writingHint;
|
||||
final List<List<String>>? independentAccept;
|
||||
final String independentHelp;
|
||||
|
||||
factory PackSegment.fromJson(Map<String, dynamic> json) {
|
||||
final listening = _objectOrEmpty(json['listening']);
|
||||
final reading = _objectOrEmpty(json['reading']);
|
||||
final writing = _objectOrEmpty(json['writing']);
|
||||
final independent = _objectOrEmpty(json['independent']);
|
||||
final speaking = _objectList(json['speaking']);
|
||||
final firstSpeaking = speaking.isNotEmpty ? speaking.first : const {};
|
||||
return PackSegment(
|
||||
id: json['id'] as String? ?? '',
|
||||
title: json['title'] as String? ?? '',
|
||||
minutes: (json['minutes'] as num?)?.toInt() ?? 12,
|
||||
itemIds: _stringList(json['itemIds']),
|
||||
listeningText: listening['text'] as String? ?? '',
|
||||
listeningQuestion: listening['question'] as String? ?? '',
|
||||
listeningOptions: _stringList(listening['options']),
|
||||
speakingText: firstSpeaking['text'] as String? ?? '',
|
||||
speakingTip: firstSpeaking['tip'] as String? ?? '',
|
||||
readingText: reading['text'] as String? ?? '',
|
||||
readingQuestion: reading['question'] as String? ?? '',
|
||||
readingOptions: _stringList(reading['options']),
|
||||
writingPrompt: writing['prompt'] as String? ?? '',
|
||||
writingExample: writing['example'] as String? ?? '',
|
||||
writingItemIds: _stringList(writing['itemIds']),
|
||||
independentPrompt: independent['prompt'] as String? ?? '',
|
||||
independentItemIds: _stringList(independent['itemIds']),
|
||||
grammarNote: json['grammarNote'] as String? ?? '',
|
||||
vocabulary: _vocabulary(json['vocabulary']),
|
||||
sceneId: json['sceneId'] as String? ?? '',
|
||||
writingAccept: writing['accept'] is List
|
||||
? _stringMatrix(writing['accept'])
|
||||
: null,
|
||||
writingHint: writing['hint'] as String? ?? '',
|
||||
independentAccept: independent['accept'] is List
|
||||
? _stringMatrix(independent['accept'])
|
||||
: null,
|
||||
independentHelp: independent['help'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A produced core item (word / phrase / pattern) with its acceptance rule.
|
||||
class CoreItem {
|
||||
const CoreItem({
|
||||
required this.id,
|
||||
required this.type,
|
||||
required this.en,
|
||||
required this.zh,
|
||||
required this.match,
|
||||
required this.exampleEn,
|
||||
required this.exampleZh,
|
||||
required this.reviewPrompt,
|
||||
this.reviewHint = '',
|
||||
this.dictationSentence = '',
|
||||
this.dictationMeaning = '',
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String type;
|
||||
final String en;
|
||||
final String zh;
|
||||
|
||||
/// Outer list = AND groups; inner list = OR alternatives.
|
||||
final List<List<String>> match;
|
||||
final String exampleEn;
|
||||
final String exampleZh;
|
||||
final String reviewPrompt;
|
||||
|
||||
/// What to tell a learner whose review answer missed the item.
|
||||
final String reviewHint;
|
||||
|
||||
/// A sentence built only from taught language, played in context and used
|
||||
/// as a dictation review. Empty when the item has none.
|
||||
final String dictationSentence;
|
||||
final String dictationMeaning;
|
||||
|
||||
factory CoreItem.fromJson(Map<String, dynamic> json) {
|
||||
final example = _objectOrEmpty(json['example']);
|
||||
final review = _objectOrEmpty(json['review']);
|
||||
final dictation = _objectOrEmpty(review['dictation']);
|
||||
return CoreItem(
|
||||
id: json['id'] as String? ?? '',
|
||||
type: json['type'] as String? ?? 'word',
|
||||
en: json['en'] as String? ?? '',
|
||||
zh: json['zh'] as String? ?? '',
|
||||
match: _stringMatrix(json['match']),
|
||||
exampleEn: example['en'] as String? ?? '',
|
||||
exampleZh: example['zh'] as String? ?? '',
|
||||
reviewPrompt: review['prompt'] as String? ?? '',
|
||||
reviewHint: review['hint'] as String? ?? '',
|
||||
dictationSentence: dictation['sentence'] as String? ?? '',
|
||||
dictationMeaning: dictation['meaning'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A recognition-only word.
|
||||
class ReceptiveWord {
|
||||
const ReceptiveWord({required this.id, required this.en, required this.zh});
|
||||
|
||||
final String id;
|
||||
final String en;
|
||||
final String zh;
|
||||
|
||||
factory ReceptiveWord.fromJson(Map<String, dynamic> json) => ReceptiveWord(
|
||||
id: json['id'] as String? ?? '',
|
||||
en: json['en'] as String? ?? '',
|
||||
zh: json['zh'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
/// One unit content pack (a0-01, A1-U01 …).
|
||||
class CoursePack {
|
||||
const CoursePack({
|
||||
required this.id,
|
||||
required this.level,
|
||||
this.status = 'draft',
|
||||
required this.title,
|
||||
required this.canDo,
|
||||
required this.categories,
|
||||
required this.coreItems,
|
||||
required this.receptiveWords,
|
||||
required this.segments,
|
||||
required this.scenes,
|
||||
required this.tasks,
|
||||
required this.materials,
|
||||
this.vocabulary,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String level;
|
||||
|
||||
/// `approved` for reviewed content, otherwise `draft`.
|
||||
final String status;
|
||||
final String title;
|
||||
final String canDo;
|
||||
final List<String> categories;
|
||||
final List<CoreItem> coreItems;
|
||||
final List<ReceptiveWord> receptiveWords;
|
||||
final List<PackSegment> segments;
|
||||
final List<PackScene> scenes;
|
||||
final List<PackTask> tasks;
|
||||
final List<PackMaterial> materials;
|
||||
|
||||
/// The unit's word list for lookup; null derives it from [coreItems] and
|
||||
/// [receptiveWords].
|
||||
final List<VocabularyItem>? vocabulary;
|
||||
|
||||
PackScene? get mainScene =>
|
||||
scenes.where((scene) => scene.kind == 'main').firstOrNull ??
|
||||
(scenes.isNotEmpty ? scenes.first : null);
|
||||
|
||||
factory CoursePack.fromJson(Map<String, dynamic> json) => CoursePack(
|
||||
id: json['id'] as String? ?? '',
|
||||
level: json['level'] as String? ?? '',
|
||||
status: json['status'] as String? ?? 'draft',
|
||||
title: json['title'] as String? ?? '',
|
||||
canDo: json['canDo'] as String? ?? '',
|
||||
categories: _stringList(json['categories']),
|
||||
coreItems: _objectList(json['coreItems']).map(CoreItem.fromJson).toList(),
|
||||
receptiveWords: _objectList(
|
||||
json['receptiveWords'],
|
||||
).map(ReceptiveWord.fromJson).toList(),
|
||||
segments: _objectList(json['segments']).map(PackSegment.fromJson).toList(),
|
||||
scenes: _objectList(json['scenes']).map(PackScene.fromJson).toList(),
|
||||
tasks: _objectList(json['tasks']).map(PackTask.fromJson).toList(),
|
||||
materials: _objectList(
|
||||
json['materials'],
|
||||
).map(PackMaterial.fromJson).toList(),
|
||||
vocabulary: _vocabulary(json['vocabulary']),
|
||||
);
|
||||
}
|
||||
|
||||
// --- tolerant parsing helpers -------------------------------------------------
|
||||
|
||||
List<String> _stringList(dynamic value) => value is List
|
||||
? [
|
||||
for (final item in value)
|
||||
if (item != null) item.toString(),
|
||||
]
|
||||
: const <String>[];
|
||||
|
||||
List<List<String>> _stringMatrix(dynamic value) => value is List
|
||||
? [for (final row in value) _stringList(row)]
|
||||
: const <List<String>>[];
|
||||
|
||||
Map<String, String> _stringMap(dynamic value) => value is Map
|
||||
? {
|
||||
for (final entry in value.entries)
|
||||
entry.key.toString(): entry.value?.toString() ?? '',
|
||||
}
|
||||
: const <String, String>{};
|
||||
|
||||
List<Map<String, dynamic>> _objectList(dynamic value) => value is List
|
||||
? [
|
||||
for (final item in value)
|
||||
if (item is Map<String, dynamic>) item,
|
||||
]
|
||||
: const <Map<String, dynamic>>[];
|
||||
|
||||
List<VocabularyItem>? _vocabulary(dynamic value) => value is List
|
||||
? _objectList(value).map(VocabularyItem.fromJson).toList()
|
||||
: null;
|
||||
|
||||
Map<String, dynamic> _objectOrEmpty(dynamic value) =>
|
||||
value is Map<String, dynamic> ? value : const <String, dynamic>{};
|
||||
@@ -1,336 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle, AssetBundle;
|
||||
|
||||
import 'assessment.dart';
|
||||
import 'core_items.dart';
|
||||
import 'course_catalog.dart';
|
||||
import 'course_models.dart';
|
||||
import '../models.dart';
|
||||
import 'course_pack.dart';
|
||||
|
||||
/// Loads the JSON content packs of every level, A0 through B1, and adapts
|
||||
/// each unit into the runtime models the learning flow consumes
|
||||
/// (`SeedLesson`, `LessonActivity`, `LessonDialogue`, `DialogueScene`,
|
||||
/// `VocabularyItem`).
|
||||
///
|
||||
/// Every level has the same pack structure and goes through the same
|
||||
/// adapter, so learning and review data follow one set of rules. The adapted
|
||||
/// data is registered into the registries in `course_catalog.dart` /
|
||||
/// `core_items.dart`, so the rest of the app keeps calling synchronous
|
||||
/// accessors (`lessonById`, `activityBySegmentId`, …).
|
||||
///
|
||||
/// `main` loads A0 before `runApp`, so the offline baseline is always there;
|
||||
/// the later levels load with the app state. Loading is best-effort: a
|
||||
/// malformed unit is skipped.
|
||||
class CourseRepository {
|
||||
CourseRepository._();
|
||||
|
||||
static final CourseRepository instance = CourseRepository._();
|
||||
|
||||
static const _mapAsset = 'assets/courses/course-map.json';
|
||||
static const _assetRoot = 'assets/courses/';
|
||||
|
||||
final List<CoursePack> _units = [];
|
||||
final Map<String, CoursePack> _unitsById = {};
|
||||
final Set<String> _assessmentLevels = {};
|
||||
bool _loadedAll = false;
|
||||
|
||||
List<CoursePack> get units => List.unmodifiable(_units);
|
||||
CoursePack? unitById(String id) => _unitsById[id];
|
||||
|
||||
/// Parses the course map and the units it lists, then registers the adapted
|
||||
/// content. [levels] limits the load to those levels (`main` loads `A0`
|
||||
/// first). Units already loaded are skipped, so the call is safe to repeat.
|
||||
/// Units are read in parallel but registered in course order, because
|
||||
/// lessons are numbered in the order they are registered; levels therefore
|
||||
/// have to load in course order too.
|
||||
Future<void> load({Set<String>? levels, AssetBundle? bundle}) async {
|
||||
if (_loadedAll) return;
|
||||
final loader = bundle ?? rootBundle;
|
||||
try {
|
||||
final mapRaw = await loader.loadString(_mapAsset);
|
||||
final map = jsonDecode(mapRaw) as Map<String, dynamic>;
|
||||
await _loadAssessments(map, levels, loader);
|
||||
final paths = <String>[
|
||||
for (final level in (map['levels'] as List? ?? const []))
|
||||
if (level is Map && (levels == null || levels.contains(level['id'])))
|
||||
for (final unit in (level['units'] as List? ?? const []))
|
||||
if (unit is Map &&
|
||||
unit['file'] is String &&
|
||||
!_unitsById.containsKey(unit['id']))
|
||||
'$_assetRoot${unit['file']}',
|
||||
];
|
||||
final packs = await Future.wait(
|
||||
paths.map((path) => _readUnit(path, loader)),
|
||||
);
|
||||
for (final pack in packs) {
|
||||
if (pack == null || _unitsById.containsKey(pack.id)) continue;
|
||||
_add(pack);
|
||||
}
|
||||
if (levels == null) _loadedAll = true;
|
||||
} catch (error, stack) {
|
||||
// A missing or broken map leaves whatever units parsed before the
|
||||
// failure in place.
|
||||
debugPrint('CourseRepository: failed to load course map: $error\n$stack');
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the stage assessments of the levels being loaded, from each
|
||||
/// level's `assessment` file in the course map.
|
||||
Future<void> _loadAssessments(
|
||||
Map<String, dynamic> map,
|
||||
Set<String>? levels,
|
||||
AssetBundle loader,
|
||||
) async {
|
||||
for (final level in (map['levels'] as List? ?? const [])) {
|
||||
if (level is! Map || level['assessment'] is! String) continue;
|
||||
if (levels != null && !levels.contains(level['id'])) continue;
|
||||
if (!_assessmentLevels.add(level['id'] as String)) continue;
|
||||
final path = '$_assetRoot${level['assessment']}';
|
||||
try {
|
||||
final json = jsonDecode(await loader.loadString(path)) as Map;
|
||||
for (final pack in json['packs'] as List? ?? const []) {
|
||||
assessmentPacks.add(
|
||||
AssessmentPack.fromJson(pack as Map<String, dynamic>),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
debugPrint('CourseRepository: skipped assessment $path: $error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads one unit; null when it is missing or malformed.
|
||||
Future<CoursePack?> _readUnit(String assetPath, AssetBundle loader) async {
|
||||
try {
|
||||
final raw = await loader.loadString(assetPath);
|
||||
final pack = CoursePack.fromJson(jsonDecode(raw) as Map<String, dynamic>);
|
||||
return pack.id.isEmpty ? null : pack;
|
||||
} catch (error) {
|
||||
debugPrint('CourseRepository: skipped unit $assetPath: $error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void _add(CoursePack pack) {
|
||||
_units.add(pack);
|
||||
_unitsById[pack.id] = pack;
|
||||
_register(pack);
|
||||
}
|
||||
|
||||
/// Adapts one pack into the shared runtime registries. Explicit rules and
|
||||
/// texts in the pack win; anything left out is derived from the core items
|
||||
/// the part teaches.
|
||||
void _register(CoursePack pack) {
|
||||
final byId = {for (final item in pack.coreItems) item.id: item};
|
||||
final scenesById = {for (final scene in pack.scenes) scene.id: scene};
|
||||
|
||||
// Core items: labels, acceptance rules and offline review prompts.
|
||||
for (final item in pack.coreItems) {
|
||||
registerCoreItem(item, level: pack.level);
|
||||
}
|
||||
|
||||
final segments = <LessonSegment>[];
|
||||
for (var i = 0; i < pack.segments.length; i++) {
|
||||
final seg = pack.segments[i];
|
||||
final scene = seg.sceneId.isNotEmpty
|
||||
? scenesById[seg.sceneId]
|
||||
: i < pack.scenes.length
|
||||
? pack.scenes[i]
|
||||
: pack.mainScene;
|
||||
final dialogue = scene == null ? null : _dialogueFromScene(scene, byId);
|
||||
final independentTerms = _acceptedTerms(seg.independentItemIds, byId);
|
||||
final writingTerms = _acceptedTerms(seg.writingItemIds, byId);
|
||||
|
||||
final lessonSegment = LessonSegment(
|
||||
id: seg.id,
|
||||
targetItemIds: seg.itemIds,
|
||||
previewItemIds: seg.itemIds,
|
||||
dialogueRequiredTerms: dialogue?.requiredTerms ?? const [],
|
||||
independentRequiredTerms:
|
||||
seg.independentAccept ??
|
||||
[if (independentTerms.isNotEmpty) independentTerms],
|
||||
estimatedMinutes: seg.minutes,
|
||||
);
|
||||
segments.add(lessonSegment);
|
||||
courseSegments[seg.id] = lessonSegment;
|
||||
|
||||
// Preview vocabulary: the pack's own list, or the core items this
|
||||
// segment teaches.
|
||||
segmentVocabulary[seg.id] =
|
||||
seg.vocabulary ??
|
||||
[
|
||||
for (final id in seg.itemIds)
|
||||
if (byId[id] case final item?) _vocabularyOf(item),
|
||||
];
|
||||
if (seg.grammarNote.isNotEmpty) {
|
||||
segmentGrammarNotes[seg.id] = seg.grammarNote;
|
||||
}
|
||||
|
||||
segmentActivities[seg.id] = LessonActivity(
|
||||
listening: seg.listeningText,
|
||||
listeningQuestion: seg.listeningQuestion,
|
||||
answers: seg.listeningOptions.isNotEmpty
|
||||
? seg.listeningOptions
|
||||
: const [''],
|
||||
speaking: seg.speakingText,
|
||||
speakingTip: seg.speakingTip,
|
||||
reading: seg.readingText,
|
||||
readingQuestion: seg.readingQuestion,
|
||||
readingAnswer: seg.readingOptions.isNotEmpty
|
||||
? seg.readingOptions.first
|
||||
: '',
|
||||
readingOptions: seg.readingOptions,
|
||||
writingPrompt: seg.writingPrompt,
|
||||
writingExample: seg.writingExample,
|
||||
writingRequiredTerms:
|
||||
seg.writingAccept ?? [if (writingTerms.isNotEmpty) writingTerms],
|
||||
writingHint: seg.writingHint.isNotEmpty
|
||||
? seg.writingHint
|
||||
: writingTerms.isEmpty
|
||||
? ''
|
||||
: '用上本段的表达之一:${_joinModels(seg.writingItemIds, byId)}。',
|
||||
independentPrompt: seg.independentPrompt,
|
||||
independentHelp: seg.independentHelp.isNotEmpty
|
||||
? seg.independentHelp
|
||||
: _joinModels(seg.independentItemIds, byId),
|
||||
);
|
||||
|
||||
if (dialogue != null) segmentDialogues[seg.id] = dialogue;
|
||||
}
|
||||
|
||||
courseLessons.add(
|
||||
SeedLesson(
|
||||
id: pack.id,
|
||||
number: courseLessons.length + 1,
|
||||
title: pack.title,
|
||||
outcome: pack.canDo,
|
||||
// Lesson-level words feed the offline lexicon lookup.
|
||||
vocabulary:
|
||||
pack.vocabulary ??
|
||||
[
|
||||
for (final item in pack.coreItems) _vocabularyOf(item),
|
||||
for (final word in pack.receptiveWords)
|
||||
VocabularyItem(
|
||||
id: word.id,
|
||||
word: word.en,
|
||||
meaning: word.zh,
|
||||
example: '',
|
||||
exampleMeaning: '',
|
||||
),
|
||||
],
|
||||
level: pack.level,
|
||||
status: pack.status == 'approved'
|
||||
? ContentStatus.approved
|
||||
: ContentStatus.draft,
|
||||
estimatedMinutes: pack.segments.fold(
|
||||
0,
|
||||
(sum, seg) => sum + seg.minutes,
|
||||
),
|
||||
explicitSegments: segments,
|
||||
targetItemIds: [for (final seg in pack.segments) ...seg.itemIds],
|
||||
),
|
||||
);
|
||||
|
||||
// Scenes marked `practice` are also free-practice scenes, unlocked once
|
||||
// the unit is finished unless they are always open.
|
||||
for (final scene in pack.scenes) {
|
||||
final practice = scene.practice;
|
||||
if (practice == null) continue;
|
||||
practiceScenes.add(
|
||||
DialogueScene(
|
||||
id: scene.id,
|
||||
title: practice.title.isNotEmpty
|
||||
? practice.title
|
||||
: '${pack.title} · ${scene.title}',
|
||||
summary: practice.summary.isNotEmpty
|
||||
? practice.summary
|
||||
: scene.setting.isNotEmpty
|
||||
? scene.setting
|
||||
: scene.title,
|
||||
script: _dialogueFromScene(scene, byId),
|
||||
recapPrompt: practice.recapPrompt.isNotEmpty
|
||||
? practice.recapPrompt
|
||||
: '再用英语完成一次「${scene.title}」的对话。',
|
||||
unlockAfterLessonId: practice.alwaysOpen ? null : pack.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
VocabularyItem _vocabularyOf(CoreItem item) => VocabularyItem(
|
||||
id: item.id,
|
||||
word: item.en,
|
||||
meaning: item.zh,
|
||||
example: item.exampleEn,
|
||||
exampleMeaning: item.exampleZh,
|
||||
);
|
||||
|
||||
/// One accepted-term group per turn: the turn's own `accept`, or the terms
|
||||
/// of the core items the turn teaches.
|
||||
LessonDialogue _dialogueFromScene(
|
||||
PackScene scene,
|
||||
Map<String, CoreItem> byId,
|
||||
) => LessonDialogue(
|
||||
goal: scene.goal.isNotEmpty
|
||||
? scene.goal
|
||||
: scene.setting.isNotEmpty
|
||||
? scene.setting
|
||||
: scene.title,
|
||||
prompts: [for (final turn in scene.turns) turn.ai],
|
||||
hints: [for (final turn in scene.turns) turn.model],
|
||||
translations: scene.turns.every((turn) => turn.aiZh.isEmpty)
|
||||
? const []
|
||||
: [for (final turn in scene.turns) turn.aiZh],
|
||||
requiredTerms: [
|
||||
for (final turn in scene.turns)
|
||||
turn.accept ?? _acceptedTerms(turn.itemIds, byId),
|
||||
],
|
||||
taskLabels: [for (final turn in scene.turns) turn.goal],
|
||||
);
|
||||
|
||||
List<String> _acceptedTerms(
|
||||
Iterable<String> itemIds,
|
||||
Map<String, CoreItem> byId,
|
||||
) {
|
||||
final terms = <String>[];
|
||||
for (final id in itemIds) {
|
||||
final item = byId[id];
|
||||
if (item == null) continue;
|
||||
for (final group in item.match) {
|
||||
terms.addAll(group);
|
||||
}
|
||||
if (item.match.isEmpty && item.en.isNotEmpty) terms.add(item.en);
|
||||
}
|
||||
return terms;
|
||||
}
|
||||
|
||||
String _joinModels(Iterable<String> itemIds, Map<String, CoreItem> byId) {
|
||||
final phrases = [
|
||||
for (final id in itemIds)
|
||||
if (byId[id] != null && byId[id]!.en.isNotEmpty) byId[id]!.en,
|
||||
];
|
||||
return phrases.join(' / ');
|
||||
}
|
||||
|
||||
/// Test-only reset back to A0 alone, so a test can start without the later
|
||||
/// levels or load them again.
|
||||
@visibleForTesting
|
||||
void resetForTest() {
|
||||
final a0 = _units.where((pack) => pack.level == 'A0').toList();
|
||||
final a0Assessments = [
|
||||
for (final pack in assessmentPacks)
|
||||
if (pack.level == 'A0') pack,
|
||||
];
|
||||
_units.clear();
|
||||
_unitsById.clear();
|
||||
_loadedAll = false;
|
||||
clearCourseCatalog();
|
||||
clearCoreItems();
|
||||
a0.forEach(_add);
|
||||
assessmentPacks.addAll(a0Assessments);
|
||||
_assessmentLevels.removeWhere((level) => level != 'A0');
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/// Course content and lookups, A0 through B1, from the JSON packs loaded by
|
||||
/// `CourseRepository`.
|
||||
library;
|
||||
|
||||
export 'answer_matching.dart';
|
||||
export 'answer_rules.dart';
|
||||
export 'assessment.dart';
|
||||
export 'core_items.dart';
|
||||
export 'course_catalog.dart';
|
||||
export 'course_models.dart';
|
||||
export 'option_order.dart';
|
||||
@@ -1,25 +0,0 @@
|
||||
int _seedHash(String seed) {
|
||||
var hash = 0x811c9dc5;
|
||||
for (final code in seed.codeUnits) {
|
||||
hash = ((hash ^ code) * 0x01000193) & 0x7fffffff;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// 选项顺序固定会让学习者养成“永远选第一个”的习惯。[options] 的第一项是标准
|
||||
/// 答案,这里按题目内容确定性地把它挪到某个位置,并打乱其余干扰项:同一道题
|
||||
/// 每次进入顺序都一样,但答案在三个位置上分布均匀。
|
||||
List<String> shuffledOptions(List<String> options, String seed) {
|
||||
if (options.length < 2) return options;
|
||||
final distractors = [...options.skip(1)];
|
||||
var hash = _seedHash(seed);
|
||||
for (var i = distractors.length - 1; i > 0; i--) {
|
||||
hash = (hash * 1103515245 + 12345) & 0x7fffffff;
|
||||
final j = hash % (i + 1);
|
||||
final swap = distractors[i];
|
||||
distractors[i] = distractors[j];
|
||||
distractors[j] = swap;
|
||||
}
|
||||
return distractors
|
||||
..insert(_seedHash('$seed#slot') % options.length, options.first);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'courses/courses.dart';
|
||||
import 'a0_core.dart';
|
||||
import 'models.dart';
|
||||
|
||||
/// A bounded AI-authored variation of an existing review item. It never
|
||||
@@ -120,40 +120,6 @@ bool matchesAdaptiveLessonAnswer(GeneratedLessonTask task, String answer) {
|
||||
return accepted || slots;
|
||||
}
|
||||
|
||||
/// Explains in Chinese why [matchesAdaptiveLessonAnswer] rejects [answer],
|
||||
/// or returns null when it accepts it. Listening answers only get a count so
|
||||
/// the explanation does not replace listening again.
|
||||
String? adaptiveAnswerShortfall(GeneratedLessonTask task, String answer) {
|
||||
if (matchesAdaptiveLessonAnswer(task, answer)) return null;
|
||||
final actual = _normalizeAnswer(answer);
|
||||
if (actual.isEmpty) return '请先输入或说出答案。';
|
||||
final spec = task.localAnswerSpec;
|
||||
final forbidden = spec.forbiddenPhrases
|
||||
.where((phrase) => _containsPhrase(actual, phrase))
|
||||
.toList();
|
||||
if (forbidden.isNotEmpty) {
|
||||
return '这里不能用:${forbidden.join('、')}。';
|
||||
}
|
||||
final missing = spec.requiredAnyPhrases
|
||||
.where(
|
||||
(alternatives) =>
|
||||
!alternatives.any((phrase) => _containsPhrase(actual, phrase)),
|
||||
)
|
||||
.toList();
|
||||
if (task.skill == 'listening') {
|
||||
return '还缺 ${missing.isEmpty ? 1 : missing.length} 处关键信息。再听一次,或慢放后补全。';
|
||||
}
|
||||
if (missing.isEmpty || answerSpecIsReferenceOnly(spec, task.answer)) {
|
||||
return '和参考表达还不一致,检查拼写和漏掉的词。';
|
||||
}
|
||||
return '还缺:${missing.map((group) => group.take(3).join(' / ')).join(';')}。';
|
||||
}
|
||||
|
||||
bool answerSpecIsReferenceOnly(GeneratedAnswerSpec spec, String answer) =>
|
||||
spec.requiredAnyPhrases.length == 1 &&
|
||||
spec.requiredAnyPhrases.single.length == 1 &&
|
||||
spec.requiredAnyPhrases.single.single == answer;
|
||||
|
||||
String _normalizeAnswer(String value) => value
|
||||
.toLowerCase()
|
||||
.replaceAll('’', "'")
|
||||
@@ -191,7 +157,7 @@ GeneratedReviewVariant? decodeGeneratedReviewVariant(
|
||||
expectedAnswer.trim().isEmpty ||
|
||||
expectedAnswer.length > 120 ||
|
||||
targetItemId != expectedTargetItemId ||
|
||||
!isCoreItem(targetItemId)) {
|
||||
!a0CoreItems.containsKey(targetItemId)) {
|
||||
return null;
|
||||
}
|
||||
return GeneratedReviewVariant(
|
||||
@@ -265,7 +231,7 @@ GeneratedLesson? decodeGeneratedLesson(
|
||||
rawTasks is! List ||
|
||||
targets.length != 1 ||
|
||||
targets.single != expectedTargetItemId ||
|
||||
!isCoreItem(expectedTargetItemId) ||
|
||||
!a0CoreItems.containsKey(expectedTargetItemId) ||
|
||||
newItems.isNotEmpty ||
|
||||
preview.isNotEmpty ||
|
||||
chunks.isNotEmpty ||
|
||||
|
||||
@@ -319,7 +319,6 @@ class LocalSnapshotStore {
|
||||
'review_items',
|
||||
'study_sessions',
|
||||
'temporary_lexicon_entries',
|
||||
'sentence_analyses',
|
||||
'learner_profiles',
|
||||
'lesson_progress',
|
||||
'assessment_records',
|
||||
@@ -430,7 +429,6 @@ class LocalSnapshotStore {
|
||||
'previewIndex',
|
||||
'completedLessons',
|
||||
'activeLessonId',
|
||||
'placementStartLessonId',
|
||||
'completedLessonIds',
|
||||
'completedSegmentIds',
|
||||
'activeSegmentIndexes',
|
||||
@@ -489,7 +487,7 @@ class LocalSnapshotStore {
|
||||
final definition = entry['definition'] as String? ?? '';
|
||||
if (key.isEmpty || definition.trim().isEmpty) continue;
|
||||
await executor.runCustom(
|
||||
'INSERT OR REPLACE INTO temporary_lexicon_entries VALUES (?, ?, ?, ?, ?, ?)',
|
||||
'INSERT INTO temporary_lexicon_entries VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
key,
|
||||
query,
|
||||
@@ -507,7 +505,7 @@ class LocalSnapshotStore {
|
||||
final payload = _encodeObject(entry['payload'] ?? {});
|
||||
if (key.isEmpty || payload == '{}') continue;
|
||||
await executor.runCustom(
|
||||
'INSERT OR REPLACE INTO sentence_analyses VALUES (?, ?, ?, ?, ?, ?)',
|
||||
'INSERT INTO sentence_analyses VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
key,
|
||||
query,
|
||||
|
||||
@@ -2,14 +2,13 @@ enum LearningGoal { dailyLife, travel, workStarter }
|
||||
|
||||
enum PlacementLevel { beginner, someBasics, simpleConversation }
|
||||
|
||||
enum AppTab { learn, dialogue, review, profile }
|
||||
enum AppTab { home, learn, dialogue, review, progress }
|
||||
|
||||
enum LessonStep {
|
||||
preview,
|
||||
listening,
|
||||
speaking,
|
||||
reading,
|
||||
material,
|
||||
writing,
|
||||
dialogue,
|
||||
independent,
|
||||
@@ -184,15 +183,6 @@ class VocabularyItem {
|
||||
this.ipa,
|
||||
});
|
||||
|
||||
factory VocabularyItem.fromJson(Map<String, dynamic> json) => VocabularyItem(
|
||||
id: json['id'] as String,
|
||||
word: json['word'] as String,
|
||||
meaning: json['meaning'] as String,
|
||||
example: json['example'] as String? ?? '',
|
||||
exampleMeaning: json['exampleMeaning'] as String? ?? '',
|
||||
ipa: json['ipa'] as String?,
|
||||
);
|
||||
|
||||
final String id;
|
||||
final String word;
|
||||
final String meaning;
|
||||
@@ -405,7 +395,11 @@ class DialogueTurn {
|
||||
final bool isLearner;
|
||||
final String? translation;
|
||||
|
||||
DialogueTurn copyWith({String? text, bool? isLearner, String? translation}) {
|
||||
DialogueTurn copyWith({
|
||||
String? text,
|
||||
bool? isLearner,
|
||||
String? translation,
|
||||
}) {
|
||||
return DialogueTurn(
|
||||
text: text ?? this.text,
|
||||
isLearner: isLearner ?? this.isLearner,
|
||||
|
||||
@@ -1,57 +1,99 @@
|
||||
import 'courses/courses.dart';
|
||||
import 'models.dart';
|
||||
import 'speech_compare.dart';
|
||||
|
||||
class ReviewCheckResult {
|
||||
const ReviewCheckResult({
|
||||
required this.complete,
|
||||
required this.message,
|
||||
this.dictation,
|
||||
});
|
||||
const ReviewCheckResult({required this.complete, required this.message});
|
||||
|
||||
final bool complete;
|
||||
final String message;
|
||||
|
||||
/// Word-by-word comparison with the dictated sentence; null otherwise.
|
||||
final SpokenComparison? dictation;
|
||||
}
|
||||
|
||||
/// Checks only the minimum information a learner needs to produce in a
|
||||
/// review. The rule and the hint come from the core item's course JSON, which
|
||||
/// accepts contractions and variable slot values instead of one fixed
|
||||
/// sentence.
|
||||
/// Checks only the minimum information a learner needs to produce in an A0
|
||||
/// review. It intentionally accepts common contractions and variable slot
|
||||
/// values instead of comparing an answer with one fixed sentence.
|
||||
class ReviewFeedback {
|
||||
const ReviewFeedback._();
|
||||
|
||||
static String _normalize(String input) => input
|
||||
.toLowerCase()
|
||||
.replaceAll('’', "'")
|
||||
.replaceAll('‘', "'")
|
||||
.replaceAll(RegExp(r'\s+'), ' ')
|
||||
.trim();
|
||||
|
||||
static ReviewCheckResult check(ReviewItem item, String input) {
|
||||
final dictation = item.skill == dictationSkill
|
||||
? coreDictationSentences[item.id]
|
||||
: null;
|
||||
if (dictation != null) {
|
||||
final comparison = compareSpoken(dictation.sentence, input);
|
||||
final missing = comparison.total - comparison.heardCount;
|
||||
final wrong = missing + comparison.extraWords.length;
|
||||
return ReviewCheckResult(
|
||||
complete: comparison.matches,
|
||||
message: comparison.matches
|
||||
? '听写正确。'
|
||||
: '有 $wrong 处和听到的句子不一致。再听一次,检查拼写和漏掉的词。',
|
||||
dictation: comparison,
|
||||
);
|
||||
}
|
||||
final complete = isCoreItem(item.id)
|
||||
? coreItemUsedIn(item.id, input)
|
||||
: RegExp(r"[a-z]+(?:'[a-z]+)?")
|
||||
.allMatches(normalizeAnswer(input))
|
||||
.map((match) => match.group(0))
|
||||
.toSet()
|
||||
.length >=
|
||||
2;
|
||||
final text = _normalize(input);
|
||||
final tokens = RegExp(
|
||||
r"[a-z]+(?:'[a-z]+)?",
|
||||
).allMatches(text).map((match) => match.group(0)!).toSet();
|
||||
bool has(String token) => tokens.contains(_normalize(token));
|
||||
bool phrase(String value) => text.contains(_normalize(value));
|
||||
bool hasAny(Iterable<String> values) => values.any(has);
|
||||
final introduction =
|
||||
phrase("i'm") || phrase('i am') || phrase('my name is');
|
||||
final itIs = phrase("it's") || phrase('it is');
|
||||
final numberWords = RegExp(
|
||||
r'\b(zero|one|two|three|four|five|six|seven|eight|nine|ten)\b',
|
||||
).allMatches(text).length;
|
||||
|
||||
final complete = switch (item.id) {
|
||||
'A0-P01' => introduction && tokens.length >= 2,
|
||||
'A0-P02' => (phrase("what's your name") || phrase('what is your name')),
|
||||
'A0-P03' => phrase('nice to meet you'),
|
||||
'A0-P04' => phrase('how do you spell'),
|
||||
'A0-P05' => phrase('how are you'),
|
||||
'A0-P06' => introduction && hasAny(['good', 'okay', 'tired']),
|
||||
'A0-P07' =>
|
||||
phrase("what's your phone number") ||
|
||||
phrase('what is your phone number'),
|
||||
'A0-P08' =>
|
||||
(phrase('my number is') || numberWords >= 3) && numberWords >= 3,
|
||||
'A0-P09' => phrase("what's this") || phrase('what is this'),
|
||||
'A0-P10' => itIs && hasAny(['book', 'pen', 'bag', 'key']),
|
||||
'A0-P11' => phrase('where are you from'),
|
||||
'A0-P12' => introduction && has('from') && tokens.length >= 3,
|
||||
'A0-P13' => phrase('this is my') && tokens.length >= 4,
|
||||
'A0-P14' => phrase('what day is it'),
|
||||
'A0-P15' =>
|
||||
itIs &&
|
||||
hasAny([
|
||||
'monday',
|
||||
'tuesday',
|
||||
'wednesday',
|
||||
'thursday',
|
||||
'friday',
|
||||
'saturday',
|
||||
'sunday',
|
||||
]),
|
||||
'A0-P16' => phrase('what time is it'),
|
||||
'A0-P17' =>
|
||||
itIs &&
|
||||
(has('clock') ||
|
||||
has("o'clock") ||
|
||||
has('oclock') ||
|
||||
phrase("o'clock")),
|
||||
'A0-P18' => introduction && has('like') && tokens.length >= 3,
|
||||
'A0-P19' => phrase('do you like'),
|
||||
'A0-P20' =>
|
||||
phrase('please say that again') || phrase('please speak slowly'),
|
||||
_ when item.id.startsWith('A0-W') =>
|
||||
tokens.contains(_normalize(item.target)) || phrase(item.target),
|
||||
_ => tokens.length >= 2,
|
||||
};
|
||||
return ReviewCheckResult(
|
||||
complete: complete,
|
||||
message: complete
|
||||
? '表达已完成,可以进入下一次复习安排。'
|
||||
: coreReviewHints[item.id] ?? '再补充本次目标中的关键英文词或句型。',
|
||||
message: complete ? '表达已完成,可以进入下一次复习安排。' : _hint(item.id),
|
||||
);
|
||||
}
|
||||
|
||||
static String _hint(String id) => switch (id) {
|
||||
'A0-P01' => '用 I’m / I am 或 My name is 介绍一个名字。',
|
||||
'A0-P02' => '试着问:What’s your name?',
|
||||
'A0-P04' => '问对方怎样拼写:How do you spell …?',
|
||||
'A0-P08' => '说出至少三个英文数字,也可用 My number is … 开头。',
|
||||
'A0-P10' => '用 It’s / It is 加上一个物品。',
|
||||
'A0-P12' => '用 I’m from … 说出一个地点。',
|
||||
'A0-P17' => '用 It’s … o’clock 说整点时间。',
|
||||
'A0-P18' => '用 I like … 说一项喜好。',
|
||||
_ => '再补充本次目标中的关键英文词或句型。',
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,25 +36,20 @@ class SherpaSttService {
|
||||
try {
|
||||
final execDir = File(Platform.resolvedExecutable).parent;
|
||||
final contentsDir = execDir.parent;
|
||||
candidateDirs.add(
|
||||
'${contentsDir.path}/Frameworks/App.framework/Resources/flutter_assets/assets/models/sense_voice',
|
||||
);
|
||||
candidateDirs.add(
|
||||
'${contentsDir.path}/Frameworks/App.framework/Versions/A/Resources/flutter_assets/assets/models/sense_voice',
|
||||
);
|
||||
candidateDirs.add('${contentsDir.path}/Frameworks/App.framework/Resources/flutter_assets/assets/models/sense_voice');
|
||||
candidateDirs.add('${contentsDir.path}/Frameworks/App.framework/Versions/A/Resources/flutter_assets/assets/models/sense_voice');
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
for (final dirPath in candidateDirs) {
|
||||
final mFile = File('$dirPath/$modelFilename');
|
||||
final tFile = File('$dirPath/$tokensFilename');
|
||||
if (mFile.existsSync() &&
|
||||
tFile.existsSync() &&
|
||||
mFile.lengthSync() > 100000000) {
|
||||
debugPrint(
|
||||
'[SherpaSttService] Using bundled SenseVoice model directly at: $dirPath',
|
||||
);
|
||||
return {'model': mFile.path, 'tokens': tFile.path};
|
||||
if (mFile.existsSync() && tFile.existsSync() && mFile.lengthSync() > 100000000) {
|
||||
debugPrint('[SherpaSttService] Using bundled SenseVoice model directly at: $dirPath');
|
||||
return {
|
||||
'model': mFile.path,
|
||||
'tokens': tFile.path,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,25 +64,14 @@ class SherpaSttService {
|
||||
final targetModelFile = File('${modelDir.path}/$modelFilename');
|
||||
final targetTokensFile = File('${modelDir.path}/$tokensFilename');
|
||||
|
||||
final modelValid =
|
||||
targetModelFile.existsSync() &&
|
||||
targetModelFile.lengthSync() > 200 * 1024 * 1024;
|
||||
final tokensValid =
|
||||
targetTokensFile.existsSync() &&
|
||||
targetTokensFile.lengthSync() > 100 * 1024;
|
||||
final modelValid = targetModelFile.existsSync() && targetModelFile.lengthSync() > 200 * 1024 * 1024;
|
||||
final tokensValid = targetTokensFile.existsSync() && targetTokensFile.lengthSync() > 100 * 1024;
|
||||
|
||||
if (!modelValid) {
|
||||
debugPrint(
|
||||
'[SherpaSttService] Unpacking bundled model to ${targetModelFile.path}...',
|
||||
);
|
||||
debugPrint('[SherpaSttService] Unpacking bundled model to ${targetModelFile.path}...');
|
||||
final tmpFile = File('${targetModelFile.path}.tmp');
|
||||
final ByteData data = await rootBundle.load(
|
||||
'assets/models/sense_voice/$modelFilename',
|
||||
);
|
||||
final Uint8List bytes = data.buffer.asUint8List(
|
||||
data.offsetInBytes,
|
||||
data.lengthInBytes,
|
||||
);
|
||||
final ByteData data = await rootBundle.load('assets/models/sense_voice/$modelFilename');
|
||||
final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
|
||||
await tmpFile.writeAsBytes(bytes, flush: true);
|
||||
if (await tmpFile.length() > 200 * 1024 * 1024) {
|
||||
if (await targetModelFile.exists()) await targetModelFile.delete();
|
||||
@@ -99,23 +83,19 @@ class SherpaSttService {
|
||||
|
||||
if (!tokensValid) {
|
||||
final tmpTokens = File('${targetTokensFile.path}.tmp');
|
||||
final ByteData data = await rootBundle.load(
|
||||
'assets/models/sense_voice/$tokensFilename',
|
||||
);
|
||||
final Uint8List bytes = data.buffer.asUint8List(
|
||||
data.offsetInBytes,
|
||||
data.lengthInBytes,
|
||||
);
|
||||
final ByteData data = await rootBundle.load('assets/models/sense_voice/$tokensFilename');
|
||||
final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
|
||||
await tmpTokens.writeAsBytes(bytes, flush: true);
|
||||
if (await targetTokensFile.exists()) await targetTokensFile.delete();
|
||||
await tmpTokens.rename(targetTokensFile.path);
|
||||
}
|
||||
|
||||
return {'model': targetModelFile.path, 'tokens': targetTokensFile.path};
|
||||
return {
|
||||
'model': targetModelFile.path,
|
||||
'tokens': targetTokensFile.path,
|
||||
};
|
||||
} catch (e, stack) {
|
||||
debugPrint(
|
||||
'[SherpaSttService] Failed to unpack bundled model: $e\n$stack',
|
||||
);
|
||||
debugPrint('[SherpaSttService] Failed to unpack bundled model: $e\n$stack');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -137,10 +117,7 @@ class SherpaSttService {
|
||||
try {
|
||||
final execDir = File(Platform.resolvedExecutable).parent;
|
||||
final frameworksDir = Directory('${execDir.parent.path}/Frameworks');
|
||||
if (frameworksDir.existsSync() &&
|
||||
File(
|
||||
'${frameworksDir.path}/libsherpa-onnx-c-api.dylib',
|
||||
).existsSync()) {
|
||||
if (frameworksDir.existsSync() && File('${frameworksDir.path}/libsherpa-onnx-c-api.dylib').existsSync()) {
|
||||
resolvedLibDir = frameworksDir.path;
|
||||
}
|
||||
} catch (_) {}
|
||||
@@ -154,9 +131,7 @@ class SherpaSttService {
|
||||
|
||||
final resolved = await _resolveModelFiles();
|
||||
if (resolved == null) {
|
||||
debugPrint(
|
||||
'[SherpaSttService] Model files could not be located or extracted.',
|
||||
);
|
||||
debugPrint('[SherpaSttService] Model files could not be located or extracted.');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -173,22 +148,15 @@ class SherpaSttService {
|
||||
|
||||
final recognizerConfig = sherpa_onnx.OfflineRecognizerConfig(
|
||||
model: modelConfig,
|
||||
feat: const sherpa_onnx.FeatureConfig(
|
||||
sampleRate: 16000,
|
||||
featureDim: 80,
|
||||
),
|
||||
feat: const sherpa_onnx.FeatureConfig(sampleRate: 16000, featureDim: 80),
|
||||
);
|
||||
|
||||
_recognizer = sherpa_onnx.OfflineRecognizer(recognizerConfig);
|
||||
_isInitialized = true;
|
||||
debugPrint(
|
||||
'[SherpaSttService] SenseVoice-Small ONNX ASR engine initialized successfully (language=en).',
|
||||
);
|
||||
debugPrint('[SherpaSttService] SenseVoice-Small ONNX ASR engine initialized successfully (language=en).');
|
||||
return true;
|
||||
} catch (e, stack) {
|
||||
debugPrint(
|
||||
'[SherpaSttService] Failed to initialize SenseVoice ASR engine: $e\n$stack',
|
||||
);
|
||||
debugPrint('[SherpaSttService] Failed to initialize SenseVoice ASR engine: $e\n$stack');
|
||||
_isInitialized = false;
|
||||
return false;
|
||||
} finally {
|
||||
@@ -234,10 +202,7 @@ class SherpaSttService {
|
||||
|
||||
final stream = _recognizer!.createStream();
|
||||
try {
|
||||
stream.acceptWaveform(
|
||||
samples: wave.samples,
|
||||
sampleRate: wave.sampleRate,
|
||||
);
|
||||
stream.acceptWaveform(samples: wave.samples, sampleRate: wave.sampleRate);
|
||||
_recognizer!.decode(stream);
|
||||
final result = _recognizer!.getResult(stream);
|
||||
|
||||
@@ -258,9 +223,7 @@ class SherpaSttService {
|
||||
String _cleanText(String text) {
|
||||
if (text.isEmpty) return text;
|
||||
// Strip SenseVoice special tags like <|zh|>, <|en|>, <|NEUTRAL|>, <|HAPPY|>, <|Speech|>, <|withitn|>, <|woitn|>, etc.
|
||||
var cleaned = text
|
||||
.replaceAll(RegExp(r'<\|[a-zA-Z0-9_\-\s]+\|>'), '')
|
||||
.trim();
|
||||
var cleaned = text.replaceAll(RegExp(r'<\|[a-zA-Z0-9_\-\s]+\|>'), '').trim();
|
||||
if (cleaned.isEmpty) return cleaned;
|
||||
// Strip leading punctuation often inserted by Whisper/SenseVoice
|
||||
cleaned = cleaned.replaceFirst(RegExp(r'^[\s,.:;!?~]+'), '').trim();
|
||||
@@ -268,8 +231,7 @@ class SherpaSttService {
|
||||
// Normalize consecutive spaces
|
||||
cleaned = cleaned.replaceAll(RegExp(r'\s+'), ' ').trim();
|
||||
// Capitalize first character if it's an English letter
|
||||
if (cleaned.isNotEmpty &&
|
||||
cleaned[0].toLowerCase() != cleaned[0].toUpperCase()) {
|
||||
if (cleaned.isNotEmpty && cleaned[0].toLowerCase() != cleaned[0].toUpperCase()) {
|
||||
cleaned = cleaned[0].toUpperCase() + cleaned.substring(1);
|
||||
}
|
||||
return cleaned;
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
/// 把设备转写和示范句逐词对照。这不是发音评分:转写只说明“机器听成了什么”,
|
||||
/// 用来提示学习者哪些词没被听出来,值得再听示范、再说一次。
|
||||
class SpokenWord {
|
||||
const SpokenWord(this.text, {required this.heard});
|
||||
|
||||
/// 示范句中的原词(保留大小写和标点,便于直接显示)。
|
||||
final String text;
|
||||
final bool heard;
|
||||
}
|
||||
|
||||
class SpokenComparison {
|
||||
const SpokenComparison({required this.words, required this.extraWords});
|
||||
|
||||
final List<SpokenWord> words;
|
||||
|
||||
/// 转写里出现、但示范句没有的词。
|
||||
final List<String> extraWords;
|
||||
|
||||
int get heardCount => words.where((word) => word.heard).length;
|
||||
int get total => words.length;
|
||||
List<String> get missedWords => [
|
||||
for (final word in words)
|
||||
if (!word.heard) word.text,
|
||||
];
|
||||
|
||||
/// 全部听出且没有多余词。
|
||||
bool get matches => total > 0 && heardCount == total && extraWords.isEmpty;
|
||||
|
||||
/// 大部分词被听出(≥ 80%)。
|
||||
bool get close => total > 0 && heardCount / total >= 0.8;
|
||||
}
|
||||
|
||||
const _digitWords = [
|
||||
'zero',
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
];
|
||||
|
||||
const _contractions = {
|
||||
"i'm": ['i', 'am'],
|
||||
"it's": ['it', 'is'],
|
||||
"what's": ['what', 'is'],
|
||||
"that's": ['that', 'is'],
|
||||
"he's": ['he', 'is'],
|
||||
"she's": ['she', 'is'],
|
||||
"name's": ['name', 'is'],
|
||||
"don't": ['do', 'not'],
|
||||
"you're": ['you', 'are'],
|
||||
};
|
||||
|
||||
/// 一个显示词对应的比较单位。例如 "I’m" 展开为 i/am,"138" 展开为
|
||||
/// one/three/eight,"o’clock" 统一为 oclock。
|
||||
List<String> _unitsOf(String raw) {
|
||||
var word = raw
|
||||
.toLowerCase()
|
||||
.replaceAll(RegExp(r'[‘’`´]'), "'")
|
||||
.replaceAll(RegExp(r"[^a-z0-9']"), '');
|
||||
word = word.replaceAll(RegExp(r"^'+|'+$"), '');
|
||||
if (word.isEmpty) return const [];
|
||||
if (word == "o'clock") return const ['oclock'];
|
||||
final expanded = _contractions[word];
|
||||
if (expanded != null) return expanded;
|
||||
if (RegExp(r'^\d+$').hasMatch(word)) {
|
||||
return [for (final digit in word.split('')) _digitWords[int.parse(digit)]];
|
||||
}
|
||||
return [word.replaceAll("'", '')];
|
||||
}
|
||||
|
||||
List<String> _displayWords(String text) => text
|
||||
.replaceAll(RegExp(r'[-–—/]'), ' ')
|
||||
.split(RegExp(r'\s+'))
|
||||
.where((word) => _unitsOf(word).isNotEmpty)
|
||||
.toList();
|
||||
|
||||
List<String> _heardUnits(String transcript) => [
|
||||
for (final word in _displayWords(transcript)) ..._unitsOf(word),
|
||||
];
|
||||
|
||||
SpokenComparison compareSpoken(String expected, String transcript) {
|
||||
final display = _displayWords(expected);
|
||||
final expectedUnits = <String>[];
|
||||
final owner = <int>[];
|
||||
for (var index = 0; index < display.length; index++) {
|
||||
for (final unit in _unitsOf(display[index])) {
|
||||
expectedUnits.add(unit);
|
||||
owner.add(index);
|
||||
}
|
||||
}
|
||||
var heard = _heardUnits(transcript);
|
||||
// 拼读时转写常把字母连成一个词(S H E N → Shen),拆回单个字母。
|
||||
final spellsLetters =
|
||||
expectedUnits.isNotEmpty &&
|
||||
expectedUnits.every((unit) => RegExp(r'^[a-z]$').hasMatch(unit));
|
||||
if (spellsLetters) {
|
||||
heard = [for (final unit in heard) ...unit.split('')];
|
||||
}
|
||||
|
||||
// 最长公共子序列:保持词序,重复词也能正确对齐。
|
||||
final n = expectedUnits.length;
|
||||
final m = heard.length;
|
||||
final table = List.generate(n + 1, (_) => List.filled(m + 1, 0));
|
||||
for (var i = n - 1; i >= 0; i--) {
|
||||
for (var j = m - 1; j >= 0; j--) {
|
||||
table[i][j] = expectedUnits[i] == heard[j]
|
||||
? table[i + 1][j + 1] + 1
|
||||
: (table[i + 1][j] >= table[i][j + 1]
|
||||
? table[i + 1][j]
|
||||
: table[i][j + 1]);
|
||||
}
|
||||
}
|
||||
final unitHeard = List.filled(n, false);
|
||||
final heardUsed = List.filled(m, false);
|
||||
var i = 0;
|
||||
var j = 0;
|
||||
while (i < n && j < m) {
|
||||
if (expectedUnits[i] == heard[j]) {
|
||||
unitHeard[i] = true;
|
||||
heardUsed[j] = true;
|
||||
i++;
|
||||
j++;
|
||||
} else if (table[i + 1][j] >= table[i][j + 1]) {
|
||||
i++;
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
final wordHeard = List.filled(display.length, true);
|
||||
for (var unit = 0; unit < n; unit++) {
|
||||
if (!unitHeard[unit]) wordHeard[owner[unit]] = false;
|
||||
}
|
||||
return SpokenComparison(
|
||||
words: [
|
||||
for (var index = 0; index < display.length; index++)
|
||||
SpokenWord(display[index], heard: wordHeard[index]),
|
||||
],
|
||||
extraWords: [
|
||||
for (var index = 0; index < m; index++)
|
||||
if (!heardUsed[index]) heard[index],
|
||||
],
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user