Skip to content

Commit de08f23

Browse files
committed
FEAT: 新增在线词典语境发音词源
1 parent c1a6d7d commit de08f23

21 files changed

Lines changed: 1347 additions & 108 deletions

TASKS.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,37 @@
11
# Echo Loop 任务清单
22

3-
> 最后更新:2026-07-03(官方/播客音频「删除音频」
3+
> 最后更新:2026-07-04(AI 词典多词表达新 Prompt 展示对齐
44
> 当前焦点:Android 结束录音闪退(离线 ASR / Silero VAD)——**仍未解决**
55
6+
## 已完成:AI 词典多词表达新 Prompt 展示对齐
7+
8+
后端 `POST /api/v2/ai/dictionary` 的多词表达 prompt 已精简为 `originalExpression / naturalness / category / pronunciationTips / meanings / similarExpressions / background / learnerTips`;前端同步收敛模型、缓存、展示和测试。
9+
10+
- [x] **联合模型**`MultiWordDictionaryEntry` 改为镜像 app 前端新 schema;前端不保留 `thinking` / `frequency`,展示与空态只看 learner-facing 字段;旧缓存缺 `queryType` 仍按单词解析,新缓存可用 `originalExpression` 识别多词。
11+
- [x] **缓存隔离**`AiDictionarySource` L2 cache type 升级为 `ai_dictionary_v2`,避开旧多词 prompt 结构缓存。
12+
- [x] **UI**:多词结果保留 `category` 顶部元信息,主要内容区按 `meanings → naturalness → pronunciationTips → similarExpressions → background → learnerTips` 展示非空字段;词典面板长词组标题自动缩小为单行完整显示,不折行、不溢出、不省略;单词结果视图不变。
13+
- [x] **TTS**:多词表达的 `meanings[].examples``similarExpressions[].sentence` 纳入词典面板预热顺序。
14+
- [x] **l10n**:替换旧多词分节文案为新字段标签,并刷新生成文件。
15+
- [x] **测试**:更新模型解析、L2/L3 缓存、UI 渲染和 TTS 提取用例。
16+
- [x] **验证**`flutter analyze` 改动相关 11 个 Dart 文件 0 问题;`flutter test test/models/dictionary/dictionary_entry_test.dart test/models/dictionary/dict_speakable_texts_test.dart test/services/dictionary/ai_dictionary_source_test.dart test/widgets/dictionary/ai_dict_result_view_test.dart` 全过(46 例)。补充验证:`flutter analyze lib/widgets/dictionary/dictionary_panel.dart lib/widgets/dictionary/ai_multi_word_result_view.dart test/widgets/dictionary/dictionary_panel_test.dart test/widgets/dictionary/ai_dict_result_view_test.dart` 0 问题;`flutter test test/widgets/dictionary/dictionary_panel_test.dart test/widgets/dictionary/ai_dict_result_view_test.dart` 全过(23 例)。
17+
18+
**完成时间**: 2026-07-04
19+
20+
## 已完成:在线词典新增语境/发音/词源源
21+
22+
在线词典源继续沿用 `kWebDictConfigs` 配置驱动机制,新增 6 个网页源:
23+
24+
- [x] **OZDIC**:新增 `ozdic` 源,使用内置 WebView 打开搭配词典词条页,可在词典切换器与设置页启用/禁用。
25+
- [x] **PlayPhrase**:新增 `playPhrase` 源,使用内置 WebView 打开 `playphrase.me` 搜索页,可在词典切换器与设置页启用/禁用。
26+
- [x] **YouGlish**:新增 `youglish` 源,使用内置 WebView 打开 YouGlish 英语发音页,可在词典切换器与设置页启用/禁用。
27+
- [x] **Forvo**:新增 `forvo` 源,使用内置 WebView 打开 Forvo 英语发音页,可在词典切换器与设置页启用/禁用。
28+
- [x] **WordReference**:新增 `wordReference` 源,使用内置 WebView 打开 WordReference 英英词典页,可在词典切换器与设置页启用/禁用。
29+
- [x] **Etymonline**:新增 `etymonline` 源,使用内置 WebView 打开词源搜索页,可在词典切换器与设置页启用/禁用。
30+
- [x] **测试**:更新 `web_dictionary_source_test.dart`,确认 14 个网页源 id 完整且唯一,并覆盖新增源的通用 lookup/URL 编码契约。
31+
- [x] **验证**`flutter analyze` 改动文件 0 问题;`flutter test test/services/dictionary/web_dictionary_source_test.dart` 全过。
32+
33+
**完成时间**: 2026-07-03
34+
635
## 已完成:官方/播客音频「删除音频」
736

837
官方精选合集与博客(播客)合集的音频 item 下载后无法回收本地占用(这些 item 由后端 / RSS 管理,不能整条删除)。新增「删除音频」菜单项——只删本地音频文件、item 保留、随时可重新下载。

lib/l10n/app_en.arb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1994,5 +1994,9 @@
19941994
"dictAiWordFamily": "Word Family",
19951995
"dictAiForms": "Word Forms",
19961996
"dictAiEtymology": "Etymology",
1997-
"dictAiTips": "Tips"
1997+
"dictAiTips": "Learning Tips",
1998+
"dictAiMultiNaturalness": "Naturalness",
1999+
"dictAiMultiPronunciationTips": "Pronunciation",
2000+
"dictAiMultiSimilarExpressions": "Similar Expressions",
2001+
"dictAiMultiBackground": "Background Knowledge"
19982002
}

lib/l10n/app_localizations.dart

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7229,8 +7229,32 @@ abstract class AppLocalizations {
72297229
/// No description provided for @dictAiTips.
72307230
///
72317231
/// In en, this message translates to:
7232-
/// **'Tips'**
7232+
/// **'Learning Tips'**
72337233
String get dictAiTips;
7234+
7235+
/// No description provided for @dictAiMultiNaturalness.
7236+
///
7237+
/// In en, this message translates to:
7238+
/// **'Naturalness'**
7239+
String get dictAiMultiNaturalness;
7240+
7241+
/// No description provided for @dictAiMultiPronunciationTips.
7242+
///
7243+
/// In en, this message translates to:
7244+
/// **'Pronunciation'**
7245+
String get dictAiMultiPronunciationTips;
7246+
7247+
/// No description provided for @dictAiMultiSimilarExpressions.
7248+
///
7249+
/// In en, this message translates to:
7250+
/// **'Similar Expressions'**
7251+
String get dictAiMultiSimilarExpressions;
7252+
7253+
/// No description provided for @dictAiMultiBackground.
7254+
///
7255+
/// In en, this message translates to:
7256+
/// **'Background Knowledge'**
7257+
String get dictAiMultiBackground;
72347258
}
72357259

72367260
class _AppLocalizationsDelegate

lib/l10n/app_localizations_en.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4079,5 +4079,17 @@ class AppLocalizationsEn extends AppLocalizations {
40794079
String get dictAiEtymology => 'Etymology';
40804080

40814081
@override
4082-
String get dictAiTips => 'Tips';
4082+
String get dictAiTips => 'Learning Tips';
4083+
4084+
@override
4085+
String get dictAiMultiNaturalness => 'Naturalness';
4086+
4087+
@override
4088+
String get dictAiMultiPronunciationTips => 'Pronunciation';
4089+
4090+
@override
4091+
String get dictAiMultiSimilarExpressions => 'Similar Expressions';
4092+
4093+
@override
4094+
String get dictAiMultiBackground => 'Background Knowledge';
40834095
}

lib/l10n/app_localizations_zh.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3893,5 +3893,17 @@ class AppLocalizationsZh extends AppLocalizations {
38933893
String get dictAiEtymology => '词源';
38943894

38953895
@override
3896-
String get dictAiTips => '提示';
3896+
String get dictAiTips => '学习提示';
3897+
3898+
@override
3899+
String get dictAiMultiNaturalness => '自然性';
3900+
3901+
@override
3902+
String get dictAiMultiPronunciationTips => '发音提示';
3903+
3904+
@override
3905+
String get dictAiMultiSimilarExpressions => '相似表达';
3906+
3907+
@override
3908+
String get dictAiMultiBackground => '背景知识';
38973909
}

lib/l10n/app_zh.arb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1557,5 +1557,9 @@
15571557
"dictAiWordFamily": "词族",
15581558
"dictAiForms": "词形变化",
15591559
"dictAiEtymology": "词源",
1560-
"dictAiTips": "提示"
1560+
"dictAiTips": "学习提示",
1561+
"dictAiMultiNaturalness": "自然性",
1562+
"dictAiMultiPronunciationTips": "发音提示",
1563+
"dictAiMultiSimilarExpressions": "相似表达",
1564+
"dictAiMultiBackground": "背景知识"
15611565
}

