返回作品列表
開源套件 · dotnet tool NuGet 已上架

Cornhsu.PolyMigrate

i18n-first 舊站搬遷工具 · dotnet tool

實習時把佛寺的老 PHP 官網整站搬成靜態站,發現最痛的不是抓網頁——是手工對配每一頁的中英文版本,而且市面上沒有任何工具解這件事。PolyMigrate 把整趟搬遷產品化:自動配對配得起來的、用共用相簿和日期猜配不到的、配不到的誠實列清單——加上十幾個真實踩過的坑,全部內建成功能。

類型
.NET 全域工具+核心函式庫(雙套件)
版本
v1.0.0-preview.1 · MIT
發佈
NuGet · OIDC Trusted Publishing
測試
119 條 · 三平台 CI
真實驗證
516 頁 · 4.6GB · 全站巡檢 0 錯誤
語言支援
任意語言數(lang_map)· 輸出 BCP-47
Terminal — Cornhsu.PolyMigrate
$ dotnet tool install -g Cornhsu.PolyMigrate --prerelease

$ polymigrate extract site.yaml    # 鏡像 HTML → frontmatter Markdown + 四份清單 + 雙語配對
$ polymigrate verify out/          # 全站巡檢:連結/媒體/欄位,exit code 直接進 CI
$ polymigrate thumbs site.yaml     # EXIF 自動轉正縮圖(手機直拍不再側躺)
$ polymigrate probe-orphans site.yaml --section news --years 2021-2023
                                   # 挖回被原站移除索引、但其實還在的孤兒文章
專案概覽

唯一會自動配對多語言頁面的搬遷工具

多語言機構站(政府、大學、NGO、宗教組織)要把老舊動態網站搬成靜態站時,都卡在同一步:手工對配每一頁的各語言版本。通用爬蟲眼裡 /ch/news/x/en/news/x 就是兩個不相干的網頁——這個對應關係搬丟了,新網站的「切換語言」按鈕就是壞的。目前全世界的做法是 Excel 人工對表。

PolyMigrate 把多語言當核心而不是外掛:檔名對稱的頁面以去語言前綴的路徑自動配對;配不起來的(活動頁檔名中英各取各的)依序用共用相簿 → slug 日期正規化 → 標題相似度給啟發式建議、附上證據欄待人工覆核;真的配不到的誠實列入缺漏清單。語言不限兩種——lang_map 宣告幾組就支援幾語,輸出一律 BCP-47 標準代碼。

配不到就誠實說配不到——錯配比漏配更傷信任。

另一半的價值是「內容保存的正確性」:手機直拍照片 EXIF 顛倒、YAML 被冒號和前導零咬壞、影片被 Markdown 轉換器丟掉、舊文章被原站從目錄移除但其實還在——這些通用工具和 AI 抽取都不在乎的細節,每一個都是真實踩過、修過、寫成測試的內建功能。

成果一覽

v1.0.0-preview.1,一次真實整站搬遷背書

發佈NuGet 雙套件(CLI tool+Core lib),推 tag 即 OIDC Trusted Publishing 自動上架(repo 零長效金鑰)
測試119 條(單元/整合/golden-file),三平台 CI(Windows/Linux/macOS)
真實驗證516 頁、4.6GB 媒體全量跑通;281 個 translation key,231 篇雙語文章自動配對;內建巡檢 1,269 內部連結+4,116 媒體引用=0 錯誤
移植即驗證以已完成搬遷的 Python 原型輸出當 golden 基準逐頁比對:466/516 頁正文空白正規化後逐字相同,其餘逐一查核為渲染等價或更保真
語言支援不限雙語:lang_map 宣告幾組就幾語,清單欄位/配對/frontmatter 全部隨語言數展開
效能媒體雜湊以(大小, mtime)快取——4.6GB 實站重跑 30.1s → 4.6s
產出即可部署redirect_map 自動填新路徑,另出 nginx conf 與 Netlify _redirects——301 設定從手填半天變複製一個檔
技術亮點

六個值得一提的設計

1

雙語配對引擎(市面沒有的那一塊)

對稱路徑去語言前綴當 translation_key 自動配;配不起來的依 config 順序用共用相簿 → slug 日期正規化(YYYYMMDD/MMDDYYYY/DDMMYYYY 混用都認)→ 標題 bigram 相似度給建議,附證據欄待人工覆核。真實站跑出 0 筆建議,查證後確認是對的——中英活動頁連海報都是不同圖,寧可不建議也不給錯誤建議

2

「內容保存的正確性」內建成功能

EXIF 轉正後才縮圖、數字 slug 強制引號(PyYAML/js-yaml 對前導 0 判斷不一致)、%20 磁碟解碼名/URL 單次編碼、影片/iframe/PDF 以佔位符穿過 Markdown 轉換保留原位、<title> 髒污時取內文真標題——LLM 抽取和通用爬蟲不在乎的細節,全部有測試背書。

3

孤兒文章探測

原站把舊文從目錄移除但頁面還在——逐日產生兩種日期格式的候選網址、命中再探 A–D 後綴變體、409 bot 防護退避重試、config 宣告 cookie 繞法、禮貌間隔。真實站挖回 13 篇被藏起來的文章

4

巡檢做成產品

