0%

讀 Paper 懶人器

前言

Paper 通常以英文流通最為廣泛,在閱讀時總是會搭配 google translate 查找不懂的語句或單字,但在複製整個語句或整段原文時,不應該存在的換行也會被複製進來。導致在輸入進 google translate 翻譯之前需要手動去掉換行。剛開始可能會覺得還好,但用到後來會發現這是非常浪費時間與惱人的事。

除此之外,有時候翻譯出來的中文慣用語會是中國大陸的慣用語,而不是台灣的慣用語,例如:server 在台灣會翻為「伺服器」而非「服務器」、macro 在台灣會翻為「巨集」而非「宏」。

因此我開發一個應用程式,為了解決這些困擾之處,其功能為當我們複製某段文章時,可以自動去掉換行,並且進行翻譯、替換某些自訂文字,再使用 Ctrl-V 貼上時,已經是翻譯與替換後的結果!絕對是碩士、博士們讀 Paper 的一個利器。

應用程式畫面展示應用程式畫面展示

成果

環境

  • python 3.8.5
  • pip 21.1.2
  • google-cloud-translate 3.2.0
  • pyperclip 1.8.2

概念流程圖

寫程式之前紙上談兵是一個好習慣,先將流程圖畫下來,思考整個過程是否具有邏輯錯誤,一改再改,最後成為定案。下圖即為此專案的主功能流程圖,我們主要需要做的事情與順序為

  1. 監聽 Copy 事件
  2. 得到剪貼板中的文字
  3. 去掉所有換行字元
  4. 將其翻譯成中文
  5. 翻譯後的文字替換特定文字
  6. 輸出最後文字至剪貼板

主功能流程圖主功能流程圖

各個擊破

監聽 Copy 事件

Python 之中內沒有內建可以監聽 Copy 事件發生的函數,因此只能自己造輪子,我採用的方法是,比對現在剪貼板中的值與上一個狀態的值是否相同,若不同代表 Copy 事件發生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import threading
import pyperclip
import time

class Hook():
def __init__(self):
self.run = True

def copyHook(self, callback):
self.run = True
self.runHook(lambda : self._copyHook(callback))

def _copyHook(self, callback):
# 清除剪貼板
copiedText = ""
pyperclip.copy(copiedText)
while self.run:
# 複製事件是否發生
tmp_value = pyperclip.paste()
if tmp_value != copiedText and tmp_value != '':
# 複製事件發生,對剪貼板中的文字進行處理
copiedText = callback(tmp_value)
# 將處理後的文字輸出至剪貼板
pyperclip.copy(copiedText)
time.sleep(0.1)

def runHook(self, functionHandle):
t = threading.Thread(target=functionHandle)
t.start()

def stop(self):
self.run = False

def copyHookCallback(copiedText):
print(copiedText)
return copiedText

h = Hook()
h.copyHook(copyHookCallback)

執行後,任意複製文字,都會原封不動地輸出在 Console 底下,由此可以確認監聽 Copy 事件功能已經完備。

Callback Function

這裡使用了一個小技巧,可以將一個 Callback Function 當作參數傳給 Hook.copyHook,當 Copy 事件發生時,會呼叫該函數並把剪貼板的文字傳入,在函數內可以對文字做任何想做的事,最後回傳想要輸出在剪貼板的文字。

這裡實際應用該函數來達到我們想要的效果,也就是去除換行、翻譯為中文、替換文字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def cancelCRLF(text):
return text

def translation(text):
return text

def replaceText(text):
return text

def copyHookCallback(copiedText):
# 去除換行
noCRLF = cancelCRLF(copiedText)
# 翻譯成中文
translated = translation(noCRLF)
# 替換特定文字
replaced = replaceText(translated)
return replaced

去除換行

這功能相對簡單,因為只需要將 \r\r\n\n 等去掉即可,剛好 Python 有一個內建的函式 splitlines 可以協助我們去除換行符號

1
2
def cancelCRLF(text):
return ' '.join(text.splitlines())

翻譯為中文

在翻譯為中文這裡可以有兩種做法

  1. 使用 selenium 開啟 Google Translate,將文字貼進左邊的原文窗格內,等待翻譯好之後再從右邊翻譯窗格拿到翻譯後的結果。但這樣的缺點是使用者需要另外下載 selenium driver
  2. 使用 Google Cloud Translate API 服務,以 API 的方式來做整合,較不會有環境問題,因此我採用這種作法。

Google Cloud Translate API

想要使用這項服務,需先申請 GCP (Google Cloud Platform) 帳號,再依照下方步驟來取得 API Key

啟用 Google Cloud Translation API

啟用 Google Cloud Translation API 步驟啟用 Google Cloud Translation API 步驟

啟用之後需要幾個步驟取得 API Key

建立 Credential建立 Credential

填寫表單資訊填寫表單資訊

進入管理金鑰介面進入管理金鑰介面

儲存 API Key儲存 API Key

Translate API 使用

費勁千辛萬苦申請到 API Key 之後就可以馬上整合到應用程式裡啦!其中 apiKeyJson 是 API Key 的絕對路徑

1
2
3
4
5
6
7
from google.cloud import translate_v2 as translate

def translation(text):
translate_client = translate.Client.from_service_account_json(apiKeyJson)
target = 'zh-TW' # 翻譯為中文
translation = translate_client.translate(text, target_language=target)
return translation['translatedText']

替換文字

替換文字很簡單,只需把介面中 Regex Entry 與對應的 Replace With Entry 一一替換即可,replaceMap 儲存了所有對應 [{'regexEntry': tk.Entry(), 'replaceWithEntry': tk.Entry()}, ...]

1
2
3
4
5
6
7
8
def replaceText(text):
for element in replaceMap:
regex = element['regexEntry'].get()
replaceWith = element['replaceWithEntry'].get()
if regex == '':
break
text = re.sub(regex, replaceWith, text)
return text

完成

到目前為止,功能基本上都已經實現,剩下的只有 GUI 呈現的部份,這部分不在此篇文章強調的重點內,可以直接參考 GitHub。最後附上使用影片教大家如何秒變懶人

參考文獻

  1. Trigger an event when clipboard content changes

  2. 利用 Google Translation API 添加即時翻譯功能 讓你的 App 更加升級!

  3. 使用 Google Cloud Translate API

  4. Authenticating as a service account

很高興能在這裡幫助到您,歡迎登入 Liker 為我鼓掌 5 次,或者成為我的讚賞公民,鼓勵我繼續創造優質文章。
以最優質的內容回應您的鼓勵