文字 Slugify(URL)

將文本規範化為 URL 友好的 slug,支援小寫、分隔符、自訂移除停用詞。

參數與輸入

建議使用 - 或 _,長度 1~3 個字元

結果

為什麼需要 Slugify?

🔍 SEO 優化

URL 中的關鍵字有助於搜尋引擎理解頁面內容,提升排名。例如 example.com/blog/how-to-learn-javascript 比 example.com/blog/123 更友善。

👁️ 可讀性與分享

使用者看到 URL 就能知道內容為何,社群媒體分享時更友好,更容易被記住與手動輸入。

💻 系統相容性

避免檔案名/URL 中的特殊字元導致錯誤,跨平台相容(Windows/Linux/Mac),避免編碼問題。

🗄️ 資料庫友好

作為唯一識別符(如使用者名、標籤),避免 SQL 注入風險,便於索引與查詢。

什麼是 Slugify?

Slug 是對文本進行標準化後作為 URL/檔案名/識別符使用的短語串。常見處理包括大小寫統一、移除標點、以分隔符連接單詞。

  • ASCII 優先:盡量移除重音與符號,僅保留字母數字與空格
  • Unicode 兼容:對大多數語言字元進行 NFKD 規範化後處理
  • URL 友好:結果僅包含字母/數字與分隔符,可直接用於路徑

使用情境

部落格文章 URL

《如何學習 JavaScript?》

how-to-learn-javascript

檔案命名

產品需求文件 v2.0.docx

product-requirements-v2-0.docx

資料庫識別符

使用者-張三

user-zhang-san

常見問題

Q: 中文字符會怎麼處理?

A: 預設會移除音標後保留拼音字母。純中文可能變為空字串,建議先手動轉為拼音再進行 slugify,或使用中文拼音轉換工具。

Q: 為什麼我的結果是空的?

A: 可能輸入全為標點符號/空格,或啟用停用詞過濾後無剩餘單詞。請嘗試關閉停用詞選項或調整輸入內容。

Q: 分隔符應該用 - 還是 _?

A: SEO 推薦使用 -(連字號),Google 會將其視為空格;_(底線)會被視為連接符,不利於分詞。檔案名可自由選擇。

Q: Slug 有長度限制嗎?

A: 技術上沒有限制,但建議保持在 50 個字元以內,以利 URL 顯示與 SEO。過長的 slug 可能被搜尋引擎截斷。

最佳實踐

推薦做法

  • 保持簡短(建議 < 50 字元)
  • 避免特殊字符,僅使用字母/數字/分隔符
  • 轉為小寫以避免大小寫敏感問題
  • 移除停用詞以提升語義密度

避免做法

  • 不要包含敏感資訊(如 ID、郵箱、密碼)
  • 不要使用特殊字符(如 @#$%^&*)
  • 不要保留空格或連續分隔符
  • 不要重複使用相同單詞

技術說明

Unicode 正規化:

使用 NFKD 分解 + 移除組合音標 (\p{M}),將 Café 轉為 Cafe。支援大多數拉丁語系字元。

停用詞清單:

基於英文常見詞(a/an/the/and/or/of/to/in/on/for/at/by/with),可自訂擴充。中文停用詞需額外處理。

瀏覽器相容性:

需支援 ES6+ 與 Unicode 正規表示式(\p{...})。現代瀏覽器(Chrome 64+、Firefox 78+、Safari 11.1+)均支援。

如何透過程式語言產生 Slug?

JavaScript

function slugify(text) {
  return text
    .toLowerCase()
    .normalize("NFKD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/[^\w\s-]/g, "")
    .trim()
    .replace(/[\s_-]+/g, "-")
    .replace(/^-+|-+$/g, "");
}

PHP

function slugify($text) {
  $text = mb_strtolower($text);
  $text = iconv("UTF-8", "ASCII//TRANSLIT", $text);
  $text = preg_replace("/[^\w\s-]/", "", $text);
  $text = preg_replace("/[\s_-]+/", "-", $text);
  return trim($text, "-");
}

Python

import re
import unicodedata

def slugify(text):
    text = text.lower()
    text = unicodedata.normalize("NFKD", text)
    text = text.encode("ascii", "ignore").decode("ascii")
    text = re.sub(r"[^\w\s-]", "", text)
    text = re.sub(r"[\s_-]+", "-", text)
    return text.strip("-")

Go

import (
    "regexp"
    "strings"
    "golang.org/x/text/unicode/norm"
)

func Slugify(text string) string {
    text = strings.ToLower(text)
    text = norm.NFKD.String(text)
    re := regexp.MustCompile(`[^\w\s-]`)
    text = re.ReplaceAllString(text, "")
    re = regexp.MustCompile(`[\s_-]+`)
    text = re.ReplaceAllString(text, "-")
    return strings.Trim(text, "-")
}

Ruby

require "unicode"

def slugify(text)
  text = text.downcase
  text = Unicode.nfkd(text).gsub(/[^\x00-\x7F]/, "")
  text = text.gsub(/[^\w\s-]/, "")
  text = text.gsub(/[\s_-]+/, "-")
  text.strip.gsub(/^-+|-+$/, "")
end

Java

import java.text.Normalizer;

public static String slugify(String text) {
    text = text.toLowerCase();
    text = Normalizer.normalize(text, Normalizer.Form.NFKD);
    text = text.replaceAll("[^\\w\\s-]", "");
    text = text.replaceAll("[\\s_-]+", "-");
    return text.replaceAll("^-+|-+$", "");
}