lib/models/dictionary/dict_speakable_texts.dart

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
library;
77

88
import 'dictionary_lookup_result.dart';
9+
import 'dictionary_entry.dart';
910

1011
/// 提取 [result] 中所有可发音英文文本,按弹窗显示顺序:
1112
/// 单词标题 → 各义项例句 → 常见搭配例句 → 词族例句。
@@ -18,19 +19,30 @@ List<String> dictionarySpeakableTexts(DictionaryLookupResult result) {
1819
// 单词标题(跨源恒有,标题行 SpeakButton 发此文本)。
1920
texts.add(result.headword);
2021

21-
// AI 源带例句,且各例句均为 _ExampleView 的点击发音目标。
22+
// AI 单词源带例句,且各例句均为 _ExampleView 的点击发音目标。
2223
if (result is AiDictResult) {
23-
final entry = result.entry;
24-
for (final meaning in entry.meanings) {
25-
for (final ex in meaning.examples) {
26-
texts.add(ex.sentence);
27-
}
28-
}
29-
for (final expr in entry.commonExpressions) {
30-
texts.add(expr.example.sentence);
31-
}
32-
for (final item in entry.wordFamily) {
33-
texts.add(item.example.sentence);
24+
switch (result.entry) {
25+
case final DictionaryEntry entry:
26+
for (final meaning in entry.meanings) {
27+
for (final ex in meaning.examples) {
28+
texts.add(ex.sentence);
29+
}
30+
}
31+
for (final expr in entry.commonExpressions) {
32+
texts.add(expr.example.sentence);
33+
}
34+
for (final item in entry.wordFamily) {
35+
texts.add(item.example.sentence);
36+
}
37+
case final MultiWordDictionaryEntry entry:
38+
for (final meaning in entry.meanings) {
39+
for (final ex in meaning.examples) {
40+
texts.add(ex.sentence);
41+
}
42+
}
43+
for (final expr in entry.similarExpressions) {
44+
texts.add(expr.sentence);
45+
}
3446
}
3547
}
3648

0 commit comments

Comments
 (0)