我有一個朋友很會記帳。不是那種每個月底才對帳單的記帳,是每買一杯飲料就打開 app 輸入的那種。我問她不累嗎,她說:「累的是月底發現錢不見了卻不知道去哪,記帳一點都不累。」
這篇講的東西本質上就是她那個 app,只是記的不是錢,是字元數。Google 的 Cloud Translation 每個計費月前五十萬字元免費,對一個小網站的機器翻譯來說夠用,前提是你有辦法保證整個月不超過。超過就是帳單,而且是那種你不會在第一時間發現的帳單。
google-auth:純 Python,可以離線裝
先講認證這一層。呼叫 Google 的 API 要先拿到存取權杖,這件事聽起來簡單,實際上牽涉到 OAuth、JWT、簽章、過期更新一堆東西,自己寫會寫到懷疑人生。google-auth 是 Google 官方的 Python 認證函式庫,Apache-2.0,支援服務帳戶金鑰、OAuth 和應用程式預設憑證,你給它一個金鑰檔,它給你一個會自動更新的權杖。
它的相依很少而且全是純 Python:cachetools、rsa、pyasn1、pyasn1-modules。這表示在一台沒有 pip 的機器上,可以用 python -m venv --without-pip 建環境,把五個 wheel 檔的 SHA-256 對 PyPI 公布的值核對過再解壓進去,不需要連網安裝。純 Python 這件事在這裡很重要,如果它帶著 C 擴充,離線安裝就會變成一場惡夢。
每日上限怎麼算出來的
我把每日上限設成 15,000 字元。理由是這個算式:15,000 乘以 32 等於 480,000,小於 500,000。
為什麼是 32 而不是 31?計費月最長三十一天,但 Google 的每日配額用哪個時區換日不一定跟你的一樣,一個計費月最多會碰到三十二個配額日。多算一天是保險。這樣即使程式的台帳算錯,Google 端的配額本身也把單月用量壓在免費額度內。這個算式我寫在程式的註解裡,寫了三行解釋那個 32,因為我知道未來的自己一定會看著它想「為什麼不是 31」然後手癢改掉。
上限要兩邊都設:Google 主控台的每日配額,和程式自己的滾動二十四小時上限。調整時有順序,調高先改主控台、確認生效再改程式;調低先改程式、再改主控台。程式的上限若高於配額,超出的請求會被 429 拒絕。這個順序就像調整兩道門的寬度,你要先把外面那道開大,裡面那道才有意義。
429 之後怎麼記帳
每次呼叫前先在台帳裡預留字元數,呼叫成功才確認。被 429 拒絕或傳輸失敗時,預留不退,那段標記為「不確定」,因為你不知道 Google 那邊有沒有算。寧可少用幾百字元,不要台帳比實際少。
這跟我朋友的記帳原則一樣:不確定有沒有花的錢,先當作花了。她說這樣月底只會有驚喜,不會有驚嚇。每個計費月開始時,先拿帳單或主控台的實際用量核對上個月,核對通過才允許新一期的呼叫。沒核對就不跑,這條規則很煩,但它是整套機制裡唯一能抓到「台帳跟現實對不上」的地方。
佔位符要用 HTML 模式送
翻譯的段落裡如果有 {{a1}}…{{/a1}} 這類佔位符,用純文字模式送翻,機器翻譯會把它們搬走或丟掉。它會覺得那串大括號是某種奇怪的標點然後幫你「修好」。
解法是改用 HTML 模式:佔位符換成自訂元素 <x-a1>…</x-a1>,文字先做 HTML 轉義,回來時還原佔位符、解碼實體,剩下任何標記就拒絕那一段。這件事我是實測後才知道的,文件沒有寫得這麼具體。第一次看到翻回來的段落裡少了一個連結佔位符的時候,我還以為是自己的正規表示式寫錯,查了半小時才發現是翻譯服務把它吃了。
小劇場:為什麼不直接付錢
「五十萬字元很快就用完吧?為什麼不乾脆付錢?」
「因為付錢的模式是用多少算多少,而我的程式是無人值守的。一個沒人看的程式加一張用多少算多少的帳單,是所有工程師的惡夢。」
「你可以設預算警示啊。」
「警示是事後通知你已經花了,我要的是事前根本花不到。」
「所以你寧可慢一點。」
「一天一萬五千字元,一個月能翻完一個小網站。慢是慢,但我睡得著。」
歸屬標章
Google 要求顯示機器翻譯結果的頁面標示由 Google 翻譯,並提供官方的標章圖檔。這是使用條款的一部分,不是可選的裝飾。放標章不會讓你的網站變醜,不放會讓你的網站變成違反條款的網站。
我對這套做法的看法
免費額度是給小規模用的,超過就是帳單。一個台帳、一個算式、兩邊的上限、一個月初的核對,這四樣東西加起來不到兩百行程式,換到的是可以整個月不看它。
我朋友的記帳 app 用了五年,她說最大的收穫不是省錢,是「知道」。知道錢去哪了,知道還剩多少,知道下個月會怎樣。我的字元台帳給我的是同一件事:知道。在一個無人值守的系統裡,知道就是安全感。
認證這件事為什麼要一個函式庫
OAuth 2.0 在 2012 年成為標準,它的核心想法是「不要把密碼給第三方,給一個有期限、有範圍的權杖」。服務帳戶是它的一種變體,給程式用的:你有一把私鑰,用它簽一個 JWT,拿去換一個一小時有效的存取權杖,過期再換。這個流程寫起來大概一百行,但裡面有簽章演算法、時間同步、錯誤重試、權杖快取,每一個都有坑。google-auth 把這一百行變成兩行,而且是 Google 自己維護的,API 變了它會跟著變。
機器翻譯本身也有一段歷史。Google 在 2016 年把翻譯引擎從統計式換成神經網路,翻譯品質那年突然跳了一大截,很多人記得那個「翻譯突然變得像人寫的」的時刻。現在的 Cloud Translation 分 Basic 和 Advanced 兩個版本,Advanced 多了詞彙表和自訂模型,品牌名稱這種不該被翻的字可以放進詞彙表。免費額度兩個版本合計算。
常見的三個誤會
第一個誤會是「免費額度用完會自動停」。不會,它會自動開始收費。要停要自己設配額,這就是為什麼主控台那邊的上限一定要設,而且要設在程式上限之上一點點。
第二個誤會是「HTML 模式比較慢或比較貴」。字元數的算法一樣,速度差不多。它只是告訴翻譯引擎「這些標籤不要動」,剛好可以拿來保護佔位符。
第三個誤會是「機器翻譯的頁面不用標示」。Google 的條款要求標示,很多其他翻譯服務也一樣。這不只是條款,也是對讀者的誠實:他們有權知道自己讀的是機器翻的。
一個週末可以試的小練習
開一個 Google Cloud 專案,啟用 Cloud Translation API,建一個服務帳戶並下載金鑰。pip install google-auth google-cloud-translate,用金鑰翻譯一句話,確認能動。然後去 API 的配額頁面,把每日字元上限設成 1,000,一個很小的數字。
接著寫一個二十行的台帳:一個 JSON 檔記今天用了多少,每次翻譯前先加上要翻的字元數,超過 900 就拒絕。跑幾次,讓它撞到自己的上限;再把台帳的上限改成 2,000,讓它去撞 Google 的上限,看 429 回來長什麼樣。這一小時你會親手體驗兩道門的關係,以及為什麼裡面那道要比外面那道窄。做完記得把配額改回去或關掉 API,不要留一個能用的金鑰在硬碟上。
後記
我朋友的記帳 app 有一個功能是「本月剩餘可花」,她說那個數字比銀行餘額有用一百倍,因為餘額是過去,剩餘可花是未來。我的字元台帳也有一個一模一樣的數字。每天早上看一眼,知道今天可以翻多少,知道月底會怎樣。無人值守的系統最需要的,大概就是這種讓人可以不看它、但想看的時候一眼就懂的數字。
留言(0)
還沒有留言。讀完有想法,歡迎留下第一則。