为maple-translate增加更友好的sdcv离线翻译


之前我写的Emacs翻译插件 maple-translate 有一个 sdcv 离线翻译的功能,它使用了 Elisp 解析 StarDict 的字典文件,所以不需要安装其它任何依赖。但是也是因为此原因,首次使用离线翻译需要等待字典文件加载到内存,这不是很友好,此次我将添加 sdcv 二进制文件的支持,使离线翻译能够更加快捷方便

首先是安装 sdcv 二进制文件

└──╼ brew install sdcv
└──╼ sdcv --help
用法:
  sdcv [选项…]  words

帮助选项:
  -h, --help                     显示帮助选项

应用程序选项:
  -v, --version                  display version information and exit
  -l, --list-dicts               display list of available dictionaries and exit
  -u, --use-dict=bookname        for search use only dictionary with this bookname
  -n, --non-interactive          for use in scripts
  -j, --json-output              print the result formatted as JSON
  --json                         print the result formatted as JSON
  -e, --exact-search             do not fuzzy-search for similar words, only return exact matches
  -0, --utf8-output              output must be in utf8
  -1, --utf8-input               input of sdcv in utf8
  -2, --data-dir=path/to/dir     use this directory as path to stardict data directory
  -x, --only-data-dir            only use the dictionaries in data-dir, do not search in user and system directories
  -c, --color                    colorize the output

然后定义二进制文件的路径,如果 sdcv 未安装,则返回为 nil

(defvar maple-translate-sdcv-program (executable-find "sdcv"))

接着修改之前写好的 maple-translate-sdcv 函数,通过判断 maple-translate-sdcv-program 是否为空采取不同的翻译操作

(defun maple-translate-sdcv(word &optional callback)
  "Search WORD with sdcv, use async request if CALLBACK non-nil."
  (if maple-translate-sdcv-program
      (maple-translate-execute maple-translate-sdcv-program
        :args (append '("-n" "-x" "-j" "-0" "-1" "-2")
                      (cl-loop for dict in maple-translate-sdcv-dicts
                               collect (expand-file-name (cdr dict) maple-translate-sdcv-dir))
                      (list word))
        :format (maple-translate-sdcv-format)
        :callback callback)
    ;; ...
    ;; 使用ELisp解析并翻译
    ))

需要说明的是,由于该函数接收一个 callback 的变量,用于处理异步翻译,如果是同步翻译,可以直接使用 call-process 获取结果

(with-temp-buffer
  (apply 'call-process "sdcv" nil t nil '("-n" "-x" "-j" "-0" "-1" "-2" "/Users/xxx/.emacs.d/stardict/stardict-lazyworm-ec-2.4.2" "word"))
  (buffer-string))

但如果是异步翻译,则需要使用 start-process ,再通过监听进程状态在进程结束后再获取翻译结果

(let ((name (format "maple-translate-process %s" ,program)))
  (set-process-sentinel
   (apply 'start-process name (format "*%s*" name) ,program ,args)
   (lambda(process _)
     (unless (process-live-p process)
       (with-current-buffer (process-buffer process)
         (prog1 (funcall ,callback ,format)
           (kill-buffer (current-buffer))))))))

这里的 buffer 名称也可以通过 (generate-new-buffer " *temp*" t) 生成一个临时 buffer

最后就是翻译结果的展示,由于输出的是多行 json,比如:

[]
[{"dict": "懒虫简明英汉词典","word":"word","definition":"\n[wә:d]\nn.\n字, 词, 话, 消息, 诺言, 命令\nvt.\n为...措辞"}]

所以我在解析翻译结果时取了个巧,没有使用 (buffer-string) 而是直接在 buffer 里操作,每次都跳到开始位置,再依次向下移动 n 行,这个 n 即是字典的数量,最后使用 (thing-at-point 'line t) 获取当前行的数据

(defun maple-translate-sdcv-format()
  "Format result with sdcv output."
  (let ((results (cl-loop for index from 0
                          for dicts in maple-translate-sdcv-dicts
                          collect
                          (progn
                            (goto-char (point-min))
                            (forward-line index)
                            (string-join (cl-loop for child across-ref (json-read-from-string (decode-coding-string (thing-at-point 'line t) 'utf-8))
                                                  collect (format "%s: %s"
                                                                  (alist-get 'dict child)
                                                                  (alist-get 'definition child)))
                                         "\n\n")))))
    (unless (null results)
      (string-join (cl-remove nil results) "\n\n"))))

最终效果

maple-translate的具体修改可见: dfd0eae

作者: honmaple
链接: https://honmaple.me/articles/2024/04/wei-maple-translatezeng-jia-geng-you-hao-de-sdcvchi-xian-fan-yi.html
版权: CC BY-NC-SA 4.0 知识共享署名-非商业性使用-相同方式共享4.0国际许可协议
wechat
alipay

加载评论