Translation In Emacs

screenshot.png
图1  Emacs 中翻译插件的使用截图,包括 emacs-immersive-translate, gt, fanyi

Emacs China 的时候看到了一些在 Emacs 中翻译的插件1,我也跟着配置了一些。得益于 LLM(Large Language Model),翻译的质量也好了很多。这篇文章记录一下我都使用了什么插件,怎么配置的以及是如何使用的。

插件使用

插件我主要使用这几个:

Elilif/emacs-immersive-translate
Immersive-translate provides bilingual simultaneous display and translation of any text in Emacs.
lorniu/gt.el
Translator on Emacs. Supports multiple engines such as Google, Bing, deepL, ChatGPT, StarDict, Youdao and so on.
condy0919/fanyi.el
Not only English-Chinese translator for Emacs.
karthink/gptel
A simple LLM client for Emacs.

我使用最多的是 emacs-immersive-translate,它的灵感是 Immersive Translate,是我使用频率极高的一个浏览器翻译插件。 emacs-immersive-translate 给我的体验和 Immersive Translate 很接近,我主要是用来查看 Emacs 中的 Info,它还有 immersive-translate-auto-mode ,可以在 buffer 变化的时候自动翻译,很方便。

emacs-immersive-translate 针对 Emacs 中的 Info,elfeed 等做了适配,总体效果看起来是不错的,每次我看 Info 都会用到。

gt.el 的灵活度会更高一些,我主要用来选中文本之后进行翻译。

fanyi 则是当作字典使用,碰到一两个单词不熟悉,就使用 fanyi 进行查询。我很喜欢 fanyi 里面的 海词,它可以生成图表,呈现单词含义的使用频率,让我了解这个单词最常见的含义。

最后,我还会用到 gptel,它实际上不是专门用来翻译的,但是作为 LLM,我可以在任何地方调用它,帮我完成一些翻译任务。

插件配置

你可以在 init-translate.el 中查看翻译的相关配置。

emacs-immersive-translate

你可以在 MELPA 上直接安装 emacs-immersive-translate。

不过我修改了源码,调整了翻译文字颜色,所以我是作为 git submodules 安装的。

然后我配置了使用 OpenRouter 作为翻译服务:

;;; immersive-translate
(require 'immersive-translate)
(add-hook 'elfeed-show-mode-hook #'immersive-translate-setup)
;; need to api key with user `apikey` in `.authinfo`
(setq immersive-translate-backend 'chatgpt
      immersive-translate-chatgpt-host "openrouter.ai/api"
      immersive-translate-chatgpt-model "google/gemini-2.5-flash"
      immersive-translate-pending-message "(≖ᴗ≖๑)"
      immersive-translate-failed-message "(つд⊂) ")

API Key 在 ~/.authinfo 中配置,由于 emacs-immersive-translate 是查找 user 为 apikey 的配置,因此 user 这里需要设置为 apikey

machine openrouter.ai/api login apikey password ********************************

关于 .authinfo 你可以参考:

gt.el

gt.el 也是可以在 MELPA 上直接安装。

我将它配置成使用 OpenRouter 作为翻译服务,翻译显示在一个新的 buffer 中。

;; @see: https://github.com/lorniu/gt.el
(maybe-require-package 'gt)
(maybe-require-package 'plz)
(with-eval-after-load 'init-auth
  (with-eval-after-load 'gt
    (setq gt-langs '(en zh)
          gt-chatgpt-key (spike-leung/get-openrouter-api-key)
          gt-chatgpt-host "https://openrouter.ai/api"
          gt-chatgpt-model "google/gemini-2.5-flash"
          gt-default-translator (gt-translator
                                 :taker (gt-taker :pick nil :prompt t)
                                 :engines (gt-chatgpt-engine :stream t)
                                 :render (gt-buffer-render
                                          :name "gt-translator"
                                          :window-config '((display-buffer-at-bottom))
                                          :then (lambda (_) (pop-to-buffer "gt-translator")))))))

API Key 也是配置在 ~/.authinfo ,使用 spike-leung/get-openrouter-api-key 这个方法从文件中查找对应的密钥。

spike-leung/get-openrouter-api-key

Source: init-auth.el

;;; init-auth --- config for auth related -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:
(setq epa-pinentry-mode 'loopback) ;; input pass in minibuffer

;; (auth-source-pass-enable) ;;; use pass to manage auth-source

;;; function to get api-key from authinfo
(defun spike-leung/get-api-key (service &optional host)
  "Retrieve the API key for SERVICE from authinfo.  if HOST exist, retrieve by HOST."
  (let* ((host (or host (format "api.%s.com" service)))
         (creds (car (auth-source-search :host host :port 443))))
    (if creds
        (let ((api-key (plist-get creds :secret
                                  )))
          (if (functionp api-key)
              (funcall api-key)
            api-key
            (error "API key not found for %s" service)))
      (error "No credentials found for %s" service))))

(defun spike-leung/get-deepseek-api-key ()
  "Retrieve the DeepSeek API key from authinfo."
  (spike-leung/get-api-key "deepseek"))

(defun spike-leung/get-openrouter-api-key ()
  "Retrieve the OpenRouter API key from authinfo."
  (spike-leung/get-api-key "openrouter" "openrouter.ai"))

(provide 'init-auth)
;;; init-auth.el ends here.

Source

fanyi

fanyi 从 MELPA 上直接安装使用即可。

gptel

gptel 也是从 MELPA 上直接安装,然后配置 OpenRouter,具体配置见 init-gptel.el

gptel 就是当 LLM 用,需要翻译的时候就复制文字和它对话。此外,在日常开发中,gptel 还能用来重构代码,完成一些重复任务,给我一些初始代码逻辑等,也是我用的很频繁的插件。

快捷键

为了方便使用,我将这几个插件绑定到了相同的快捷键入口。

;;; keybindings
(defvar spike-leung/my-translate-keymap (make-sparse-keymap)
  "Keymap for translation commands.")

(define-key spike-leung/my-translate-keymap (kbd "i") 'immersive-translate-buffer)
(define-key spike-leung/my-translate-keymap (kbd "p") 'immersive-translate-paragraph)
(define-key spike-leung/my-translate-keymap (kbd "c") 'immersive-translate-clear)
(define-key spike-leung/my-translate-keymap (kbd "f") 'fanyi-dwim2)
(define-key spike-leung/my-translate-keymap (kbd "g") 'gt-translate)

(with-eval-after-load 'init-my-keybindings
  (define-key spike-leung/meta-o-keymap (kbd "t") spike-leung/my-translate-keymap))

写在最后

有了翻译插件,在阅读 info 等文档上真的帮助很大,极大地提高阅读效率。集成了 LLM 后,翻译的质量也好了很多。但依赖翻译插件也有个问题,就是原文会看的比较少,不利于增进英语学习。

如果你还有更好的插件,欢迎推荐啦 (ノ>ω<)ノ

脚注:

Webmentions (0)

如果你想回应这篇文章,你可以在你的文章中链接这篇文章,然后在下面输入你的文章的 URL 并提交。你的回应随后会显示在此页面上(如果是垃圾信息我会屏蔽)。如果要更新或删除你的回应,请更新或删除你的文章,然后再次输入该文章的 URL 并提交。(了解有关 Webmention 的更多信息。)


作 者: Spike Leung

创建于: 2025-01-16 Thu 21:39

修改于: 2025-11-05 Wed 11:17

许可证: 署名—非商业性使用—相同方式共享 4.0

支持我: 用你喜欢的方式