This is a translation framework for emacs, and is flexible and powerful.
In addition to Google Translate, it now supports more engines like Google RPC API, Bing, DeepL. You can easily add other translation engines on the basis of the framework.
Very scalable, very flexible, asynchronous request and better user experience.
The reason I rewrite this is that I want to add the new RPC API of Google translation to this plugin. Since adding new API, it's not nice to implement the plugin as a translation framework first.
Then, there is it.
With MELPA and package.el:
M-x package-install RET go-translate RET
(require 'go-translate)
Or manually download go-translate.el
then put into /some-path
, then add this to .emacs
:
(add-to-list 'load-path "/some-path")
(require 'go-translate)
Add this to your configuration file:
(require 'go-translate)
(setq gts-translate-list '(("en" "zh")))
;; (setq gts-default-translator (gts-translator :engines (gts-bing-engine)))
(setq gts-default-translator
(gts-translator
:picker (gts-prompt-picker)
:engines (list (gts-bing-engine) (gts-google-engine))
:render (gts-buffer-render)))
Then use gts-do-translate
to start translation.
;; your languages pair used to translate
(setq gts-translate-list '(("en" "zh") ("fr" "en")))
;; config the default translator, it will be used by command gts-do-translate
(setq gts-default-translator
(gts-translator
:picker ; used to pick source text, from, to. choose one.
;;(gts-noprompt-picker)
;;(gts-noprompt-picker :texter (gts-whole-buffer-texter))
(gts-prompt-picker)
;;(gts-prompt-picker :single t)
;;(gts-prompt-picker :texter (gts-current-or-selection-texter) :single t)
:engines ; engines, one or more. Provide a parser to give different output.
(list
(gts-bing-engine)
;;(gts-google-engine)
;;(gts-google-rpc-engine)
;;(gts-deepl-engine :auth-key [YOUR_AUTH_KEY] :pro nil)
(gts-google-engine :parser (gts-google-summary-parser))
;;(gts-google-engine :parser (gts-google-parser))
;;(gts-google-rpc-engine :parser (gts-google-rpc-summary-parser) :url "https://translate.google.com")
(gts-google-rpc-engine :parser (gts-google-rpc-parser) :url "https://translate.google.com")
)
:render ; render, only one, used to consumer the output result. Install posframe yourself when use gts-posframe-xxx
(gts-buffer-render)
;;(gts-posframe-pop-render)
;;(gts-posframe-pop-render :backcolor "#333333" :forecolor "#ffffff")
;;(gts-posframe-pin-render)
;;(gts-posframe-pin-render :position (cons 1200 20))
;;(gts-posframe-pin-render :width 80 :height 25 :position (cons 1000 20) :forecolor "#ffffff" :backcolor "#111111")
;;(gts-kill-ring-render)
:splitter ; optional, used to split text into several parts, and the translation result will be a list.
(gts-paragraph-splitter)
))
Slots picker/engines/render/splitter
can be a function or lambda, it allows the dynamic initialization of slots while translating.
For example, set a separate translation behavior for pdf-tools:
(setq gts-default-translator
(gts-translator
:picker
(lambda ()
(cond ((equal major-mode 'pdf-view-mode)
(gts-noprompt-picker :texter (gts-current-or-selection-texter)))
(t (gts-prompt-picker))))
:engines
(lambda ()
(cond ((equal major-mode 'pdf-view-mode)
(gts-bing-engine))
(t (list
(gts-bing-engine)
(gts-google-engine :parser (gts-google-summary-parser))
(gts-google-rpc-engine)))))
:render
(lambda ()
(cond ((equal major-mode 'pdf-view-mode)
(gts-posframe-pop-render))
(t (gts-buffer-render))))))
Another example, adopt different translation strategies according to your variable or translate content:
(defvar your-gts-split-enable nil
"Split translate?")
(setq gts-default-translator
(gts-translator
:picker (gts-prompt-picker)
:splitter (lambda ()
(if your-gts-split-enable (gts-paragraph-splitter)))
:engines (lambda ()
(with-slots (text) gts-default-translator
(if your-gts-split-enable
(gts-deepl-engine :auth-key "xxx")
(list
(gts-bing-engine)
(gts-deepl-engine :auth-key "xxx")
(if (string-match-p " " text)
(gts-google-rpc-engine)
(gts-google-engine))))))
:render (gts-buffer-render)))
You can look into customize-group
- go-translate
for more configurations.
In addition to setup gts-default-translator
and direct use gts-do-translate
, you can define your own commands.
Eg, pick directly and use Google RPC API to translate:
(defun my-translate-command-1 ()
(interactive)
(gts-translate (gts-translator
:picker (gts-noprompt-picker)
:engines (gts-google-rpc-engine)
:render (gts-buffer-render))))
Eg, pick directly and add the results into kill-ring:
(defun my-translate-command-2 ()
(interactive)
(gts-translate (gts-translator
:picker (gts-noprompt-picker)
:engines (gts-google-rpc-engine)
:render (gts-kill-ring-render))))
Eg, pop a childframe to show the translation result:
(defun my-translate-command-3 ()
(interactive)
(gts-translate (gts-translator
:picker (gts-prompt-picker)
:engines (gts-google-rpc-engine)
:render (gts-posframe-pop-render))))
Eg, show multiple engines's result (Google/DeepL) in a pin childframe:
(defun my-translate-command-4 ()
(interactive)
(gts-translate (gts-translator
:picker (gts-prompt-picker)
:engines (list (gts-google-rpc-engine :parser (gts-google-rpc-summary-parser)) (gts-deepl-engine))
:render (gts-kill-ring-render))))
To avoid the cost of creating objects every time you call a command, you can define your command this way:
;; predefine
(defvar my-translator-n
(gts-translator :picker .. :engines .. :render ..))
;; reference
(defun my-translate-command-n ()
(interactive)
(gts-translate my-translator-n)
Compose yourself. Whatever you like.
The command gts-do-translate
will take gts-default-translator
as the default translator.
By default, it use gts-buffer-render
to display translation results. In the result buffer, there are several shortcut keys:
h
show helpg
refreshq
exitx
exchangessource language
andtarget language
and refresh the translationM-n
andM-p
, switch to the next/prev available translation direction, and refreshy
to speak the current selection or word. You should havemplayer/mpv
installed, or on Windows it will fallback to usepowershell
to do the tts job.C
clear all caches in gts-default-cacher
Use childframe to show the results.
You should install posframe
from MELPA first if you want to use these renders.
gts-posframe-pop-render
will pop a childframe in current position to show the results.
The frame will be disappeared by any user action, except when you focus into it. When you focus into the
frame, you can use all keybindings that like in gts-buffer-render
.
gts-posframe-pin-render
will pin a childframe to service for the translation result.
It is draggable and resizable, and you can change the position/color/etc as you wish.
By default, translator uses gts-prompt-picker
to pick the source text and translation from/to languages.
gts-prompt-picker
uses a texter to decide the initial source text, the default texter is gts-current-or-selection-texter
,
it takes the currently selected text or word-at-point
as the default source text.
In the pop-up read-from-minibuffer
interface triggled by gts-prompt-picker
, you can use C-l
to clear the input, and fire the translation
with Return
. Also, you can use C-n
and C-p
to switch translation directions. These directions are those configured in gts-translate-list
above.
The gts-noprompt-picker
is another choice if you don't like the prompting style's picking.
It will automately take the text from texter and choose a suitable from/to, then translate directly.
Make mix-source-and-target-style translation possible. This is a basic implement to split source text to several parts.
Extending components is easy and happy, go and have a try!
You can replace almost everything. logger/cacher/http-client, picker/texter/render and engine...
For example, you want to insert the results directly into the buffer, create your own render like below.
Define class and override methods:
;; A class
(defclass your-render (gts-render) ())
;; A method
(cl-defmethod gts-out ((_ your-render) task)
(deactivate-mark)
(with-slots (err parsed) task
(if err (user-error "%s" err))
(insert (if (listp parsed) (string-join parsed "\n\n") parsed))))
Use them in your translator:
(defun my-translate-command-5 ()
(interactive)
(gts-translate (gts-translator
:picker (gts-noprompt-picker)
:engines (gts-google-rpc-engine)
:render (your-render) ; yeap!
)))
Of course, it's relatively easy to build a new translation engine too:
- create a class from
gts-engine
, implementgts-translate/gts-tts
- create a class from
gts-parser
, implementgts-parse
For example, if you can't stand the poor performance of url.el
,
you can implement your own gts-http-client
with curl, etc.
More documentation will be added later.