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

text to slug alternative
具備明確字元規則的 Text To 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-worldhello-world
Crème brûléecreme-bruleecreme-brulee
北京 20242024北京-2024
Café — Łódźcafe-lodzcafe-lodz
A & Ba-ba-b

對於沒有 ASCII 分解路徑的書寫系統,這種對比尤其明顯。在 ASCII 模式下,「北京 2024」會完全失去中文字元;只有數字存活,並以連字號連接。在 Unicode 模式下,相同的輸入則會讓兩種文字並存。這個差異在兩種實際情境中很重要。其 CMS、路由器和分析工具皆支援 Unicode URL 的多語系部落格,會受益於 Unicode 模式,因為 slug 保留了語言身分。舊式系統、純 ASCII 檔名或較舊的路由器,則會受益於 ASCII 模式,因為 slug 保證只包含小寫的 a 到 z 與數字。任何已在 Unicode 層級處理字元的人,都可以在特殊字元的碼位檢視工具中看到套用於原始字形上的相同碼位視角。

如何將標題轉換為 slug

  1. 開啟 Text To Slug 並將標題或詞組貼入輸入欄位。可接受最多 100,000 個 UTF-16 碼單位的輸入,足以涵蓋一般部落格文章標題與產品名稱,並留有餘裕。
  2. 選擇分隔符 — 用於標準 URL 路徑的連字號,或用於類程式碼識別碼、錨點名稱或筆記鍵的底線。
  3. 將最大長度設定在 1 到 200 個 Unicode 碼位之間。上限會在轉換後強制執行,以確保預覽結果與下載檔案逐位元組一致。
  4. 如果你的目標系統僅接受小寫的 a 到 z 與數字,請選擇 ASCII 模式;如果支援拉丁文字以外的字元,則選擇 Unicode 模式。
  5. 點擊 Create (建立),接著仔細閱讀精確的預覽結果,確認字元數、分隔符樣式與書寫系統保留情況,再行下載。
  6. 下載 UTF-8 純文字檔案,或將預覽內容複製到你的 CMS slug 欄位中。

一個完整的範例會走過整個處理流程。輸入為「Crème brûlée recipes — 10 easy ideas!」,使用連字號分隔符,最大長度 120 個字元,並採用 ASCII 模式。步驟 1 — NFKD 分解會將「Crème」拆解為 C、r、e、組合用抑音符號、m、e,並將「brûlée」拆解為 b、r、u、l、e、e 加上一個組合用長音符號和一個組合用尖音符號。步 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 不會檢查保留路由、檔案系統名稱、資料庫限制、不區分大小寫的衝突,或任何現有的 URL 清單。它不會加上網域、開頭斜線、檔案副檔名或百分比編碼。輸出結果僅為預覽中顯示的 slug 片段。下載動作會在點擊時建立一個暫時的 UTF-8 Blob URL,並在瀏覽器開始傳輸時撤銷它。編輯任何輸入欄位會清除先前的結果,因此下一次點擊 Create 會從頭產生新的值。唯一性強制執行、百分比編碼與 URL 前綴處理都屬於目的地層級的職責,而非 slug 步驟的範疇。

此替代方案的適用情境與替代選擇

Text To Slug 適用於編輯工作:為部落格文章、產品頁面、錨點、類似 CSS 的識別碼、筆記鍵或檔案名稱起草 slug,且規則需要清楚可見。它也適用於處理多語系內容並希望逐篇文章選擇是否進行轉寫或保留書寫系統的團隊。當目標系統已強制執行 slug 模式時 (例如強制使用 UUID 型識別碼的 CMS),或當工作流程確實需要轉寫時 (例如自動將西里爾文標題渲染為拉丁近似拼寫以利 SEO),它就較不適合。對於重度依賴轉寫的工作流程,配備策劃字典的程式庫或自訂規則集是更誠實的工具,因為 Text To Slug 刻意不內建任何轉寫字典,僅透過 Unicode 屬性來處理每個書寫系統。隱私方面同樣直截了當:正規化、選、預與下載建立皆在本地端執行。貼到欄位中的標題絕不會傳輸到網路服務,且在瀏覽器的 Unicode 實作下,接受的輸入總是會產生確定性的值。

延伸閱讀:如何在 Roblox 中讓角色隱形。