verify 只讀輸出、不碰網路(Phase 契約完整性的試金石):frontmatter 欄位、內部連結對路由集、媒體引用對磁碟;已記錄的原站壞圖降 warning 不誤報。「0 錯誤成績單」是行銷主軸,驗證器就該是產品的一部分。

5

決定性輸出與跨平台一致

同 config 兩平台產出必須相同——Windows 保留裝置名(con.md)、大小寫碰撞、尾點空白在任何平台都一致地拒寫並記錄,而不是 Windows 炸、Linux 默默通過;排序穩定、輸出可 diff。

6

301 不打破頁

redirect 的新路徑與內文連結改寫共用同一套路由函式——兩者若各寫一份,遲早不同步,301 就打到 404。同一條規則、一個出口,nginx 與 Netlify 格式直接匯出。

工程方法

評估規畫 → 原型當規格 → 真實資料回歸 → 才發佈

規畫書

先誠實評估,再工程審查

市場空位查證(主要對手 Firecrawl 的縫:付費雲端、不吃 per-site config、不做雙語配對)、最大風險點名(「用 .NET 寫的」不是賣點,i18n-first 才是);再加一輪工程審查把「不先定會返工」的項目分級——編碼、Phase 輸出契約、BCP-47、跨平台路徑、測試策略,A 級全部在骨架期落地。

原型即規格

已完成的搬遷當考卷

不是重新發明——把實習時跑通過的 Python 搬遷腳本逐函式移植,每一步用 516 頁真實資料對答案;發現差異就查核到底是 bug 還是改進(空白正規化、跳脫方式、原型丟資料三類),全部記錄在案。

活文件

fixture 站=功能的活文件

合成離線雙語小站,每修一個坑就加一頁重現它(壞圖、不對稱檔名、日期混用、%20、輪播空清單…),golden-file 測試逐 byte 比對;4.6GB 真實資料留本機迴歸,CI 只跑 fixture。

CI 回饋

CI 抓到的坑回饋成防護

上 GitHub 首跑,Windows runner 就翻出「CRLF checkout 改變 raw string literal 內容」的本機/CI 行為分歧——當天修測試+.gitattributes 全 repo 釘 LF 根治,並把坑寫進團隊記憶。

不做清單

「不做」也明文記錄

不硬配(錯配比漏配傷信任)、Abot 不用(半棄坑,自寫禮貌爬蟲)、ImageSharp 棄用(4.x 起連 build 都要授權金鑰——規畫書預警的風險當場應驗,換 Magick.NET 並把決議寫回規畫書)。

真實驗證

495+ 頁的整站搬遷,先做完才做工具

PolyMigrate 是一次已完成搬遷的產品化:佛光山香雲寺官網(中英雙語、老 PHP 站)——516 頁鏡像、4.6GB 媒體、231 篇雙語文章、13 篇孤兒文章挖回、141 張 EXIF 顛倒照片轉正。移植成工具後用同一批資料逐頁對答案,最後由內建巡檢驗收:1,269 個內部連結、4,116 個媒體引用,0 錯誤

設計取捨

取了什麼、捨了什麼、為什麼

決定理由
雙語做核心,非外掛定位最鋒利(the i18n-first migrator)單語使用者覺得被綁單語=只有一組 lang_map 的特例,架構上化解
config 驅動,一站一份搬得深、搬得對Firecrawl 式「貼網址即用」認真搬一個站 ≠ 隨手抓一頁;站別知識該顯式存檔
離線、可重跑、結果固定資料不出機器,重跑可 diff 驗證雲端 API 的便利政府/學校資料敏感又沒預算,三件事都是硬需求
啟發式只建議,不自動合併清單可信,人保有最終決定權全自動的爽感配錯語言版本比漏配嚴重得多
frontmatter 輸出 BCP-47下游 Astro/Hugo i18n 直接對上與原站 URL 前綴的直覺一致URL 前綴是來源站的實作細節,不該外流到輸出
套件 ID 用 Cornhsu. 前綴verified prefix 藍勾勾、作品集一致裸名 PolyMigrate 的品牌感指令名仍是 polymigrate,信任訊號對新工具更重要
技術棧總覽

從管線到上架的具體組成

套件Cornhsu.PolyMigrate(dotnet tool,指令 polymigrate)+ Cornhsu.PolyMigrate.Core(抽取/配對/巡檢 lib) · MIT
管線四階段:診斷 → 鏡像 → 結構化抽取 → 匯出;Phase 之間以版本化檔案契約銜接,可只重跑一段不重爬
抽取AngleSharp(CSS selector 抽正文) · ReverseMarkdown(+markdownify 等效清理) · YamlDotNet(frontmatter 強制引號跳脫)
配對對稱 translation_key 自動配 · 啟發式(共用相簿/slug 日期正規化/標題 bigram)附證據 · 任意語言數,輸出 BCP-47
影像Magick.NET(Apache 2.0):EXIF AutoOrient → Lanczos 縮圖 · 增量快取可重跑
品質119 條測試 · 離線 fixture 站(每坑一頁)+golden-file 逐 byte 比對 · 三平台 CI · 跨平台路徑安全
發佈OIDC Trusted Publishing(無長效金鑰) · 推 tag 即自動測試、打包、上架 NuGet