;; -*- lexical-binding: t; -*- ;; ;; Translate sentence/region using language translator. ;; Copyright (c) 2002-2019, Hiroyuki Ohsaki. ;; All rights reserved. ;; ;; $Id: pytrans.el,v 1.12 2024/04/19 13:01:02 ohsaki Exp $ ;; ;; This code is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY. No author or distributor ;; accepts responsibility to anyone for the consequences of using it ;; or for whether it serves any particular purpose or works at all, ;; unless he says so in writing. Refer to the GNU Emacs General Public ;; License for full details. ;; Everyone is granted permission to copy, modify and redistribute ;; this code, but only under the conditions described in the ;; GNU Emacs General Public License. A copy of this license is ;; supposed to have been given to you along with GNU Emacs so you ;; can know your rights and responsibilities. It should be in a ;; file named COPYING. Among other things, the copyright notice ;; and this notice must be preserved on all copies. ;; usage: ;; Put pytrans program (available on request) in your path, and add the ;; following lines to your ~/.emacs. ;; (autoload 'pytrans-translate "pytrans" nil t) ;; (global-set-key "\C-cT" 'pytrans-translate) (defvar pytrans-mode-map (make-sparse-keymap)) (define-key pytrans-mode-map "\C-m" 'pytrans-insert-current-sentence) (defvar-local pytrans-window-config nil) (defvar-local pytrans-source-buffer nil) (defvar-local pytrans-last-position nil) (defvar pytrans-font-lock-keywords '( ;; LaTeX commands ("\\$.+?\\$" . font-lock-string-face) ("\\\\\\(ref\\|cite\\){\\(.+?\\)}" (1 font-lock-keyword-face) (2 font-lock-variable-name-face)) ("%.*" . font-lock-comment-face) ;; translation engine ("^[A-Z][A-Za-z]+:$" . font-lock-function-name-face))) (defun pytrans-mode () "A major mode for browing the output from pytrans program." (interactive) (kill-all-local-variables) (setq major-mode 'pytrans-mode) (setq mode-name "Pytrans") (use-local-map pytrans-mode-map) ;; local variables (make-local-variable 'pytrans-window-config) (make-local-variable 'pytrans-source-buffer) (make-local-variable 'pytrans-last-position) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '(pytrans-font-lock-keywords nil nil)) ;; (visual-line-mode 1) (flyspell-mode 1) (run-hooks 'pytrans-mode-hook)) (defun pytrans-translate-string (str) "Return a translated version of STR using pytrans program." (let ((cmd "pytrans -d")) (with-temp-buffer (insert str) (shell-command-on-region (point-min) (point-max) cmd t t) (buffer-string)))) (defun pytrans-replace-region (start end text &optional note) "The region between START and END is commented out, and the text TEXT is inserted afterwards." (interactive) (save-restriction (narrow-to-region start end) ;; insert newline at the end if necessary (goto-char (point-max)) (unless (looking-back "\n" nil) (insert "\n")) ;; leave the note at the end if specified (when note (goto-char (point-max)) (skip-chars-backward "\n") (insert note)) ;; FIXME: should not assume LaTeX-style comment (let ((comment-start "% ") (comment-padding " ")) (comment-region (point-min) (point-max))) (goto-char (point-max)) (insert text))) (defun pytrans-translate-region (start end &optional do-insert) "Translate the region between START and END. If DO-INSERT is non-nil, the region is replaced with the translation. Otherwise, the translation is displayed in other window." (interactive "r\nP") (let* ((str (buffer-substring start end)) (out (pytrans-translate-string str))) (if do-insert (pytrans-replace-region start end out) ;; otherwirse, display in other window (let* ((conf (current-window-configuration)) (srcbuf (current-buffer)) (buf (get-buffer-create "*Pytrans Translation*"))) (with-current-buffer buf (pytrans-mode) (setq pytrans-window-config conf) (setq pytrans-source-buffer srcbuf) (setq pytrans-last-position (cons start end)) ;; insert the output (erase-buffer) (insert out) (goto-char (point-min)) ;; display in the other window (delete-other-windows) (split-window) (set-window-buffer (next-window) buf)))))) (defun pytrans-current-sentence-position () "Return the positions of the begining and the end of the sentence under the current point as a cons cell." (let ((beg (save-excursion (if (re-search-backward "\\(\n\n\\|。\\|\\. *\n\\|\\end{.+?}\\)" nil t) (progn (goto-char (match-end 0)) ;; skip comments (while (looking-at " *%") (forward-line 1)) (point)) (point-min)))) (end (save-excursion (if (re-search-forward "\\(\n\n\\|。\\|\\. *\n\\)" nil t) (progn (if (looking-at "[ \t]*\n") (forward-line 1)) (point)) (point-max))))) (cons beg end))) (defun pytrans-translate (&optional do-insert) "Translate the sentence at the current point via the pytrans program." (interactive "P") (let ((pos (pytrans-current-sentence-position))) (pytrans-translate-region (car pos) (cdr pos) do-insert))) (defun pytrans-insert-current-sentence () (interactive) (let* ((beg (car pytrans-last-position)) (end (cdr pytrans-last-position)) (engine nil) (str (buffer-substring (save-excursion (end-of-line) (if (re-search-backward "^\\([A-Za-z]+\\):$" nil t) (progn (setq engine (match-string 1)) (forward-line 1) (point)) (line-beginning-position))) (save-excursion (if (re-search-forward "\n\n" nil t) (progn (backward-char 1) (point)) (line-end-position))))) (m (make-marker))) (with-current-buffer pytrans-source-buffer (pytrans-replace-region beg end str (format " [%s]" engine)) (set-marker m (point))) (set-window-configuration pytrans-window-config) (goto-char m)))