init: GitHub 中文化 (繁體) userscript v0.15
This commit is contained in:
158
DEVELOPMENT.md
Normal file
158
DEVELOPMENT.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# GitHub 中文化 (繁體) UserScript — 開發紀錄
|
||||
|
||||
一份給 ScriptCat / Tampermonkey 用的 GitHub 介面繁體中文化腳本,檔案在 `github-zh-tw.user.js`。本文件紀錄從 v0.6 一路迭代到 v0.15 的過程、遇到的坑與解法。
|
||||
|
||||
## 設計原則
|
||||
|
||||
1. **只翻 GitHub 介面本身,不動使用者內容。** 檔名、commit 訊息、issue 標題、使用者名稱、topic 標籤、repo 描述、README、程式碼一律保留原樣。
|
||||
2. **不破壞功能。** 搜尋語法(`is:issue state:open`)必須維持英文,否則點擊篩選會失效。
|
||||
3. **SPA 友善。** GitHub 用 Turbo/pushState,切頁不會重載腳本,需要自己監聽。
|
||||
4. **效能優先。** 預編譯 regex、debounce、只處理必要節點。
|
||||
|
||||
## 架構概念
|
||||
|
||||
三層替換:
|
||||
|
||||
```
|
||||
text
|
||||
├─ TIME_AGO_REGEX "3 days ago" → "3 天前"
|
||||
├─ WORD_BOUNDARY_REGEX 詞庫批次替換(長詞優先、大小寫不敏感)
|
||||
└─ ABBREV_REGEX "62.8k" → "6.3 萬"
|
||||
```
|
||||
|
||||
兩層跳過:
|
||||
|
||||
```
|
||||
shouldSkipTextAncestor(el)
|
||||
├─ SKIP_TAGS SCRIPT / STYLE / CODE / PRE / INPUT / TEXTAREA ...
|
||||
├─ isContentEditable / role="textbox"
|
||||
└─ SKIP_CONTEXT_SELECTOR closest() 快速判斷使用者內容脈絡
|
||||
```
|
||||
|
||||
掃描策略:`TreeWalker` 走所有文字節點 + `querySelectorAll(ATTR_SELECTOR)` 補翻屬性。
|
||||
|
||||
## 版本演進
|
||||
|
||||
### v0.6(初版)
|
||||
使用者提供,基本能翻導覽列。用 `setInterval(500ms)` 輪詢 + MutationObserver flag。
|
||||
|
||||
**問題:** 詞庫太少、每個詞跑一次 regex 效能差、沒處理 SPA。
|
||||
|
||||
### v0.7
|
||||
- 擴充詞庫(Settings、Pulls、Commits、Branches…)
|
||||
- 使用預編譯的 `WORD_BOUNDARY_REGEX` 一次替換
|
||||
- 拿掉 setInterval 輪詢,改 MutationObserver + debounce
|
||||
- Hook `pushState` / `replaceState`、監聽 `turbo:load`
|
||||
|
||||
### v0.8
|
||||
**Bug:** `stars` / `watching` / `forks` 小寫沒被翻。
|
||||
**原因:** regex 只有 `g` 沒 `i` 旗標。
|
||||
**修:** 改 `gi`,搭配 `LOWER_MAP` 做大小寫不敏感查表。
|
||||
|
||||
**Bug:** `Security and quality` 變 `安全 and quality`。
|
||||
**原因:** 只有 `Security` 在詞庫,`and quality` 不在。
|
||||
**修:** 加整句 `Security and quality` → `安全與品質`。長詞優先排序會先吃掉整句。
|
||||
|
||||
### v0.9(數字格式化)
|
||||
使用者要求「62.8k」改成更好看的格式。
|
||||
|
||||
新增 `formatAbbrev`:
|
||||
- `≥ 1 萬` → `X.X 萬`(≥100 萬去掉小數)
|
||||
- `< 1 萬` → 千分號 `7,800`
|
||||
- 純數字不動
|
||||
|
||||
**額外問題:** `<strong>62.8k</strong>` 沒被掃到。
|
||||
**原因:** SCAN_SELECTORS 沒包 `<strong>`。
|
||||
**修:** 加入 `strong, b, em, h1-h4, li`。
|
||||
|
||||
### v0.10
|
||||
**Bug:** 按鈕上的 `7.8k` / `62.8k`(在 `<span class="Counter">` 裡)沒格式化。
|
||||
**原因:** `Counter` 在 skip 清單。
|
||||
**修:** 移除 `Counter` skip。純數字本來就不會被誤翻,`k/M` 後綴才會處理。
|
||||
|
||||
### v0.11(分支頁)
|
||||
Branches 頁大量詞彙沒翻:Default、Active、Stale、All、Updated、Check status、Behind、Ahead 等。
|
||||
|
||||
也加了相對時間 regex:
|
||||
```js
|
||||
TIME_AGO_REGEX = /\b(\d+)\s+(second|minute|hour|day|week|month|year)s?\s+ago\b/gi
|
||||
```
|
||||
|
||||
**修 `Active 分支清單` 的半翻:** 把 `Branches` → `分支`(中文無複數差異),加整句 `Active branches` → `活躍分支`。
|
||||
|
||||
### v0.12(TreeWalker 重構)
|
||||
**Bug:** `<th>` / `<td>` / `<relative-time>` 的文字沒翻,`<input>` 的 placeholder 也沒翻。
|
||||
**原因:** SCAN_SELECTORS 白名單太窄;`<input>` 整個被 SKIP_TAGS 跳過,連屬性都沒碰。
|
||||
|
||||
**大改:**
|
||||
1. 拿掉 SCAN_SELECTORS,改用 `TreeWalker` 走所有文字節點
|
||||
2. 屬性翻譯獨立於文字節點跳過邏輯 — `<input>` 文字跳過但 placeholder 仍翻
|
||||
3. `shouldSkipTextAncestor` 改成走 parent chain,比單一元素判斷更穩
|
||||
|
||||
### v0.13
|
||||
補空狀態整句:`No branches match the search` → `沒有分支符合搜尋條件` 等。
|
||||
|
||||
**教訓:** 差點加了 `of` → `/共`,但 `of` 會破壞「Member of X」「out of」等句型,移除。同理 `Page`、`Showing` 也太通用不加。
|
||||
|
||||
### v0.14(使用者內容保護)
|
||||
使用者抱怨檔名、commit 訊息、repo 描述被翻(`授權` / `自述檔.md` / `更新.sh` / `Initial 提交` / `全部 IN ONE Hacking Tool For Hackers`)。
|
||||
|
||||
**新增 `SKIP_CONTEXT_SELECTOR`:**
|
||||
- 檔名:`.react-directory-filename-column`、`a[href*="/blob/"]`、`a[href*="/tree/"]`
|
||||
- Commit:`a[href*="/commit/"]`、`[data-testid="latest-commit-details"]`
|
||||
- Repo 描述:`.BorderGrid-cell > p`、`.f4.my-3`
|
||||
- Topics:`a.topic-tag`
|
||||
- README:`.markdown-body`
|
||||
- 程式碼:`.blob-code-inner`、`.highlight`、`.react-code-lines`
|
||||
- Issue / PR 標題:`.markdown-title`、`.js-issue-title`
|
||||
|
||||
用 `el.closest(SKIP_CONTEXT_SELECTOR)` 一次判斷祖先脈絡。
|
||||
|
||||
### v0.15(Issues 頁)
|
||||
多個新問題:
|
||||
1. `is:issue state:開啟中` — 搜尋語法被翻,**破壞功能**
|
||||
2. `karanraza608-星標` — 使用者名稱被翻
|
||||
3. 下拉選單沒翻(Sort by / Newest / Oldest…)
|
||||
4. `read the 貢獻 guidelines` — 半翻
|
||||
5. Dismiss / Want to contribute 沒翻
|
||||
|
||||
**新增跳過脈絡:**
|
||||
- 使用者:`a[data-hovercard-type="user"|"organization"]`、`a[data-hovercard-url*="/users/"]`
|
||||
- 搜尋:`[role="searchbox"]`、`[role="combobox"]`、語法 token `.pl-c1` 等
|
||||
- Labels:`.IssueLabel`、`.Label`
|
||||
|
||||
**新增詞庫:** `Sort by` / `Order` / `Newest` / `Oldest` / `Created on` / `Last updated` / `Total comments` / `Best match` / `Reactions` / `Recently updated` / `Dismiss` / `Want to contribute to` / 整句 `If you have a bug or an idea, read the contributing guidelines before opening an issue`。
|
||||
|
||||
## 關鍵技術點
|
||||
|
||||
### 大小寫不敏感 + 保留原大小寫?
|
||||
做不到。因為 `Open` (tab) 和 `opened` (issue meta 動詞) 經過 `gi` + LOWER_MAP 會用同一個翻譯。只能二選一。目前選「保留 tab 翻譯」,動詞保留英文。
|
||||
|
||||
### 長詞優先
|
||||
```js
|
||||
const sorted = [...TRANSLATION_MAP.keys()]
|
||||
.sort((a, b) => b.length - a.length)
|
||||
```
|
||||
搭配 regex 的回溯(backtracking),長詞失敗時會嘗試短詞。讓「Active branches」整句優先於「Branches」單字。
|
||||
|
||||
### 屬性裡的 `...` 結尾
|
||||
`Search branches...` 放進詞庫但 `\b...\b` 的尾端 `\b` 不會成立(`.` 是非字元 + 後方結尾也非字元)。解法是詞庫只放 `Search branches`,regex 會在 `\b` 前停下,保留後面的 `...`。
|
||||
|
||||
### React 元件的 key 變動
|
||||
GitHub 的 React class 常含 hash(如 `SearchInput-module__searchInput_f5Xpk`)。不靠這些,改靠穩定的 `role`、`data-testid`、`data-hovercard-*`、`[href*="/blob/"]`。
|
||||
|
||||
## 目前已知限制
|
||||
|
||||
- **大小寫衝突**:同詞不同語境無法分別翻譯。
|
||||
- **使用者自定 label / project 名稱**:會看內容決定,但已用 `.IssueLabel` / `.Label` 跳過彩色 label。
|
||||
- **Issue 標題**:刻意保留英文(屬使用者內容)。
|
||||
- **新 UI 改版**:如果 GitHub 改變 class / data-testid,跳過規則可能失效。
|
||||
|
||||
## 檔案
|
||||
|
||||
- `github-zh-tw.user.js` — 腳本本體
|
||||
- `DEVELOPMENT.md` — 本文件
|
||||
|
||||
## 安裝
|
||||
|
||||
在 ScriptCat / Tampermonkey「新增腳本」,貼上 `github-zh-tw.user.js` 全文。或用「從本地檔案匯入」。
|
||||
Reference in New Issue
Block a user