# 日语查词模式总结 算是[[双击查词]]和[[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 在客户端实现——如果用户的输入没有变化,就使用之前保留的解析结果。 > 晩御飯を食べましたか。 晩ご飯を食べましたか。