鐵人賽終於來到最後關頭,
正好遇上三天國慶連假。
放假之前,我心想,太好了!
正好可以趁這三天趕趕進度,
順利的話,說不定還可以儲備幾天的稿子,
這樣就不用每天被自己追著跑了。。。
嗯。我只能說,
這想法實在太天真了呀啊啊啊啊啊~~
這幾天既要當孝順的兒子、和藹的老爸、親愛的老公、
還要當夠義氣的朋友、約了就要到的好咖,
不時身兼小丑、司機、球友、打雜兼撞鐘、技術長兼心理師,
前後場都要兼顧,哇咧非但沒休息到,
反而還比平常更忙呀。
更別說什麼鐵人賽了,
根本一點點時間都擠不出來呀!!
實在沒辦法,
我只好先把預計要談的主題奉上,
姑且先卡個位,等假期結束家人朋友長輩晚輩各自就位,
上班的上班,上學的上學,
我再陸續補上內容吧哈哈哈哈哈。。。
其實越談到後面,內容應該越精彩可期才對。
還剩下幾天的時間,
我希望可以再盡量談些重要的技術做法與發展方向。
比如在 NLP 自然語言處理領域中,
Python 可說是極為重要的語言。
而像 Tensorflow 這麼重要的框架,
也是以 Python 做為主要的程式語言。
Javascript 雖然也有非常廣泛的應用,
但如果想引用 Python 領域早已成熟或最先進的一些發展,
終究還是像隔了一層紗,
經常會有看得見卻摸不著的喟歎。
昨天我們所介紹的 flask,
就是可以把 Python 強大資源引入使用的一個關鍵。
只要透過 flask,就能輕鬆建立 RESTful API,
把 Python 強大的能力提供給 Javascript,
甚至提供給其他各種語言呼叫引用。
昨天我們好不容易透過 flask 的程式碼,
建立了向外部 Google API 取得語法分析結果的能力。
接著就來看看外掛這邊的 Javascript 程式碼,
如何向 flask server 發出請求,
還有取得結果之後,如何加以運用吧 ^_^
首先,我們還是建立一個全域變數 syntax_results,
用來保存各句的語法分析結果。
畢竟到外部進行語法分析,量太大還是有可能需要花錢的,
因此我們會針對查過的句子,把相應的語法分析結果,
保存在 syntax_results 這個變數中。
(而且也會同步保存在 localStorage 內)
syntax_results 這個全域變數,可以用句子內容做為索引,
找出之前所保存的相應語法分析結果。
針對 syntax_results 這個全域變數,究竟需要修改哪些程式碼呢?
我們其實可以循著 saved_terms 的腳步,大體上就不會有所遺漏了。
每次我們在切換翻譯的 switchTranslation() 函式內,
都會嘗試先從 localStorage 裡取出之前保存的語法分析結果。
function switchTranslation() {
...
saved_terms = localStorage.getItem('saved_terms')
saved_terms = saved_terms?JSON.parse(saved_terms):{}
syntax_results = localStorage.getItem('syntax_results')
syntax_results = syntax_results?JSON.parse(syntax_results):{}
...
而在進入翻譯編輯界面的 switchToModification() 函式內,
我們則會把這個全域變數送進 setPanels() 函式,
function switchToModification(node) {
...
setPanels({
...
saved_terms: saved_terms,
syntax_results: syntax_results,
});
...
這樣一來,語法分析的結果就送入面板中了。
我們的 bt_panels 主面板元件收到 syntax_results 之後,
會把它一路往下傳遞,
先交給【句子面板】bt_sent_panel 元件,
再傳給【原文句】orig_sent 元件。
Vue.component('orig_sent', {
props: [
...
'saved_terms',
'syntax_results'
],
...
在【原文句】orig_sent 元件的 compoted 設定裡,
我們會先嘗試從 syntax_results 裡,
取出相應的語法分析結果 syntax_result。
如果找不到的話,
就會利用 sendMessage 機制,
向【背景服務】發出 get_syntax_result 的請求,
並暫時把 syntax_result 設定為空的 null。
computed: {
...
syntax_result: function () {
...
// 如果沒有保存過語法分析結果,就要去外部取回
chrome.runtime.sendMessage({ cmd: 'get_syntax_result', data: {
sentence: this.orig_texts[this.sent_index],
}
});
// 取回語法分析結果需要花點時間,這裡先把值設定為空的 null
return null
...
}
【原文句】 orig_sent 的 template 樣板,
則會把 syntax_result 送入【原文 token】元件,
最後由【原文 token】呈現出不同的顯示效果。
Vue.component('orig_token', {
...
template: `
<div :id="key" class="orig_token"
:class="[is_root]"
v-on:mouseover="activate"
v-on:mouseout="deactivate"
v-on:click="tokenClicked"
v-on:dblclick="dict_search">
<div class="partOfSpeech tag">{{pos}}</div>
<div class="dependencyEdge label">{{role}}</div>
<div class="lemma">{{lemma}}</div>
<div class="ot_text" :title="token_title">{{ot_text}}</div>
<div class="tt_text" :title="token_title">{{tt_text}}</div>
<div class="mt_text">{{mt_text}}</div>
</div>`
順帶一提,
由於我們也把 saved_terms 送入【原文 token】,
因此也就順便趁這個機會,
把各個 token 相應的【機譯】與【人譯】,
全都顯示在每個【原文 token】元件中。
接著來到【背景服務】background.js 的程式碼中,
我們先在 onMessage 裡監聽前面所發出的 get_syntax_result 指令,
然後用 fetch() 向我們的 flask server 發出請求,
請它用 Python 代替 Javascript 向 Google API 取得語法分析結果:
chrome.runtime.onMessage.addListener( (message, sender, sendResponse) => {
...
else if (message.cmd == "get_syntax_result") {
...
fetch('http://localhost/api/syntax', {
...
})
.then(function(response) {
...
// 取得語法分析結果之後,再把語法分析結果送回去
chrome.tabs.sendMessage(sender.tab.id,
{cmd: 'return_syntax_result', data: result}
);
})
}
...
}
我們可以看到,在順利取得語法分析結果之後,
這裡就會再利用 sendMessage 機制,
發出一個 return_syntax_result 指令。
接下來回到 content.js 中,
當 onMessage 收到 return_syntax_result 這個指令時,
就等於我們的網頁,已經取得語法分析的結果了。
它會先更新 syntax_results 這個全域變數,
並同步更新 localStorage 裡的內容,
然後再把新的 syntax_results 重新送入 Vue 實體 panels,
以更新各面板的顯示內容。
...
else if (message.cmd == 'return_syntax_result') {
...
// 更新全域變數 syntax_results
syntax_results[sent_text] = syntax_result
...
// 把 syntax_results 存入 localStorage
localStorage.setItem('syntax_results', JSON.stringify(syntax_results))
...
// 更新 Vue 實體
panels.$data.syntax_results = syntax_results;
panels.$forceUpdate()
...
最後的畫面呈現效果如下:
我們可以看到,為了讓畫面簡潔一點,
這裡只顯示單詞的【人譯】資訊,
其他資訊全都先隱藏了起來。
但如果把滑鼠游標移動到單詞上方,
則會出現該單詞的【詞性】與它在句中所扮演的【角色】。
至此,總算把語法分析結果順利呈現出來了。
還記得嗎?
最初我們之所以要引入語法分析的結果,
主要是希望可以增加一些線索,
協助我們在替換不良翻譯用語時,
可以達到更精準的效果。
現在我們已經順利將語法分析結果導入【句子面板】,
後續只要在點擊單詞時,
把【詞語原型】、【詞性】、【角色】等等這些資訊,
送往【詞語面板】裡的【詞語表單】,
就可以把單詞相關的更多線索保存起來了。
一旦保存了更多的線索,
在進行詞語替換時,自然就能達到更精準的效果了。
這部分的工作並不困難,就留給各位做為練習好了。 ^_^
其實語法分析的結果,還有其他更多的運用方式。
例如在查字典時,
可以針對相應的詞性,做出更精確的查詢。
在原文句的呈現上,也可以根據語法分析結果,
表現出句子裡各個單詞的結構關係,
這樣更有助於譯者判斷一些艱難的句子,
避免做出錯誤的翻譯。
而我們今天之所以能達成這些功能,
全都是因為搭配了 Python 的 flask server 服務。
有了這個 flask server,
能做的事遠遠不止如此。
舉個例子來說,
之前我們的一鍵翻譯功能,
也可以請 flask server 代勞。
而 Python 在 NLP 方面各種最先進的應用方式,
也可以藉由它來導入到我們的應用之中。
這裡的想像空間簡直無限大,
在此就不繼續展開了。
我們比較關心的,倒是另一個比較切身的問題。
雖然我們目前的翻譯工具,
已經具有相當好用的各種功能,
但目前所有的資料,全都保存在瀏覽器的 localStorage 中。
這種保存方式,最大的問題就是容量有限,
實際上只有 5MB 的容量限制。
再者,所保存的資料只能在同網域內使用。
如果我們可以把資料保存到 flask server 中,
無論是容量上的限制,或是跨網域的限制,
全都可以一一打破。
這才是 flask server 更加厲害、
真正可以解決我們切身需要的一大功能呀!
明天我們就來看看,
flask server 如何協助我們跨越種種限制,
讓我們順利保存各種重要的翻譯心血資料囉!