「Crème Brûlée Café」「Привет мир」「重庆火锅」这样的标题总要变成 URL slug,而真正的选择只有三种:音译成纯 ASCII 字母;保留原文字符,让它们被百分号编码;或者手工写一个别的语言的 slug。本指南比较这三种做法,核对 Google 文档的说法,并用我们的 URL slug 生成器 的真实输出,逐一讲解拉丁字母重音、德语变音、西里尔字母、希腊字母和中文拼音,以及常见 slugify 函数在哪些地方悄悄丢了字母。

处理非 ASCII 标题的三种方式

做法示例输出优点缺点
音译成 ASCIIcreme-brulee-cafe、privet-mir、chong-qing-huo-guo所有浏览器、应用、CMS 和服务器都支持;容易输入罗马化写法要让读者看得懂;声调和部分区别会丢失
保留 Unicodecrème-brûlée-café、москва-гид、重庆火锅与读者阅读和搜索时用的词一致凡是链接被编码的地方都会变成很长的百分号编码;用别的键盘不好输入
手工撰写或翻译chongqing-hot-pot用词完全可控每个页面都要手工处理;容易前后不一致

大多数国际化网站会为每种语言选定一种做法并全站统一。最糟糕的结果是混用:同一个栏目里有的页面是罗马化的,有的不是。

Google 对非英文 URL 怎么说

Google 的 URL 结构指南建议在 URL 中使用受众所用语言的词,「以及适用时音译的词」("and, if applicable, transliterated words")。它的示例里既有德语路径 /lebensmittel/pfefferminz,也有日语路径 /ペパーミント。所以罗马化 URL 和原文字符 URL,Google 都接受。

同一页面还有两条与此相关的技术规则:

  • 链接里的非 ASCII 字符要做百分号编码。 你链接到自己的页面时,ASCII 以外的字符应在 href 属性中进行百分号编码。Google 给的例子是写 gem%C3%BCse,而不是 gemüse。
  • URL 区分大小写。 Google 把 /APPLE 和 /apple 视为不同的 URL,这也是在所有区分大小写的文字里统一使用小写的又一个理由。

百分号编码会把 slug 变成什么样

URL 只能携带有限的 ASCII 字符,其他字符都要按 UTF-8 字节发送,每个字节写成 % 加两位十六进制数。用浏览器自带的 encodeURI 和 encodeURIComponent 函数处理,结果如下:

可读形式编码后
https://example.com/café-москваhttps://example.com/caf%C3%A9-%D0%BC%D0%BE%D1%81%D0%BA%D0%B2%D0%B0
重庆火锅%E9%87%8D%E5%BA%86%E7%81%AB%E9%94%85

每个带重音的拉丁字母或西里尔字母占 2 个字节(编码后 6 个字符),每个汉字占 3 个字节(编码后 9 个字符)。4 个汉字的 slug 编码后就是 36 个字符。如果读者常在会显示编码形式的应用里分享链接,音译更好读。把任意字符串粘贴进 URL 编码/解码工具,就能看到它的字节,也能把链接解码回可读文字。

拉丁字母的重音和特殊字母

大多数带重音的拉丁字母是「基本字母 + 附加符号」,去掉符号是安全的:é → e、ñ → n、ç → c。少数字母没有可去掉的符号,需要显式映射:ß → ss、æ → ae、ø → o、ł → l、đ → d。我们的引擎两种情况都能处理;德语变音选项见下一节。下面是真实输出,旁边是常见的一行正则 text.toLowerCase().replace(/[^a-z0-9]+/g, '-') 的结果:

标题我们的默认简易正则
Crème Brûlée Cafécreme-brulee-cafecr-me-br-l-e-caf-
Straße über Ärger öffnenstrasse-uber-arger-offnenstra-e-ber-rger-ffnen
Æbleskiver på Øresundaebleskiver-pa-oresund-bleskiver-p-resund
Łódź Kraków Gdańsklodz-krakow-gdansk-d-krak-w-gda-sk
Đà Nẵng Phở Bòda-nang-pho-bo-n-ng-ph-b-

简易正则处理纯英文没问题,但标题里只要出现一个重音字母,单词就会被拆散。

德语、丹麦语等语言的惯例

有些语言在无法使用特殊字母时有自己的替代写法。德语传统上把 ä、ö、ü 写成 ae、oe、ue,所以 mueller 比 muller 更接近「Müller」。WordPress 也是这个思路:它的 remove_accents() 函数只在德语区域设置(de_DE、de_CH、de_AT)下应用 ä → ae、ö → oe、ü → ue 和 ß → ss,在丹麦语下应用 å → aa、ø → oe。没有区域设置时,WordPress 把 ß 映射为单个 s。

