Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/FlutterBuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: '3.41.0'
flutter-version: '3.44.0'
- name: Decode keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/release.jks
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/FlutterBuildTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: '3.41.0'
flutter-version: '3.44.0'

- run: flutter pub get
- run: flutter build windows
Expand All @@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v6
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.0'
flutter-version: '3.44.0'
- run: flutter pub get
- run: flutter build web --release
- uses: actions/upload-artifact@v7
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@

- 复习模块添加了强化学习选项
- 添加了学习中单词自动播放发音的功能
- 复习阶段添加了进度指示

### Improvement

- 优化了FSRS学习机制
- 听写界面不熄屏
- 优化解压逻辑,避免主线程卡死
- 复习改为自动乱序
- 更新Flutter框架至3.44.0

### Fix

- 修复了可能的复习列表重复问题
- 补全控制器销毁逻辑,避免内存泄露
- 修复了网页端中无法通过文件导入数据的问题
- 修复了单词推送不截止的问题

## v1.0.0 - 2026-3-18 - (100000)

Expand Down
29 changes: 16 additions & 13 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@

待审查通过后,你的提交就会被合并。

---

## 你必须做到的

请在你进行提交和PR时关注以下的问题:
Expand All @@ -44,7 +46,7 @@

这个项目设置了必须要有GPG验证了是你的更改后才能进行合并。

