我之前一直都在使用 youdao-dictionary 这个插件,虽然没有配置过秘钥,这时会有使用次数的限制(反正是每天1000次还是多少来着,有些忘了),但是我平时使用次数不多,所以也不会达到使用限制,不管是单词翻译还是长句翻译都很不错。但最近几个月 youdao-dictionary 如果不配置私钥,处于一个完全不可用的状态。
我曾寻找过它的替代品,并且使用过一段时间的 fanyi.el,不可否认,这同样是一个非常不错的插件,不管是UI还是功能,但是,可能是因为 fanyi.el 使用异步请求的原因,偶尔会出现一些意想不到的报错,更重要的是它不支持长句翻译和离线翻译。
考虑再三,还是决定自己造一个轮子,主要功能就按照我之前使用 youdao-dictionary 的习惯:
翻译光标下的单词,以及选中的单词或句子
翻译的内容输出到
echoarea或者新的buffer,方便复制增加一个离线翻译的功能,并且可以不依赖外部工具
基于上述,所以有了这个新的轮子 maple-translate
如何使用?
可以使用 quelpa 安装
(use-package maple-translate :quelpa (:fetcher github :repo "honmaple/emacs-maple-translate") :commands (maple-translate maple-translate+))
或者手动下载仓库
git clone https://github.com/honmaple/emacs-maple-translate ~/.emacs.d/site-lisp/maple-translate
然后进行配置
(use-package maple-translate
:ensure nil
:commands (maple-translate maple-translate+))
长句翻译
目前长句翻译仅支持 , 目前支持 youdaoyoudao 和 google 两种翻译引擎,可以修改设置
(setq maple-translate-engine 'youdao) ;; 或者 (setq maple-translate-engine 'google)
谷歌翻译
国内无法直接使用,但是可以单独为谷歌翻译设置代理
(setq maple-translate-google-url "https://translate.googleapis.com/translate_a/single") (setq maple-translate-google-proxies '(("http" . "127.0.0.1:1086") ("https" . "127.0.0.1:1086")))
离线翻译
我个人平时在使用 Emacs 时不太习惯依赖外部的工具,比如我之前就写过一个markdown,org-mode实时预览插件 maple-preview,它区别于其它插件,并不会依赖外部的工具,比如 Pandoc,甚至不会生出任何文件到我的本地环境
同样的,我也不希望使用离线翻译时还要依赖外部诸如 sdcv,goldendict 等工具,我想要直接使用 Elisp 来对词典进行解析。所幸前人栽树,后人乘凉,已经有了一个纯 Elisp 实现的解析器 https://www.emacswiki.org/emacs/stardict.el
我所需要做的,就是下载需要的词典到本地(这个步骤是必须的,即使我不喜欢),然后设置
;; 离线词典所在的目录 (setq maple-translate-sdcv-dir "~/.stardict/dicts") ;; 所使用的词典 -> (词典名词 . 词典具体目录) (setq maple-translate-sdcv-dicts '(("lazyworm-ec" . "stardict-lazyworm-ec-2.4.2") ("lazyworm-ce" . "stardict-lazyworm-ce-2.4.2")))
最后修改翻译引擎
(setq maple-translate-engine 'sdcv)
注意:第一次使用离线翻译需要等待词典初始化,这会需要耗费一段时间,后面就快了
多引擎翻译
maple-translate 同样支持多引擎,需要修改设置为一个列表
(setq maple-translate-engine '(youdao dictcn sdcv))
翻译原理
目前支持的几个引擎: youdao(有道)、dictcn(海词)、iciba(金山词霸)、bing(必应)、google(谷歌)、sdcv(离线)。除 sdcv 外,其它几个都依赖于网络,maple-translate 可以看作是一个爬虫,通过爬取翻译页面,然后使用Emacs内置的 dom 对HTML进行解析,最后获取到想要的内容(谷歌使用API获取)。基于此,我还写了一个超简单的 类xpath 解析器
(defun maple-translate-dom-by-key(dom key)
(let (func num)
(when (string-match "\\[\\([0-9]+\\)\\]" key)
(setq num (match-string 1 key))
(setq key (substring key 0 (- (length key) (length num) 2))))
(cond ((string-prefix-p "." key)
(setq key (substring key 1) func 'dom-by-class))
((string-prefix-p "#" key)
(setq key (substring key 1) func 'dom-by-id))
((string-prefix-p "*" key)
(setq func (lambda(p _) (dom-children p))))
(t (setq key (intern key) func 'dom-by-tag)))
(if (null num)
(funcall func dom key)
(nth (string-to-number num) (funcall func dom key)))))
(defun maple-translate-dom-find(dom xpath)
(cl-loop for key in (string-split (string-trim xpath) "/")
if (consp dom)
do (setq dom (maple-translate-dom-by-key dom key))
else return dom)
dom)
这样,我就能通过一些简单的语法来定位想要的内容,比如在有道里面使用
(maple-translate-dom-find dom ".simple dict-module/.trans-container/.word-exp")
获取到 class 名称为 simple dict-module 下的 class 名称为 trans-container 下的 class 名称为 word-exp 的所有元素, 也就是基本释义
知识共享署名-非商业性使用-相同方式共享4.0国际许可协议