Feat/sandhi expansion#233
Conversation
## Phase A:重構大埔(零行為變更)
- `getDapuSandhiHtml()` → `getSandhiHtml(html, dialectCode)`:
移除三條硬編碼 regex,改為查表驅動
- 新增 `classifyTone(syllable, dialectCode)`:
- NFD 正規化剝離變音符 → 查 `reverse_tone_mapping.json` 回傳調值
- 入聲判定:`/b$|d$/` 或 `/g$/ && !endsWith('ng')`(修正 `-ng` 鼻音尾誤判為入聲)
- 新增 `sandhi_rules.json`,`da`(大埔)三條規則:
- `53 when 31/53/54/21 → 55`(補入後字陰入 54、陽入 21)
- `33 when 113/31/21 → 35`(補入後字陽入 21)
- `113 when 113 → 33`
- CSS class 一般化:`sandhi-高降變/中平變/低升變` → `sandhi-t{調值}`
(大埔:t55 紅、t35 紫、t33 藍;顏色不變,保證像素級回歸)
- `main.js` 載入清單補 `reverse_tone_mapping.json`、`sandhi_rules.json`
- 壓測 25,635 筆大埔拼音:25,625 筆完全吻合,10 筆差異全為舊 regex 幽靈變調已修正
## Phase B:海陸腔
- `sandhi_rules.json` 新增 `ha`:上聲 24→33(無條件)、陰入 5→2(無條件)
- CSS 新增 `sandhi-t2`(綠色;亮:`forestgreen`,暗:`#A5D6A7`)
- `main.js` `getSandhiPronunciation()` 新增 `dialect.includes('海陸')` → `'ha'`
- 主列表渲染解除寫死 `'da'`,動態依腔別傳入 `dialectCode`
## Phase C:四縣腔(含南四縣)
- `sandhi_rules.json` 新增 `si`:陰平 24 後接 24/55/5 → 11
- CSS 新增 `sandhi-t11`(橘色;亮:`darkorange`,暗:`#FFB74D`)
- `main.js` 新增 `dialect.includes('四縣')` → `'si'`
## 拼音 Regex 搜尋
- 搜尋欄新增「拼音 regex 模式」:含 regex 元字元且只由拼音字元組成時,
對 `客語標音_查詢`(數字調字串)做 `RegExp.test()`
- 用途:`.+53 .+53`(相連兩音節)方便測試變調視覺;無效 regex 安靜回空,不爆
## 快取
- `data/data_version.json` → `2026071704`(每次改 JSON 必須 bump,已被坑兩次)
- `index.html` `?v=4.4.5`
AI 分工:
- 可行性評估(5 腔難度排序):Claude Fable 5
- 施工書(引擎架構、鐵則、各腔規則):Opus 4.8
- Phase A 實作:Gemini 3.1 Pro
- Phase A bug 修正(快取未刷新、-ng 誤判、規則補全)、審查:Sonnet 4.6
- Phase B/C 實作:Gemini 3.1 Pro
Co-authored-by: Claude Fable 5 <Fable5@ClaudeCode>
Co-authored-by: Opus 4.8 <Opus4.8@ClaudeCode>
Co-authored-by: Claude Sonnet 4.6 <Sonnet4.6@Antigravity>
Co-authored-by: Gemini 3.1 Pro <Gemini-3-1-Pro@Antigravity>
Signed-off-by: GHSRobert <robertus0617@gmail.com>
## Phase D:詔安腔(zh) - `sandhi_rules.json` 新增 `zh`,六條規則: - `31 when 31 → 55`,`else → 11` - `24 always → 5`(有 -b/-d 止聲韻尾的陰入) - `24v always → 55`(-g 尾失落、純母音結尾的陰入) - `53 always → 33`、`55 always → 33`、`43 always → 3` - `reverse_tone_mapping.json`:詔安 `zh` 補 `"á": "24v"` sentinel, 區分「有 stop 陰入 → 5」與「-g 尾失落陰入 → 55」 - CSS 新增 `sandhi-t5`(藍綠 `teal` / `#4DB6AC`)、`sandhi-t3`(咖啡 `saddlebrown` / `#D7CCC8`) ## Phase E:饒平腔(rh,僅新竹音) - `sandhi_rules.json` 新增 `rh`,四條規則: - `53 when 11/53 → 33`,`else → 11` - `55 when 11/53/2 → 53`,`else → 33` - `24 always → 33`、`2 always → 5` - **【】括號防護**(`inBracket` 狀態機):`dialectCode === 'rh'` 時跳過 `【】` 內的卓蘭/桃園音,不套新竹規則;圓括號 `()()` 照常變調 - CSS 新增 `sandhi-t53`(紫紅 `mediumvioletred` / `#F48FB1`) ## 判調修正:鼻音音節可變調(#4) - `classifyTone()` 重構:捨棄逐字元 `vowel_map` 比對,改用 NFD 拆解 直接提取五個客話調號標記(`́/̀/̌/̂/̄`) + 獨立注音符號(ˊˋˇˆ¯)一律正規化 → 支援 `m̌`/`ňg`/`ǹg` 等鼻音音節 ## 遊戲 regression 修正(Phase D 副作用) - `js/game/game-ui.js`:`formatGamePinyinWithSandhi()` 移除寫死的 `typeof getDapuSandhiHtml` 檢查,改為動態推算 `dialectCode`(da/ha/si/zh/rh) 再呼叫泛用 `getSandhiHtml`;補上饒平 `prefix==='饒'/'平'` 對應 ## 官方例詞 fixture 回歸測試(#1/#2) - 新增 `tests/sandhi_fixture_test.js`:五腔 HakPinPlus 格式例詞 + 預期輸出, 對真實引擎跑 `getSandhiHtml`;全數通過 - `main.js` 末尾補 `module.exports { classifyTone, getSandhiHtml, formatPhoneticForDisplay }`, 讓測試腳本可直接 `require`(瀏覽器端 `typeof module` 守衛不影響) ## 快取 - `data_version.json` → `2026071706`、`index.html ?v=4.4.8` AI 分工: - Phase D/E 實作、遊戲 regression 修正、fixture 測試、鼻音修正:Gemini 3.1 Pro - 全腔總審查(規則逐條對官方文件、判調覆蓋率、鐵則 2 大埔零行為變更):Opus 4.8 Co-authored-by: Gemini 3.1 Pro <Gemini-3-1-Pro@Antigravity> Co-authored-by: Opus 4.8 <Opus4.8@ClaudeCode> Signed-off-by: GHSRobert <robertus0617@gmail.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Review: Sandhi Expansion (#233)Nice piece of work — generalizing the Dapu-only tone sandhi engine ( Code quality / maintainability
Potential bugs / correctness
Search feature (
|
依 PR #233 bot review 裁示(7 項選 5 做): ## #1 去重 getDialectCode 新增 `getDialectCode(dialect)` 函式(`main.js`),單一入口吃三種輸入形狀: - 完整腔名字串(`includes`) - 單一腔字(大/海/四/南/平/安) - 遊戲資料變數前綴(含「饒」) 收掉五處各自為政的 if/else:`getSandhiPronunciation`、主表渲染、 `createComparisonRow`×2、`game-ui.js` 的 `formatGamePinyinWithSandhi`。 (表名解析器 `tableName.startsWith` 用途不同,未動。) ## #6 inBracket 補說明 `getSandhiHtml` 中 `inBracket && dialectCode==='rh'` 上方補三行說明: 饒平限定、【】以外照變、保護卓蘭/桃園音的設計理由。 ## #2 vowel_map 守衛補說明 `classifyTone` 開頭的 `vowel_map` 存在性守衛補說明: 已改走 NFD 判調,`vowel_map` 此處僅作「資料已載入」哨兵用。 ## #5 .replace 非全域補說明 NFD 鏈中各 `.replace` 補說明:呼叫端一律單音節,故不需 `/g`。 ## CI:fixture test 自動跑 新增 `.github/workflows/sandhi-test.yml`: push/PR 動到 `main.js`/`sandhi_rules.json`/`reverse_tone_mapping.json`/ fixture 時觸發,跑 `node tests/sandhi_fixture_test.js`(17/17 綠才過)。 ## 快取 - `main.js?v=4.4.8 → 4.4.9`、`game-ui.js?v=4.6.11 → 4.6.12` - 無 data 異動,`data_version` 不需 bump 引擎邏輯零改動;`getDialectCode` 等價性已在 fixture tests 驗過。 Co-authored-by: Opus 4.8 <Opus4.8@ClaudeCode> Signed-off-by: GHSRobert <robertus0617@gmail.com>
Deploying hakspring with
|
| Latest commit: |
7d47fad
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://c883852c.hakspring.pages.dev |
| Branch Preview URL: | https://feat-sandhi-expansion.hakspring.pages.dev |
Review: Feat/sandhi expansion (#233)Nice piece of work — generalizing the Dapu-only tone-sandhi engine into a data-driven, multi-dialect ( Correctness
Code quality
Performance / Security
Style / conventions
Test coverage
Overall this is a well-structured, well-documented refactor with real test coverage added where there was none — nice work. The |
No description provided.