如何给 google-translate.el 增加 翻译文本跟随源文本 的功能?

Org Mode 的 Agenda 里面按 F 有跟随功能,类似 follow-mode. 我想给 google-translate 实现类似的功能。谷歌翻译的网页翻页有这个功能,很方便双语对比。 这里是 org-agenda-follow 的实现

(defun org-agenda-follow-mode ()
  "Toggle follow mode in an agenda buffer."
  (interactive)
  (unless org-agenda-follow-mode
    (setq org-agenda-pre-follow-window-conf
	        (current-window-configuration)))
  (setq org-agenda-follow-mode (not org-agenda-follow-mode))
  (unless org-agenda-follow-mode
    (set-window-configuration org-agenda-pre-follow-window-conf))
  (org-agenda-set-mode-name)
  (org-agenda-do-context-action)        ; (1)
  (message "Follow mode is %s"
	         (if org-agenda-follow-mode "on" "off")))

(defun org-agenda-do-context-action ()
  "Show outline path and, maybe, follow mode window."
  (let ((m (org-get-at-bol 'org-marker)))
    (when (and (markerp m) (marker-buffer m))
      (and org-agenda-follow-mode
	         (if org-agenda-follow-indirect
	             (org-agenda-tree-to-indirect-buffer nil) ; (2)
	           (org-agenda-show)))
      (and org-agenda-show-outline-path
	         (org-with-point-at m (org-display-outline-path t))))))

(defun org-agenda-tree-to-indirect-buffer (arg)
  "Show the subtree corresponding to the current entry in an indirect buffer.
This calls the command `org-tree-to-indirect-buffer' from the original buffer.

With a numerical prefix ARG, go up to this level and then take that tree.
With a negative numeric ARG, go up by this number of levels.

With a `\\[universal-argument]' prefix, make a separate frame for this tree, \
i.e. don't use
the dedicated frame."
  (interactive "P")
  (if current-prefix-arg
      (org-agenda-do-tree-to-indirect-buffer arg)
    (let ((agenda-buffer (buffer-name))
	        (agenda-window (selected-window))
          (indirect-window
	         (and org-last-indirect-buffer
		            (get-buffer-window org-last-indirect-buffer))))
      (save-window-excursion (org-agenda-do-tree-to-indirect-buffer arg))
      (unless (or (eq org-indirect-buffer-display 'new-frame)
		              (eq org-indirect-buffer-display 'dedicated-frame))
	      (unwind-protect
	          (unless (and indirect-window (window-live-p indirect-window))
	            (setq indirect-window (split-window agenda-window)))
	        (and indirect-window (select-window indirect-window))
	        (switch-to-buffer org-last-indirect-buffer :norecord)
	        (fit-window-to-buffer indirect-window)))
      (select-window (get-buffer-window agenda-buffer))
      (setq org-agenda-last-indirect-buffer org-last-indirect-buffer))))

这里是我的实现思路

  • [ ] how to separate translation source text?
    • [ ] use markers
      • [ ] use text-properties as markers
        • [ ] separate text with sentences as unit
          • [ ] how to mark sentence beginning and end?
          • [ ] how to add text-property on sentence unit?
          • [ ] what text-property key-value to assign?
            • [ ] a unique value of key ~'google-translate-sentence~
              • [ ] use number or hash value?
        • [ ] insert text-property markers in source text before translation
        • [ ] how to add corresponding text-property to translated text? (question)
          • [ ] google-translate on sentence by one sentence, then combine results into one?
            • [ ] This is not efficient
          • [ ] check out how google web page translate implement this?
        • [ ] follow-mode follows those text-property markers between source buffer and translated buffer
        • [ ] also highlight follow part text with different highlight background color
        • [ ] add detect markers function to ~post-command-hook~

翻译成中文很麻烦,写Elisp的大多都能看懂这个英文,就不特意再翻译一下了。 :joy: 现在的困难在标记 (question) 的地方

我觉得你应该把问题分解一下

哪个是你能做的,哪个是不能做的

楼主是想按段落对比?那也许可以先翻译之后检查回车符?

如果段落太长,再检查段落中的句号,分成长度合适的几个部分?

不能做的在 标记 (question) 的地方。这个地方思路想不出来,其他的地方我都能写。

我想按照句子为单位。谷歌的网页翻译不是有那个功能么,当鼠标放在某一个句子上的时候,同时高亮当前句子和源文件(也就是网页)的句子。

Emacs不能绑定函数到mouse hover上,但是以你可以用track-mouse

https://www.gnu.org/software/emacs/manual/html_node/elisp/Mouse-Tracking.html

然后用(posn-point (event-end(read-event))得到光标目前的point。

另外高亮应该用overlay,比text property更适合你的使用场景。

我写了个用百度翻译的包,可以逐句翻译,你可以看看,翻译结果显示到posframe 或者 新开一个buffer

大体上是 hook postcommand ,判断当前命令是否是光标移动命令,如果移动了,就用run-with-idle-timer设置一个延迟,执行翻译

这个实现的方法也是很有意思。很不错,像eldoc。赞一个!

可以拿来参考一下,不过我主要是文档翻译,而且翻译的时候如果发送的是大片的文本,翻译出来的结果理论上更准确一点,因为有上下文。

QQ浏览器有个不错的功能是翻译当前整页的文本,然后双屏显示,并且保持着段落对齐。我觉得这个不错,和你的设想也挺一致的

对,其实就是为了适应整个文档的场景,由于文档中以句子或者段落为段落,自动跟随,这样就能知道当前对应的翻译或者源文本是哪里。这样方便交叉看。我再深入想想,Emacs下要怎么实现。

要翻译段落也是可以的把 thing-at-point 的 'sentence 参数 改成 'paragraph 就行了。

是可以的, (字数不够凑)

感觉已经偏离主题了,我是想问问标记 (question) 的那个问题。要怎么实现在源文本中标记句子的同时,在翻译文本中也标记出对应的句子?

我不说了么,用track-mouse。不要求hover的话可以用button,button要点击才能激活,光hover只是高亮

执行翻译之前,先把要翻译的文本做好断句,建议直接翻译一个段落,调用 google api 返回的结果就会以句子为单位,返回一个类似下面的数组 [[“她是个女孩子。\n”,“she is a girl.\n”,null,null,1] ,[“哈哈”,“haha”,null,null,1]

这时候可以用 hash-table ,把原句存为 key, 翻译结果存为 value, 这样子就可以 通过 Thing-at-point 获取位置的句子,再从hash-table找到翻译结果 至于段落,把这些句子 join 起来 存为key, 结果 join 起来 存为 value, 同样 thing-at-point 获取段落,再到hash-table 检查结果

标记可以用 font-lock 把对应的翻译结果高亮

鼠标的方案也不错。毕竟移动的话,鼠标还是蛮方便的。特别适合hover. 不过我还是想要类似 post-command-hook 之类的方案也弄一个。鼠标的这个也要实现。

是个不错的办法。感谢,就用这个办法了。