一個完全在瀏覽器中執行的文字轉 slug 替代方案,可以將頁面標題轉換成長度為 1 到 200 個 Unicode 碼位( code point )、全小寫、符合 URL 規範的識別字,過程中僅使用 NFKD 正規化與明確的字元規則,然後讓你將結果下載為 UTF-8 文字檔——完全不需要將標題上傳到任何地方。一個真正替代方案的核心特質在於透明:每一個被保留、移除或替換的字元,都遵循一條你可以預先查看的規則,而不是一套你必須憑空相信的隱藏轉譯字典。Text To Slug 正是以這個理念為核心所打造。它會對輸入執行 Unicode NFKD 正規化,移除組合附加符號( combining marks ),將字母轉為小寫,然後根據你所選擇的模式,將結果篩選為僅保留 ASCII 的 a 到 z 與數字,或是保留所有 Unicode 字母與數字。空格、標點符號、符號,以及任何不允許的字元連續序列,會被合併為單一連字號或底線。開頭與結尾的分隔符會被去除。長度上限會在轉換後以 Unicode 碼位計算,絕不會切斷一個 UTF-16 代理對( surrogate pair )。如果最終沒有任何字元保留,工具會回傳明確的錯誤,而不是回傳一個空白的 slug。所有這些處理都完全在瀏覽器本地端完成——無上傳、無帳號、無任何網路請求。