我们的工具对所有语言使用同一套通用映射,另外提供一个「德语变音」开关。开启后,「Müller Größe Übersicht」会变成 mueller-groesse-uebersicht,而不是 muller-grosse-ubersicht;「Straße über Ärger öffnen」会变成 strasse-ueber-aerger-oeffnen,而不是 strasse-uber-arger-offnen。

在我们的映射里,丹麦语的 å 变成 a,ø 变成 o,例如 aebleskiver-pa-oresund。如果需要丹麦语的 aa、oe 写法,请手工修改这些 slug。

停用词也因语言而异。停用词编辑器提供西班牙语、法语和德语预设,会追加到英文列表上:使用西班牙语预设时,「El Mejor Café de la Ciudad」会变成 mejor-cafe-ciudad,而不是 el-mejor-cafe-de-la-ciudad;法语和德语预设分别得到 meilleures-crepes-paris 和 tipps-garten。

西里尔字母和希腊字母

西里尔字母和希腊字母没有可去除的重音,需要一张完整的字母对照表。我们的引擎为每种文字使用一张固定的、英文读者易读的对照表:

标题Slug
Привет мирprivet-mir
Щука и Ёжshchuka-i-yozh
Юрий Хрущёвyuriy-khrushchyov
Καλημέρα κόσμεkalimera-kosme

这两种文字都有多套罗马化方案,我们用的是一套通用方案,不是某个国家的标准,所以人名拼写可能和护照或地图上的不同。品牌、人名和城市名请手工修改;乌克兰语、塞尔维亚语或保加利亚语文本请抽查几条。

中文:拼音 slug

汉语拼音是中文罗马化的国际标准(ISO 7098)。汉字没有可以去掉的字母,所以普通的 slugify 函数要么直接丢掉汉字,要么需要拼音词典。我们的工具有一个可选的「中文转拼音」设置,只有在文本包含中文时才加载词典:

标题拼音模式关闭拼音保留 Unicode
重庆火锅chong-qing-huo-guo(空)重庆火锅
银行开户指南yin-hang-kai-hu-zhi-nan(空)银行开户指南
绿茶的好处lv-cha-de-hao-chu(空)绿茶的好处
北京 Travel Guide 2026bei-jing-travel-guide-2026travel-guide-2026北京-travel-guide-2026

值得了解的细节:

  • 不带声调。 声调符号不是 ASCII,所以会被去掉。
  • ü 写作 v,例如 lv-cha。这是拼音输入法的惯例,也有网站写成 lu 或 lyu。
  • 多音字按上下文读。 「重」在「重庆」里读 chong,「行」在「银行」里读 hang,因为词典按整词判断。生僻人名仍可能读错。
  • 每个音节一个连字符。 想按词连写(例如 chongqing-hotpot)请手工撰写。
  • 日语和韩语不一样。 日文汉字会按中文读音转换,假名会被去掉:東京 ラーメン 变成 dong-jing。韩文会被去掉。这两种语言请使用「保留 Unicode」或自己写 slug。

不同平台的取舍不同,有些会在没有任何提示的情况下丢掉字母。Django 的 slugify() 先用 NFKD 规范化文本,再丢弃所有非 ASCII 字符。这样能正确去掉重音,但无法分解的字母会彻底消失。下面是 Django 算法和我们的结果对比:

标题Django 默认Django allow_unicode=True我们的默认
Crème Brûlée Cafécreme-brulee-cafecrème-brûlée-cafécreme-brulee-cafe
Straße über Ärger öffnenstrae-uber-arger-offnenstraße-über-ärger-öffnenstrasse-uber-arger-offnen
Æbleskiver på Øresundbleskiver-pa-resundæbleskiver-på-øresundaebleskiver-pa-oresund
Привет мир(空)привет-мирprivet-mir
重庆火锅(空)重庆火锅chong-qing-huo-guo(拼音模式)

WordPress 的 sanitize_title_with_dashes() 文档注明它「不会替换带重音的特殊字符」("does not replace special accented characters"),而是做百分号编码;保存文章时会先运行 remove_accents(),应用上文的区域设置规则,所以结果取决于网站语言。

代码:用 JavaScript 和 Python 音译重音字母

如果你只需要在自己的代码里处理拉丁字母部分,做法是:先映射那几个无法分解的字母,再规范化为 NFKD,然后去掉组合符号。对本指南中所有拉丁字母示例,下面的函数都能得到与我们引擎相同的 slug,例如 strasse-uber-arger-offnen 和 aebleskiver-pa-oresund:

