# 日语查词模式总结
算是[[双击查词]]和[[Melonpan]],[[英语]] 这个不是很明显,[[非辞書]]上就体现得很明显。
部分笔记在 Lark 可云端也存一份。
## 模式
### 注音模式
ruby_anote
应用场景:给一段话标注读音。
原理: 用 Mecab 分析,返回读音即可。
### 整句模式
应用场景:
1. 用户看不懂整个句子,需要整个句子的分析结果
1. 参考捧读、MOJi
2. 用户想制作保留了上下文的 Anki卡片,提供句子后,自动选中分析结果中【难度最高】的那个单词
3. 用户想制作保留了上下文的 Anki卡片,提供句子后,标注或者提取【难度较高】的单词
原理: 用 Mecab 分析,返回所有解析结果。
在2和3的场景,对于日语高阶学习者而言,应该可以直接过滤掉助词。Weblio 和 Goo 辞书的每月的热搜单词绝大多数都是名词和动词的现象,也能印证这个看法。
但大多数日语初学者更在意【にでへはをが】这个几个助词,在日语学习群的观察能印证这一点。
对于这个情况,也许应该考虑借助 RAG ,让GPT结合上下文和词典的解释生成更靠谱的解释。另外,是类似问答机器人的那种对话界面,极有可能是中日文混合输入,这应该会对Mecab 的分析有一定影响。
### 辞书模式
应用场景:
1. 用户在其他软件读小说时,长按单词即可查到解释
1. 参考:Yomichan 和 jidoujisho
2. 用户在翻译时,想确认某个单词的译法是否准确,将光标放到想查单词的起始位置,无需用鼠标选中单词,直接双击键盘任意按键,程序将光标后的文本上传到服务器,分析然后返回第一个解析结果
3. 用户在打字时,突然想确认才输入的内容,无需用鼠标选中单词,程序直接读取光标前的文本,分析并返回最后一个解析结果
原理: 返回所有解析结果中第一个或最后一个。
优点:不要求用户提供完整的上下文,且通过手动设定最大分析字数,无视多余的部分,可以有效节省服务器成本。
缺陷:缺少上下文可能导致解析结果不正确。(需用实际数据验证合理的最大分析字数是多少)
底层封装虽然不用区分用户要查的是光标前还是光标后的内容,但对外提供的方法还是要进行区分。
如果光标后面没有文本,那可以确定用户想查的内容就是光标前面的。
### 智能模式
上传整个句子,但返回类似辞书模式的结果。
场景:
1. 用户在其他软件读小说时,长按单词即可查到解释
1. Yomichan 和 jidoujisho有个问题:如果按的地方不是单词的开头位置,那分析结果就错了(只分析了部分文本
2. 但 Kindle、MOJi 阅读没有这个问题,应该是先基于了完整的上下文进行了分析,将分析的结果保存在本地
2. 用户在翻译时,想确认某个单词的译法是否准确,将光标放到想查单词的起始位置,无需用鼠标选中单词,直接双击键盘任意按键,程序将光标后的文本上传到服务器,分析然后返回第一个解析结果
1. 用户没有准确将光标放到单词的起始位置,而是单词的中间
3. 用户在打字时,想确认才输入的内容,无需用鼠标选中单词,直接读取光标前的文本,分析并返回最后一个解析结果。
1. 注意,用户有可能在输入了【。】或者【、】后,才想起来查词,这个时候的最后一个解析结果其实是标点。
2. 注意这个模式很可能需要提出来,尤其是用户的输入并不完整的时候。
综上,最好的选择还是上传完整的句子。这种模式也是[[易查]]默认的模式。
原理: 除了完整的句子,也要提供光标在完整句子里的位置。
解析完整个句子之后,计算出光标附近的单词的解析结果,再返回即可。
```Markdown
晩ご飯を食べましたか。
解析结果:
晩ご飯 晩御飯
を を
食べ 食べる
まし ます
た た
か か
```
## 算法实现
### 辞书模式算法实现
笑了,下面的代码问题不少:
首先输入的文本中表层形可能会有重复值,而
其次,字典默认顺序其实是会被打乱的233
```Python
# 晩ご飯を食べましたか。
result = {"晩ご飯": "晩御飯", "を": "を", "食べ": "食べる", "まし": "ます", "た": "た", "か": "か","。":"。"}
def _cursor_word(analysis_result:dict, cursor_index:int)->str:
"""
"""
if sum(len(key) for key in result) < cursor_index:
# 如果光标所在的位置大于文本的长度,直接抛出异常
raise ValueError("Cursor index is out of range.")
length_before_cursor = 0
for surface, base_form in analysis_result.items():
length_before_cursor += len(surface)
if length_before_cursor >= cursor_index:
# TODO = 说明用户的光标正好放在2个句节的分界处
# 可以考虑基于难度和词频猜测哪个更有可能是用户想查的单词,
# 或者允许按照个人习惯,设置这种情况是返回前还是后
return base_form
print(_cursor_word(result,4))
import unittest
# 单元测试
class TestCursorWord(unittest.TestCase):
def test_cursor_word(self):
# 0晩1ご2飯3を4食5べ6ま7し8た9か10。11
test_cases = [
# (光标索引, 期望的输出)
(0, "晩御飯"), # 光标在"晩御飯"之前
(1, "晩御飯"),
(2, "晩御飯"),
(3, "晩御飯"),
(4, "を"),
(5, "食べる"),
(6, "食べる"),
(7, "ます"),
(8, "ます"),
(9, "た"),
(10, "か"),
(11, "。"),
# 异常测试
(12, "。"),
(1111, "。"),
]
for cursor_index, expected_output in test_cases:
with self.subTest(cursor_index=cursor_index, expected_output=expected_output):
if cursor_index > sum(len(key) for key in result):
# 如果光标位置超出范围,检查是否抛出异常
with self.assertRaises(ValueError):
_cursor_word(result, cursor_index)
else:
self.assertEqual(_cursor_word(result, cursor_index), expected_output)
if __name__ == '__main__':
unittest.main()
```
上面的示例代码其实可以用 JS 在客户端实现——如果用户的输入没有变化,就使用之前保留的解析结果。
> 晩御飯を食べましたか。
晩ご飯を食べましたか。