Emacs provides [a number of *hook* variables](https://www.gnu.org/software/emacs/manual/html_node/elisp/Standard-Hooks.html#Standard-Hooks), each of which contains a list of functions that are executed when a specific event happens. But there isn't a hook that indicates that the selection has changed. The selection, called “region” in Emacs terminology, is the part of the buffer between the [*point*](https://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html#Point) (the cursor) and [the *mark*](https://www.gnu.org/software/emacs/manual/html_node/elisp/The-Mark.html#The-Mark), and the selection is highlighted when the mark is *active*. There are hooks for mark (de)activation, but [there's no hook triggered when the point (or the mark changes)](https://emacs.stackexchange.com/questions/20596/is-there-a-hook-i-can-use-to-run-my-function-when-point-moves). Furthermore the selection can change because the text changes — in Emacs this can happen even when the selection is active. So if you want to detect all selection changes, you have to add your code to [`post-command-hook`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Command-Overview.html#index-post_002dcommand_002dhook-1447). (add-hook 'post-command-hook 'auto-translate-region-maybe) If running your code after each command is too expensive, you could run it on a delay instead, by setting an [idle timer](https://www.gnu.org/software/emacs/manual/html_node/elisp/Idle-Timers.html#Idle-Timers). (run-with-idle-timer 1 nil 'auto-translate-region-maybe) The reason I called the function “maybe” is that you probably want to run it in certain buffers, but `post-command-hook` and timers are global. Detecting which buffer may need a translation is not straightforward since the active buffer may have just changed, especially with the timer method. Doing this correctly and efficiently turns out to be pretty complicated. Unfortunately I didn't find a package that monitors region changes and provides a proper `region-changed-hook`. In my answer I'll keep things (relatively) simple and show how to display a translation only for the active window's buffer. Translation is pretty expensive so you should avoid running it again with the same arguments. Remember the last string you sent and don't update the translation result if that hasn't changed. (defvar 'auto-translate-region-mode nil "Automatically translate the selection.") (make-variable-buffer-local 'auto-translate-region-mode) (defvar 'auto-translate--last-text nil) (make-variable-buffer-local 'auto-translate--last-text) (defvar 'auto-translate--last-translation nil) (make-variable-buffer-local 'auto-translate--last-translation) (defun auto-translate-call-engine (text) "Translate TEXT and return the result." TODO) (defun auto-translate-region-redisplay () "Redisplay the last cached region translation." ;; Display auto-translate--last-translation somewhere TODO) (defun auto-translate-region-now () "Display the region's translation." ;; Get the selected text and normalize it a bit (let* ((raw-text (buffer-substring-no-properties (region-beginning) (region-end))) (text (replace-regexp-in-string "\\`[^[:alpha:]]+" "" (replace-regexp-in-string "[^[:alpha:]]+\\'" "" raw-text)))) (unless (string= text auto-translate--last-text) ;; The selected text has changed, so update the translation (setq auto-translate--last-translation (auto-translate-call-engine text) auto-translate--last-text text)) ;; (Re)display the translated text (auto-translate-region-redisplay))) (auto-translate-region-maybe () "Display the region's translation if automatic region translation is active and the mark is active." (if (and auto-translate-region-mode mark-active) (auto-translate-region-translate)))