Skip to content

Commit

Permalink
v0.3更新 (#19)
Browse files Browse the repository at this point in the history
* 更新readme,新增贡献者板块

* docs: add ks233 as a contributor for code (#10)

* docs: update README.md

* docs: update .all-contributorsrc

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>

* docs: add shaka0919 as a contributor for code (#11)

* docs: update README.md

* docs: update .all-contributorsrc

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>

* Update README.md (#12)

* Update .all-contributorsrc (#13)

* 修改错字、删除多余代码

* Update MainForm.cs

* Update MainForm.cs

* 沉浸模式

* 更新文档

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: 跨性别 <[email protected]>
  • Loading branch information
3 people authored Feb 20, 2024
1 parent 748ff5d commit 8c1ec82
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 48 deletions.
39 changes: 39 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"projectName": "ja-learner",
"projectOwner": "ks233",
"files": [
"README.md"
],
"commitType": "docs",
"commitConvention": "angular",
"contributorsPerLine": 7,
"contributors": [
{
"login": "ks233",
"name": "ks233",
"avatar_url": "https://avatars.githubusercontent.com/u/38981529?v=4",
"profile": "https://ks233.github.io/",
"contributions": [
"code"
]
},
{
"login": "shaka0919",
"name": "Harvey Wang",
"avatar_url": "https://avatars.githubusercontent.com/u/17539962?v=4",
"profile": "https://github.com/shaka0919",
"contributions": [
"code"
]
},
{
"login": "ly-nld",
"name": "跨性别",
"avatar_url": "https://avatars.githubusercontent.com/u/38471793?v=4",
"profile": "http://lgbt.sh",
"contributions": [
"infra"
]
}
]
}
120 changes: 98 additions & 22 deletions GUI/MainForm.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
using Microsoft.Web.WebView2.Core;
using System.Diagnostics;
using System.Runtime.InteropServices;
using MeCab;
using System.Text;
using System.Reflection.Metadata;
using OpenAI_API.Chat;
using Microsoft.VisualBasic;
using System.Windows.Forms.Design;
using System.Net.Http;

namespace ja_learner
{
Expand All @@ -16,9 +9,30 @@ public partial class MainForm : Form
DictForm dictForm;

TextAnalyzer textAnalyzer = new TextAnalyzer();
GptCaller gptCaller;
private string sentence = "";

private bool immersiveMode = false;
public bool ImmersiveMode
{
get { return immersiveMode; }
set
{
if (value)
{
webView.Parent = this;
tabControl.Hide();
panel1.Hide();
FormBorderStyle = FormBorderStyle.None;
}
else
{
webView.Parent = tabControl.TabPages[0];
tabControl.Show();
panel1.Show();
FormBorderStyle = FormBorderStyle.Sizable;
}
immersiveMode = value;
}
}
public string Sentence
{
get { return sentence; }
Expand Down Expand Up @@ -82,6 +96,20 @@ private void CoreWebView2_NewWindowRequested(object sender, CoreWebView2NewWindo
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
{
string message = e.TryGetWebMessageAsString();
// 来自webview的特殊按键事件处理
if (message == "DBLCLICK")
{
ImmersiveMode = !ImmersiveMode;
return;
}
else if (message == "MOUSEDOWN")
{
// if (ImmersiveMode)
{
Drag();
}
return;
}
dictForm.SearchText(message);
dictForm.ShowAndFocus();
Focus();
Expand Down Expand Up @@ -111,16 +139,16 @@ private void MainForm_SizeChanged(object sender, EventArgs e)
{
if (timerWindowAttach.Enabled)
{
heightAfter = this.Height;
heightAfter = Height;
if (dictForm.Visible)
{
dictForm.Width = this.Right - WindowAttacher.TargetWindowRect.Right;
dictForm.Width = Right - WindowAttacher.TargetWindowRect.Right;
}
}
}


private System.Drawing.Point locationBefore; // 记录普通模式下窗口的位置
private Point locationBefore; // 记录普通模式下窗口的位置
private Size sizeBefore; // 记录普通模式下窗口的大小
private int heightAfter = 200; // 附着模式时,窗体通常会比较矮

Expand All @@ -131,17 +159,17 @@ private void checkBoxWindowAttach_CheckedChanged(object sender, EventArgs e)
if (checkBoxWindowAttach.Checked)
{
// 记录普通状态的窗口位置,切换到吸附状态下的窗口位置
sizeBefore = this.Size;
locationBefore = this.Location;
this.Height = heightAfter;
sizeBefore = Size;
locationBefore = Location;
Height = heightAfter;
timerWindowAttach.Enabled = true;
}
else
{
timerWindowAttach.Enabled = false;
heightAfter = this.Height;
this.Size = sizeBefore;
this.Location = locationBefore;
heightAfter = Height;
Size = sizeBefore;
Location = locationBefore;
}
}

Expand All @@ -151,7 +179,7 @@ private void timerWindowAttach_Tick(object sender, EventArgs e)
{
WindowAttacher.AttachWindows(this, dictForm);
}
catch (Exception ex)
catch
{
WindowAttach = false;
}
Expand All @@ -176,6 +204,10 @@ public bool WindowAttach
private void checkBoxClipboardMode_CheckedChanged(object sender, EventArgs e)
{
ClipBoardMode = checkBoxClipboardMode.Checked;
if (ClipBoardMode)
{
Sentence = Clipboard.GetText(TextDataFormat.UnicodeText).Trim().Replace(" ", "");
}
}
private void timerGetClipboard_Tick(object sender, EventArgs e)
{
Expand All @@ -201,7 +233,7 @@ public bool ClipBoardMode

private void btnInputText_Click(object sender, EventArgs e)
{
Sentence = Microsoft.VisualBasic.Interaction.InputBox("手动输入", "输入句子", "", 0, 0);
Sentence = Interaction.InputBox("手动输入", "输入句子", "", 0, 0);
}

private void timerSelectWindow_Tick(object sender, EventArgs e)
Expand Down Expand Up @@ -250,7 +282,7 @@ private void textBoxHwnd_TextChanged(object sender, EventArgs e)
string windowTitle = WindowAttacher.GetWindowTitle(hwnd);
checkBoxWindowAttach.Text = $"与【{windowTitle}】对齐";
// 判断窗口句柄是不是自己的
if (hwnd == this.Handle || hwnd == dictForm.Handle)
if (hwnd == Handle || hwnd == dictForm.Handle)
{
checkBoxWindowAttach.Enabled = false;
return;
Expand Down Expand Up @@ -340,7 +372,51 @@ private void comboBoxExtraPrompts_Click(object sender, EventArgs e)

async private void checkBoxTranslateKatakana_CheckedChanged(object sender, EventArgs e)
{
await webView.ExecuteScriptAsync($"setTranslateKatakana({checkBoxTranslateKatakana.Checked.ToString().ToLower()})");
string param = checkBoxTranslateKatakana.Checked ? "true" : "false";
await webView.ExecuteScriptAsync($"setTranslateKatakana({param})");
}

// https://stackoverflow.com/questions/31199437/borderless-and-resizable-form-c
// 从↑抄来的代码,无边框模式下可调整窗口大小
const int WM_NCHITTEST = 0x0084;
const int HTCLIENT = 1;
const int HTCAPTION = 2;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCHITTEST:
if (m.Result == (IntPtr)HTCLIENT)
{
m.Result = (IntPtr)HTCAPTION;
}
break;
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style |= 0x40000;
return cp;
}
}

// 窗口拖拽
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool ReleaseCapture();

private void Drag()
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
}
82 changes: 58 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1 align="center">KS的日语学习工具 v0.2</h1>
<h1 align="center">KS的日语学习工具 v0.3</h1>
<div align="center">
<strong>📖 简易日语学习 / 视觉小说阅读辅助工具</strong>
<br />
Expand All @@ -14,6 +14,7 @@
</div>



## 功能介绍

* **语句分析**:用不同样式区分句子成分,为句子中的汉字注音
Expand All @@ -26,16 +27,25 @@

## 使用说明

### 基本的句子分析
### 分析与查词

* 读取剪贴板或手动输入句子
* 勾选“片假不留”可以把片假名单词翻译成英文
* 勾选“片假不留”把片假名单词翻译成英文
* 点击片假名单词上方的英文可以隐藏该单词的翻译,再点一下恢复显示,用于屏蔽错误的翻译结果
* 可以用 Ctrl + 滚轮调整分析界面的显示大小
* 本质浏览器套壳,你甚至可以按 F12 打开控制台(
* 在语句分析界面点击单词快速查词
* 左键点击单词会在词典窗口中显示 MOJi 辞書的搜索结果
* 中键点击单词会在浏览器中打开单词在 MOJi 辞書的搜索页面
* 左键点击单词:在词典窗口中显示 MOJi 辞書的搜索结果
* 中键点击单词:在浏览器中打开单词在 MOJi 辞書的搜索页面
* SHIFT + 左键点击单词:在搜索内容末尾追加该单词
* 词典窗口手动搜索
* 双击搜索框清空搜索内容
* 点击链接跳转至对应的词典网站


![demo](README/demo.gif)

搜索框与 SHIFT 追加搜索是 v0.3 新增的功能,MeCab 有时候会过度断句,比如把“二十四节气”当成了三个词,“四”的注音还标错了。用追加搜索功能就可以把拆开的词拼回来,使查词更加灵活。


### 窗口吸附
Expand All @@ -60,43 +70,67 @@

#### 使用 GPT(需要 API Key)

首先要配置 api key,在 `config.txt` 的第一行输入 api key,第二行输入 api url
首先要配置 api key,在 `appsettings.json` 中配置 ApiKey 与 ApiUrl

```
sk-xxxxxxxxx
https://api.openai.com/{0}/{1}
"ApiKey": "sk-xxx",
"ApiUrl": "https://api.openai.com/{0}/{1}",
```

如果你使用官方 API,第二行就不用修改,如果使用第三方反代,就要修改为相应的域名
如果你使用官方 API,就不用修改 ApiUrl,如果使用第三方反代,就要将其修改为相应的域名

配置好 api key 就可以使用 GPT 翻译和解说文本了。
配置好 ApiKey 就可以使用 ChatGPT 翻译和解说文本了。

![gpt](README/gpt.gif)

## 声明
### 沉浸模式

### 分析与翻译仅供参考
- 双击分析页面的背景,进入仅显示语句分析的沉浸模式
- CTRL + 左键拖动窗口
- 左键拖动窗口边缘调整窗口大小

本项目的分词与注音功能基于 MeCab,有时会犯一些低级错误,比如把「<ruby>身体<rt>からだ</rt></ruby>」注音为 しんたい、把「<ruby>二人<rt>ふたり</rt></ruby>」注音为 ににん,用词汇更丰富的 [UniDic](https://clrd.ninjal.ac.jp/unidic/) 词典替换 `dic` 文件夹中默认的 IPADIC 效果会稍好一些。
![immersive](README/immersive.gif)

翻译毕竟都是机翻,出错也很正常。谷歌翻译遇到复杂的句式和不规范的表达就容易翻车,ChatGPT 比谷歌懂更多俗语、流行语,但偶尔也会发癫,比如使用简体中文以外的语言回复、唐突的[塞氏翻译法](https://zh.moegirl.org.cn/zh-hans/塞氏翻译法)等等。建议把本软件当做一个精读工具而不是翻译器,把注意力放在日语原文上,只在不确定的时候使用翻译作为参考。
## 使用建议

外来语标注功能使用谷歌翻译将片假名单词翻译为英语,但不是所有片假名单词都是外来语,外来语也不一定来源于英语,还有像 supplies 和 surprise 这样的“同音词”也不好区分,因此也会出现标记错误的情况
本项目的分词与注音功能基于 MeCab,虽然整体准确率还算可以,但有时会犯一些低级错误,比如在某些语境下把<ruby>身体<rt>からだ</rt></ruby>注音为 しんたい、把<ruby>二人<rt>ふたり</rt></ruby>注音为 ににん,遇到读音特殊的人名也无法正确注音。用词汇更丰富的 [UniDic](https://clrd.ninjal.ac.jp/unidic/) 词典替换 `dic` 文件夹中默认的 IPADIC 效果会稍好一些

根据我个人的使用体验,整体准确率还可以接受,但还是不建议完全初学者使用,以免被误导。如果遇到可疑的注音或翻译,建议点击单词查看 MOJi 辞書的解释和注音,并对照不同引擎的翻译结果,或者使用 ChatGPT 的解说功能
翻译毕竟都是机翻,准确率有限。谷歌翻译遇到复杂的句式和不规范的表达就容易翻车,ChatGPT 比谷歌懂更多俗语、流行语,但比较不稳定,偶尔会使用简体中文以外的语言回复、唐突地使用[塞氏翻译法](https://zh.moegirl.org.cn/zh-hans/塞氏翻译法)。建议把本软件当做一个精读工具而不是翻译器,把注意力放在日语原文上,只在不确定的时候使用翻译作为参考

### 相关项目
外来语标注功能使用谷歌翻译将片假名单词翻译为英语,但不是所有片假名单词都是外来语,外来语也不一定来源于英语,还有像 supplies 和 surprise 这样的“同音词”也不好区分,因此也会出现标注错误的情况。

本项目的前端页面:[ks233/ja-learner-webview](https://github.com/ks233/ja-learner-webview)
根据作者自己的使用体验,整体准确率还可以接受,但还是不建议完全初学者使用,以免被误导。如果遇到可疑的注音或翻译,建议查询更权威的词典,比如 [Weblio 辞書](https://www.weblio.jp/)、大辞林、小学馆日中,网络用语可以查 [ニコニコ大百科](https://dic.nicovideo.jp/)

开坑的想法主要来源于 [YUKI 翻译器](https://github.com/project-yuki/YUKI)[Translation-Aggregator](https://github.com/Translation-Aggregator/Translation-Aggregator),前者支持了丰富的翻译接口,内置了文本提取功能,但使用起来比较复杂,且缺少快速查词的功能;后者虽然可以鼠标悬停查词,但只有日英词典、界面比较古老,而且翻译接口几乎炸完了,于是我决定搓一个更简单、更符合自己需求的工具。
## 相关项目

[taishi-i/awesome-japanese-nlp-resources](https://github.com/taishi-i/awesome-japanese-nlp-resources) 有非常多日语相关的工具和资源,对我开发这个项目帮助很大
开坑的想法主要来源于 [YUKI 翻译器](https://github.com/project-yuki/YUKI)[Translation-Aggregator](https://github.com/Translation-Aggregator/Translation-Aggregator),前者支持了丰富的翻译接口,内置了文本提取功能,但使用起来比较复杂,且缺少快速查词的功能;后者虽然可以鼠标悬停查词,但只有日英词典、界面比较古老,而且翻译接口几乎炸完了,于是我决定搓一个更简单、更符合自己需求的工具

### 使用的第三方库与框架
使用的第三方工具与参考资料:

* 形态分析:[taku910/mecab](https://github.com/taku910/mecab) 的 .Net 移植版本 [kekyo/MeCab.DotNet](https://github.com/kekyo/MeCab.DotNet)
* 调用 ChatGPT:[OkGoDoIt/OpenAI-API-dotnet](https://github.com/OkGoDoIt/OpenAI-API-dotnet)
* 谷歌翻译:参考了 [FilipePS/Traduzir-paginas-web](https://github.com/FilipePS/Traduzir-paginas-web) 的 API 调用方式
* ChatGPT:[OkGoDoIt/OpenAI-API-dotnet](https://github.com/OkGoDoIt/OpenAI-API-dotnet)
* [前端页面](https://github.com/ks233/ja-learner-webview)[WebView2 控件](https://www.nuget.org/packages/Microsoft.Web.WebView2),Vite + Vue
* 单词搜索:[MOJi 辞書](https://www.mojidict.com/)
* Webview 页面:Vite + Vue
* 谷歌翻译:参考了 [FilipePS/Traduzir-paginas-web](https://github.com/FilipePS/Traduzir-paginas-web) 的 API 调用方式
* 其它参考资源:[taishi-i/awesome-japanese-nlp-resources](https://github.com/taishi-i/awesome-japanese-nlp-resources)

## 贡献者

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->

<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://ks233.github.io/"><img src="https://avatars.githubusercontent.com/u/38981529?v=4?s=100" width="100px;" alt="ks233"/><br /><sub><b>ks233</b></sub></a><br /><a href="#code-ks233" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shaka0919"><img src="https://avatars.githubusercontent.com/u/17539962?v=4?s=100" width="100px;" alt="Harvey Wang"/><br /><sub><b>Harvey Wang</b></sub></a><br /><a href="#code-shaka0919" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://lgbt.sh"><img src="https://avatars.githubusercontent.com/u/38471793?v=4?s=100" width="100px;" alt="跨性别"/><br /><sub><b>跨性别</b></sub></a><br /><a href="#infra-ly-nld" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
</tr>
</tbody>
</table>

<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->
Binary file added README/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README/immersive.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion TextAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct TextAnalyzerResult
public string Surface;
public string Pos;// 词性
public string Basic;// 基本型
public string Reading;// 渲染
public string Reading;// 读音
public readonly string ToJson()
{

Expand Down
1 change: 0 additions & 1 deletion appsettings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"SecretKey": "Secret key value",
"AppSetting": {
"ApiKey": "sk-xxx",
"ApiUrl": "https://api.openai.com/{0}/{1}",
Expand Down

0 comments on commit 8c1ec82

Please sign in to comment.