在 slug 工具世界中「替代方案」的真正含義
大多數公開的 slug 產生器依賴伺服器端的函式庫,例如 Python 的 python-slugify、PHP 的 Str::slug,或是發佈在 npm 上的 JavaScript slugify 套件。這些函式庫內建了轉譯字典,會將非拉丁文字對應到近似的拉丁拼寫:德文字母 ß 對應為 ss、中文字元對應為拼音、斯拉夫字母對應為外觀相似的拉丁字母。對某些網站來說這很實用,但對其他網站而言卻是個問題——因為轉譯規則可能導致 slug 與標題的含義產生差異,而團隊可能從未審視過這些規則。Text To Slug 翻轉了這個契約,完全不做任何語言判斷。在 ASCII 模式下,帶有變音符號的拉丁字母會透過 NFKD 分解為其 ASCII 基本字元加上組合附加符號;接著組合附加符號會被去除,因此 "Crème brûlée" 會透過純粹的 Unicode 正規化變成 "creme brulee",而不是依賴法文專屬的字典項目。任何無法分解為 ASCII 基本字元的字母,會連同其周圍的分隔符序列一併被移除。Unicode 模式則會保留瀏覽器歸類為 Unicode 字母或數字的字元,因此中文、阿拉伯文、斯拉夫文、希臘文及其他文字都能原封不動地保留在 slug 中。
ASCII 模式 vs Unicode 模式:如何抉擇
有兩個設定決定了輸出結果幾乎所有的差異:分隔符的選擇,以及字元保留模式。一個有效的比較方式是將相同的輸入分別餵入兩種模式,觀察哪些字元被保留下來。瀏覽器內的 NFKD 資料——定義於 Unicode Standard Annex #15,並透過 MDN 的 String normalize 參考文件 暴露給 JavaScript——驅動了兩種模式下的分解過程,過程中不會查閱任何轉譯表。
| 輸入 | ASCII 模式輸出 | Unicode 模式輸出 |
|---|---|---|
| Hello World! | hello-world | hello-world |
| Crème brûlée | creme-brulee | creme-brulee |
| 北京 2024 | 2024 | 北京-2024 |
| Café — Łódź | cafe-lodz | cafe-lodz |
| A & B | a-b | a-b |
對於沒有 ASCII 分解路徑的文字來說,這種對比尤其明顯。在 ASCII 模式下,"北京 2024" 會完全失去中文字元,僅保留數字,並以連字號連接。在 Unicode 模式下,相同的輸入會讓兩種文字並存保留。這個差異在兩種實際情境中相當重要。當多語系部落格的 CMS、路由器和分析工具都支援 Unicode URL 時,Unicode 模式較為合適,因為 slug 能保留語言識別性。對於舊版系統、純 ASCII 檔案名稱,或較舊的路由器,ASCII 模式則較為合適,因為 slug 保證只會包含小寫 a 到 z 與數字。任何已經在 Unicode 層級處理字元的人,都可以在我們的特殊字元碼位檢視工具中,看到同一套碼位視角如何套用於原始字形。
如何將標題轉換為 Slug
- 開啟 Text To Slug,並將標題或片語貼入輸入欄位。最多可接受 100,000 個 UTF-16 碼位的輸入,這已足以涵蓋一般的部落格文章標題與產品名稱,並仍有餘裕。
- 選擇分隔符——標準 URL 路徑使用連字號,類程式碼的識別字、錨點名稱或筆記鍵則使用底線。
- 將最大長度設定為 1 到 200 之間的任意 Unicode 碼位數。該上限會在轉換後強制執行,因此預覽結果會與下載檔案內容完全一致。
- 如果你的目標系統僅接受小寫 a 到 z 與數字,請選擇 ASCII 模式;如果系統支援拉丁文字以外的字元,請選擇 Unicode 模式。
- 點擊「建立」,然後仔細閱讀精確的預覽結果,以確認字元數、分隔符樣式與文字保留情況,再進行下載。
- 下載 UTF-8 純文字檔,或將預覽內容複製到你的 CMS slug 欄位中。
以下是一個完整的範例,走過整個處理流程。輸入為 "Crème brûlée recipes — 10 easy ideas!",使用連字號分隔符、最大長度 120 個字元,並採用 ASCII 模式。步驟 1——NFKD 分解會將 "Crème" 拆解為 C、r、e、組合用抑音符號( combining grave mark )、m、e,並將 "brûlée" 拆解為 b、r、u、l、e、e,以及組合用長音符號( combining circumflex )與組合用尖音符號( combining acute mark )。步驟 2——移除組合附加符號後,得到 "Creme brulee recipes — 10 easy ideas!"。步驟 3——小寫化處理會產生相同的小寫形式。步驟 4——ASCII 模式僅保留 a 到 z 與數字 0 到 9,因此破折號、驚嘆號及其周圍的空格會被標記為待移除。步驟 5——每一段不允許的字元序列會合併為單一連字號,開頭與結尾的連字號會被去除。步驟 6——轉換後的字串為 34 個碼位,遠低於 120 個字元的上限,因此不會進行截斷。最終 slug 為:creme-brulee-recipes-10-easy-ideas(34 個碼位)。計算過程非常直接:creme 的 5 個字母,加上 1 個分隔符,加上 brulee 的 6 個字母,加上 1 個分隔符,加上 recipes 的 7 個字母,加上 1 個分隔符,加上 2 個數字,加上 1 個分隔符,加上 easy 的 4 個字母,加上 1 個分隔符,加上 ideas 的 5 個字母,合計 34 個碼位。
Slug 衝突與 Text To Slug 不會跨越的界線
Slug 並不會保留 URL 也不保證唯一性。兩個不同的標題可能會被正規化為相同的值,而截斷處理更會提高衝突風險,因為它會在指定的最大長度處切開轉換後的值,並去除切點處的結尾分隔符,而不會嘗試保留最後一個完整的字。這個最後的選擇是刻意的:保留最後一個字可能會使值超出所宣告的上限,或是讓長度遠低於上限。契約保持在誠實的狀態,代價是在罕見情況下會出現略顯尷尬的邊界。除了唯一性之外,Text To Slug 不會檢查保留路由( reserved routes )、檔案系統名稱、資料庫限制、不分大小寫的衝突,或任何現有的 URL 清單。它不會加上網域、開頭斜線、檔案副檔名,或百分號編碼。輸出結果僅為預覽中顯示的 slug 區段。下載動作會在點擊時建立一個臨時的 UTF-8 Blob URL,並在瀏覽器開始傳輸後立即撤銷。編輯任何輸入欄位都會清除先前的結果,因此下次點擊「建立」時會從頭產生新的值。唯一性強制執行、百分號編碼與 URL 前綴處理,都屬於目的地層級的工作,而非 slug 步驟。
這個替代方案的適用情境與替代選擇
當工作屬於編輯性質時,Text To Slug 相當合適:為部落格文章、產品頁面、錨點、類 CSS 識別字、筆記鍵或檔案名稱草擬 slug,且規則需要一目了然。對於處理多語系內容、並希望能針對每篇文章決定是否進行轉譯或保留原文的團隊來說,它也很合適。但在以下情況則較不適用:目標系統已強制執行特定的 slug 格式——例如強制使用 UUID 識別字的 CMS——或是工作流程確實需要轉譯,例如為了 SEO 自動將斯拉夫文標題渲染為拉丁近似拼寫。對於重度依賴轉譯的工作流程,搭載精選字典的函式庫或自訂規則集會是更誠實的工具,因為 Text To Slug 刻意不搭載任何轉譯字典,僅透過 Unicode 屬性來處理每種文字。在隱私方面也很直截了當:正規化、篩選、預覽與下載建立都在本地端執行。貼入欄位的標題絕不會被傳輸到任何網路服務,且接受的輸入在瀏覽器的 Unicode 實作下永遠會產生確定性的結果。