reverso.el icon indicating copy to clipboard operation
reverso.el copied to clipboard

Emacs client for https://www.reverso.net/: translation, grammar check, context and synonyms search

#+TITLE: reverso.el

[[https://melpa.org/#/reverso][file:https://melpa.org/packages/reverso-badge.svg]]

Emacs client for [[https://www.reverso.net/][Reverso]]. The implemented features are:

  • [[https://www.reverso.net/text-translation][Translation]]
  • [[https://context.reverso.net/translation/][Context]] (AKA bilingual concordances)
  • [[https://www.reverso.net/spell-checker/english-spelling-grammar/][Grammar check]]
  • [[https://synonyms.reverso.net/synonym/][Synonyms search]]
  • [[https://conjugator.reverso.net/conjugation-english.html][Conjugation]]
  • Installation The package is available on MELPA. Install it however you normally install packages, e.g. with [[https://github.com/jwiegley/use-package][use-package]] and [[https://github.com/radian-software/straight.el][straight.el]]:

#+begin_src emacs-lisp (use-package reverso :straight t) #+end_src

Or [[https://tony-zorman.com/posts/use-package-vc.html][use-package with vc]], which works on Emacs 30 out-of-the-box. #+begin_src emacs-lisp (use-package reverso :vc (:url "https://github.com/SqrtMinusOne/reverso.el.git")) #+end_src

Alternatively, clone the repository, add it to the =load-path= and =require= the package.

  • Usage There's a single entrypoint for all implemented functions: =M-x reverso=. The UI is implemented using the excellent [[https://github.com/magit/transient/][transient.el]].

** Input Handling All commands handle input as follows:

By default, the input string is empty. If a command is launched with a region selected, use the string of that region. If launched with the prefix argument (=C-u=), use the entire buffer.

Results are displayed in =reverso-result-mode= buffers. When launched within that buffer, the command uses the input string specific to the buffer. If launched with =C-u=, it uses the output string from that buffer (if available).

** Translation Use =M-x reverso t= or =M-x reverso-translate= to invoke the translation transient.

[[./img/translation-transient.png]]

The "Source language" and "Target language" parameters are self-explanatory. Note that not every language is compatible with every other language in the general case. "Swap languages" attempts to swap them.

Enabling "Brief translation output" will display only the translated version of the string in the output buffer.

[[./img/translation-res.png]]

Otherwise, the result buffer may contain the following sections:

  • Source text and Translation
  • Corrected text, if available
  • Context results, if available

Context results typically appear for short strings, as seen in the example from the screenshot.

** Context Use =M-x reverso c= or =M-x reverso-context= to invoke context search (or [[https://en.wikipedia.org/w/index.php?title=Online_bilingual_concordance&redirect=no][bilingual concordances]], essentially a Rosetta stone generator).

The input/output UI resembles that of the translation command.

Interestingly, direct context search often yields different results than the "Context results" section of the translation command. Hence, checking both might provide more comprehensive data.

** Synonyms Use =M-x reverso s= or =M-x reverso-synonyms= to invoke the synonyms search.

[[./img/synonyms-transient.png]]

[[./img/synonyms-res.png]]

If necessary, results are segmented by parts of speech.

Each part of speech section contains up to three subsections:

  • Synonyms
  • Examples
  • Antonyms

** Conjugation Use =M-x reverso o= or =M-x reverso-conjugation= to invoke verb conjugation dialog.

[[./img/conjugation-transient.png]]

[[./img/conjugation-res.png]]

** Grammar check Use =M-x reverso g= or =M-x reverso-grammar= to invoke the grammar check.

[[./img/grammar-transient.png]]

Currently, only English, French, Spanish, and Italian languages are available.

[[./img/grammar-res.png]]

The results may contain the following sections:

  • Source text, highlighting errors with =reverso-error-face=
  • Corrected text
  • Corrections

** Grammar check in buffer It can be convenient to apply the grammar check directly to the current buffer without displaying results in another buffer. Use =M-x reverso b= or =M-x reverso-grammar-buffer= for this.

[[./img/grammar-buffer-transient.png]]

Running =e= there (or =M-x reverso-check-buffer=) utilizes the current buffer as input and highlights any found errors using overlays. If a region is selected, the check is confined to that region.

There are a couple of caveats there. First, the service considers each linebreak as a new line, which is incompatible with [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Filling.html][filling text]], i.e. breaking it into lines of a specified width. The "Remove linebreaks" option (=l=) is a workaround for this.

Secondly, the service usually freaks out with special syntax, for instance, Org Mode links.

The third issue partly follows from the second one, as the service often finds "errors" within hidden parts of Org links. Either skip these errors or execute =M-x org-toggle-link-display= in Org files beforehand.

Lastly (and this applies to all other methods as well), the API usually restricts input size. If the service returns an error, try running the command on a smaller region of the buffer.

[[./img/grammar-buffer-res.png]]

When the cursor is placed on an error, the "Information" section provides details.

"Fix error" (=f= or =M-x reverso-check-fix-at-point=) opens a completion interface with potential fixes. "Ignore error" (=i= or =M-x reverso-check-ignore-error=) simply removes the overlay and moves to the next error.

"Previous error" (=p= or =M-x reverso-check-prev-error=), "Next error" (=n= or =M-x reverso-check-next-error=), "First error" (=P= or =M-x reverso-check-first-error=) and "Last error" (=L= or =M-x reverso-check-last-error=) serve to navigate the error list.

"Clear" (=c= or =M-x reverso-clear=) removes error overlays. If a region is selected, it removes overlays only in that region; otherwise, it removes them from the entire buffer.

** History Enable =reverso-history-mode= to keep history:

#+begin_src emacs-lisp (reverso-history-mode) #+end_src

I haven't implemented persistence yet, but I might in the future.

After enabling the minor mode, =M-x reverso-history= or =M-x reverso h= will display recent commands. =RET= on shows the results of each command.

  • Caveats Before proceeding further, here are some caveats to be aware of.

First, the package uses a reverse-engineered API, so all the typical consequences apply, such as sudden irreparable breakages. Although I've been using it for over a year, so... maybe not.

Second, the limit on input size has been mentioned. The obvious workaround is executing commands on a smaller region.

Third, there have been reports that Reverso dispatches IP bans to particularly enthusiastic users, so be cautious if you're sending lots of automated queries. This is also why I didn't implement running one command for multiple consecutive regions.

Finally, exercise caution with the content sent to the service. Avoid inadvertently sharing confidential information (like passwords) or anything that could be used against you in other ways. While the service claims to be [[https://www.reverso.net/privacy.aspx?lang=EN][GDPR-compliant]], we can't actually check that.

  • Customization Run =M-x customize-group reverso= to view the available parameters. Here are a few.

If you don't need all 17 languages, customize the =reverso-languages= variable to narrow down the list: #+begin_src emacs-lisp (setq reverso-languages '(english german russian)) #+end_src

If the length of =reverso-languages= exceeds =reverso-language-completing-read-threshold=, switching a language in transient buffers will invoke =completing-read= (minibuffer completion). Otherwise, it will simply switch to the next language available.

=reverso-max-display-lines-in-input= controls the maximum number of lines displayed in the input section of a transient buffer.

The available faces:

  • =reverso-highlight-face=
  • =reverso-error-face=
  • =reverso-heading-face=
  • =reverso-keyword-face=
  • =reverso-definition-face= are inherited from the faces of =transient.el= and =basic-faces= to look nice.
  • Elisp API In Emacs Lisp, there are four primary functions that interact with the Reverso API:
  • =reverso--translate=
  • =reverso--get-context=
  • =reverso--get-grammar=
  • =reverso--get-context=

Refer to the docstrings for more detailed information.

Each function is asynchronous, and the results are retrieved via a callback.

As Reverso sometimes modifies its available languages and compatibility matrix, so if you change that, execute =reverso-verify-settings= to check for potential errors.

  • Alternatives and Observations A widely recognized translation service is [[https://translate.google.com/][Google Translate]], so of course, there's an [[https://github.com/atykhonov/google-translate][Emacs client]] for it.

The [[https://github.com/emacs-grammarly][emacs-grammarly]] package series provides the Elisp API for [[https://www.grammarly.com/][Grammarly]] (a grammar checking service) along with multiple frontends. Unlike Reverso, Grammarly has an official API (so you don't risk getting an IP ban), and it allows a much larger input size.

Additionally, Grammarly is less bothered by Org and Markdown syntax, although it struggles with inline code blocks. It seems to do work generally better than Reverso, but it also generates a lot of false positives. For instance, it finds a lot of issues in [[https://www.economist.com/][The Economist]] articles, which, I think, have beautiful English.

Another notable grammar-checking solution is [[https://languagetool.org/][LanguageTool]], which can be [[https://dev.languagetool.org/http-server][run offline]] and used with its [[https://github.com/mhayashi1120/Emacs-langtool][Emacs package]]. This tool offers the advantage of unlimited usage and doesn't transmit your data to a third-party server you can't control. But it still doesn't like markup syntaxes.

Also, I've been pretty happy with [[https://github.com/valentjn/ltex-ls][LTeX LS]], which is a LanguageTool-based language server explicitly designed to support markup formats like Org, Markdown, LaTeX, among others.

The [[https://www.npmjs.com/package/reverso-api][reverso-api]] npm package implements the same commands in JavaScript. It also provided invaluable information for creating this package.