> 如果你不知道GPG是什么,请参照[这篇文章](https://docs.github.com/zh/authentication/managing-commit-signature-verification/about-commit-signature-verification)
> 如果你不知道GPG是什么,请参照[这篇文章](https://docs.github.com/authentication/managing-commit-signature-verification/about-commit-signature-verification)

### DCO签名

Expand All @@ -54,6 +56,8 @@

你可以通过 `git commit -s` 对提交进行签署。

---

## 其他的事项

### 代码规范
Expand All @@ -67,18 +71,17 @@
- 对于一个类,请使用大写字母或者下划线开头
- 对于一个函数,请使用小写字母开头

违反此规定的PR会要求进行相应修改后即可
---

### 关于AI辅助生成代码

> 该定义仅对此项目生效

#### 存储单词数据的变量
定义: 通过 **自然语言** 请求或指导 **大语言模型(LLM)** 生成贡献中的 **编程语言代码** ,并且 **由人工的编写代码** 占贡献中 **40%** 以下的,即认为是 **AI辅助生成代码** 。

在应用中,存储单个阿拉伯语单词数据的变量使用`word`为变量名`Map<String, dynamic>`为其类型,内部详细结构如下
要求: 我并不反对通过AI辅助生成代码,但**十分希望**通过AI辅助生成的贡献能做到以下几点:

``` dart
Map<String, dynamic> word = {"arabic": "{Arabic}",
"chinese": "{Chinese}",
"explanation": "{explanation}",
"subClass": "{className}",
"learningProgress": "{times}", //int
"id": "{id}" // int getSelectedWords函数结果中会有,并且所有面向用户的界面中调用的数据都应具有此键值对。
};
```
1. 尊重他人,避免出现有侮辱性的部分。(要是出现了这类问题,**请不要拿AI做挡箭牌**)
2. 注意不要在代码中泄露你的Key。(当然,如果你想让别人也免费用的话,那就另说了)
3. 先在你自己的环境下确保你的代码能正常运行。
4. AI也会犯错,在你请求核查之前请先自己理解并排查可能的问题。
104 changes: 102 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,105 @@
<!-- markdownlint-disable MD033 -->
# arabic_learning | Ar 学

> 该软件目前还在开发阶段,很多功能尚未完善,使用时请倍加小心。
<p align="center">
<img src="https://github.com/OctagonalStar/arabic_learning/actions/workflows/FlutterBuild.yml/badge.svg" alt="Flutter Build">
<img src="https://img.shields.io/badge/License-AGPL_3.0-blue.svg" alt="License: AGPL-3.0">
</p>

一个用于记背阿拉伯语单词的学习软件。
一个跨平台的阿拉伯语 ~~背单词~~ 单词学习软件,支持 **Android / Windows / Linux / macOS / Web**。

## 功能

### 学习

- 选择课程进行单词学习
- 多种题型可自由配置与排序:单词卡片、中译阿选择题、阿译中选择题、拼写题、听力题
- 题型内/题型间/全局三级乱序
- 偏好易混词或同课词

### 规律复习(FSRS)

> [!TIP]
> 你需要手动启用复习功能才会生效

- 基于 [FSRS](https://github.com/open-spaced-repetition/fsrs4anki) 算法的间隔重复复习
- 可自由配置期望提取率、评分限时(优秀/良好)
- 自我评级模式
- 每日单词推送,学习即加入复习计划
- 强化记忆循环:同一到期卡片在队列中交错出现多次

### 测试

- **自主听写**:配置播放速度/次数/间隔,听写期间不熄屏
- **局域网联机对战**:通过 WebRTC 实现,支持扫码/口令连接,双方实时 PK

### 词库管理

- 从Github[此仓库](https://github.com/JYinherit/Arabiclearning)线上下载词库
- 本地 JSON 文件导入
- 词汇总览:按词库-课程分级展示,支持网格列数自定义
- 词汇查找:阿语/中文双向搜索,支持 BK 树模糊匹配及编辑距离容错

### 数据同步

- **WebDAV** 远程备份与恢复
- 本地数据导出/导入(JSON 文件)

### 音频与 TTS

- 系统 TTS / 在线 TTS/ 神经网络语音合成(sherpa-onnx / VITS)
- 播放速度可调(0.5x – 1.5x)
- 自动播放发音(学习中进入阿译中选择题时自动朗读)

### 个性化

- 多种主题色
- Material UI
- 深色模式
- 备用字体(阿语 Vazirmatn / 中文 NotoSansSC)

### 统计

- 连胜天数
- 已学词汇
- 待复习数量
- 词库单词总数

## 平台

| 平台 | 支持 |
| ------ | ------ |
| Android | ✅ |
| Windows | ✅ |
| Linux | ✅ (请自行构建) |
| macOS | ✅ |
| iOS | 已在v0.1.12终止支持 |
| Web | ✅ [在线体验](https://octagonalstar.github.io/arabic_learning/) |

> 网页版为最新构建版,相对于软件版本可能有更多功能和更多 bug。

## 技术栈

- **框架**:Flutter/Dart
- **状态管理**:Provider
- **复习算法**:FSRS
- **音频**:flutter_tts / just_audio / sherpa_onnx
- **联机**:flutter_webrtc
- **同步**:webdav_client
- **搜索**:BK 树
- **存储**:shared_preferences / idb_shim(Web IndexedDB)

## 构建

```bash
flutter pub get
flutter run
```

各平台构建产物参见 GitHub Releases 和 Github Action。

## 许可证

[AGPL-3.0](LICENSE)

Copyright (C) 2025 OctagonalStar
9 changes: 9 additions & 0 deletions lib/funcs/fsrs_func.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ class FSRS {
logger.fine("计算得分: hard");
return Rating.hard;
}

DateTime? getCardBirthday(int wordId) {
try {
return config.reviewLogs.firstWhere((ReviewLog rvl) => rvl.cardId == wordId).reviewDateTime;
} catch (e) {
logger.severe("wordID: $wordId card not found or has more than one");
return null;
}
}
}

@immutable
Expand Down
2 changes: 1 addition & 1 deletion lib/package_replacement/fake_dart_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class File {
// nothing
return "";
}
bool existsSync() {return true;}
bool existsSync() {return false;}

void deleteSync() {}

Expand Down
4 changes: 3 additions & 1 deletion lib/pages/learning_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,13 @@ class LearningPage extends StatelessWidget {
int tries = 0;
while(pushWords.length < FSRS().config.pushAmount && tries < FSRS().config.pushAmount * 10){
int chosen = rnd.nextInt(AppData().wordData.words.length);
if(!FSRS().isContained(chosen)) {
DateTime? cardBirthday = FSRS().getCardBirthday(chosen);
if(cardBirthday == null || cardBirthday.difference(DateTime.now()).inDays == 0) {
pushWords.add(AppData().wordData.words.elementAt(chosen));
}
tries++;
}
pushWords.removeWhere((WordItem item) => FSRS().isContained(item.id));
if(pushWords.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("今日的推送已完成"), duration: Duration(seconds: 1),),
Expand Down
Loading
Loading