const SPECIAL = { ß: 'ss', æ: 'ae', Æ: 'AE', ø: 'o', Ø: 'O', ł: 'l', Ł: 'L', đ: 'd', Đ: 'D', ı: 'i' };

function asciiSlug(text) {
  return text
    .replace(/[ßæÆøØłŁđĐı]/g, (c) => SPECIAL[c])
    .normalize('NFKD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

asciiSlug('Łódź Kraków Gdańsk'); // 'lodz-krakow-gdansk'
import re
import unicodedata

SPECIAL = str.maketrans({"ß": "ss", "æ": "ae", "Æ": "AE", "ø": "o", "Ø": "O",
                         "ł": "l", "Ł": "L", "đ": "d", "Đ": "D", "ı": "i"})

def ascii_slug(text: str) -> str:
    text = unicodedata.normalize("NFKD", text.translate(SPECIAL))
    text = "".join(c for c in text if not unicodedata.combining(c)).lower()
    return re.sub(r"[^a-z0-9]+", "-", text).strip("-")

ascii_slug("Straße über Ärger öffnen")  # 'strasse-uber-arger-offnen'

它们不处理西里尔字母、希腊字母和中文。这些文字请使用完整的音译表或库,或者直接用我们的工具。我们的 Slugify 正则与代码片段 指南提供了五种语言的完整函数。

注意

常见错误

  • 对国际化标题使用只认 ASCII 的正则。 如上表所示,[^a-z0-9]+ 会把「Crème」变成 cr-me。
  • 悄悄丢字母。 NFKD 加「忽略非 ASCII」会丢掉 ß、æ、ø、ł、đ,还会把西里尔字母和中文标题变成空。请用你发布的每种语言的真实标题测试 slug 函数。
  • 发布空 slug。 我们的工具会标出每个 slug 为空的标题;在你自己的代码里,请加一个兜底方案。
  • 混用方案。 网站做到一半从 muller 换成 mueller,或从 Unicode 换成拼音,会造成 URL 不一致,以后修复还得做重定向。
  • 重复编码。 对已经编码过的 slug 再编码一次,会把 %C3%A9 变成 %25C3%25A9。存储可读的 slug,在生成链接时只编码一次。

多语言 slug 检查清单

[ ] 每个语言栏目只用一种做法:音译、保留 Unicode 或翻译
[ ] 拉丁字母重音已去掉(é -> e),特殊字母已映射(ß -> ss、æ -> ae、ł -> l)
[ ] 德语内容:确定 ä -> a 还是 ä -> ae,并全站统一
[ ] 西里尔字母和希腊字母用同一张表罗马化;人名已手工核对
[ ] 中文:开启拼音模式,并检查输出里有没有读错的名字
[ ] 日语和韩语:保留 Unicode 或手写 slug
[ ] 没有空 slug;没有悄悄丢掉的字母
[ ] href 中的非 ASCII 链接只做一次百分号编码
[ ] 在不改变意思的前提下去掉了该语言的停用词
[ ] slug 全小写,且在所在目录内唯一

在工具里试一试

URL slug 生成器 提供「智能音译」「德语变音」「中文转拼音」和「保留 Unicode」开关,还有西班牙语、法语和德语停用词预设。批量 CSV Slug 生成器 可以把它们应用到整张表格。想让罗马化后的 slug 保持简短,请参考我们的 URL slug 长度最佳实践。如果只想去掉人名或普通文本里的重音、而不生成 slug,请用去除重音符号工具。拼音与汉字网址的详细对比,见 中文网址 slug 指南。

FAQ

常见问题

URL 应该音译还是保留原文字符?

对 Google 来说两种都可以:它建议使用受众语言的词,也接受音译的词。如果链接经常被分享,或需要用别的键盘输入,就音译;如果读者使用这种文字,而且你的平台对 UTF-8 的处理始终一致,就保留原文字符。

URL slug 里的 ü 应该写成 u 还是 ue?

德语内容在无法使用变音字母时,传统写法是 ue,所以 mueller 更接近 Müller。其他语言通常写成 u。全站选定一条规则并坚持使用。

中文标题怎么生成 slug?

在我们的工具里开启「中文转拼音」:重庆火锅会变成 chong-qing-huo-guo,不带声调。你也可以用「保留 Unicode」保留汉字,或者手工写一个英文 slug。

为什么生成的 slug 是空的?

标题里只有函数无法罗马化的字符,例如关闭拼音时的中文、日文假名或韩文。中文请开启拼音,或切换到「保留 Unicode」,也可以手工写 slug。