我之前一直都在使用 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+))
长句翻译
目前长句翻译仅支持 , 目前支持 youdao
youdao
和 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
的所有元素, 也就是基本释义