前言
分享链接的时候突然发现,Quartz 默认的路径中文编码实在太长了:

现在的结构大概是这样:url/文件夹/笔记,但是中文路径在浏览器地址栏会被进行 URL 编码(Percent-encoding),原本三个字的标题瞬间变成一串几十个字符
解决思路
将文件夹和笔记名称本身都改为英文,使用 Quartz 支持的title参数将名称变回中文
为了不影响我自己在 Obsidian 客户端上的编辑,最终选择的的解决方法是:
在 CF Pages 部署的时候运行一个python脚本,遍历项目的 content 文件夹,将所有中文命名的文件重命名唯一的为 6 位哈希,并将原来的中文标题放到 YAML 的title和aliases参数里
如果不添加
aliases参数,Quartz 的双链会断
因为 Quartz 的链接解析器(Link Resolver) 是基于文件系统路径工作的。当你通过脚本把文件重命名为df0251.md后,Quartz 的索引中就不再存在“中文标题”这个路径,因此双链[[中文标题]]就会因为找不到匹配的文件名而指向一个不存在的页面(404)
1. 文件夹
文件夹直接改名,在目录中加个index.md写上中文标题即可,顺便关闭不知道为什么会出现的 giscus 评论区
---
title: 博客相关
comments: "false"
---2. 笔记
为什么叫这个名:因为本来听AI说能直接加一个
slug参数就能解决,沿用了最开始的名字
在英语语法中,后缀-ify的意思是**“使……化”或“变成……”**(类似中文里的“自动化”、“标准化”)
import os
import hashlib
import frontmatter # pip install python-frontmatter
CONTENT_DIR = "content"
EXCLUDE_FILES = ["index.md", "changelog.md", "License.md", "friendlink.md", "aboutme.md"]
def get_hash(text):
return hashlib.md5(text.encode()).hexdigest()[:6]
def safe_rename():
for root, dirs, files in os.walk(CONTENT_DIR):
for file in files:
# 排除非 md 文件和排除列表中的文件
if file.endswith(".md") and file not in EXCLUDE_FILES:
old_path = os.path.join(root, file)
original_title = os.path.splitext(file)[0]
# 如果文件名已经是 6 位哈希,说明已经处理过,跳过
if len(original_title) == 6 and all(c in '0123456789abcdef' for c in original_title):
continue
try:
# 加载笔记内容和元数据
post = frontmatter.load(old_path)
# 1. 确保原始中文名作为 title 保留,解决网页显示问题
if 'title' not in post.metadata:
post.metadata['title'] = original_title
# 2. 核心:将原始中文名加入 aliases,修复 Quartz 双链 404
current_aliases = post.metadata.get('aliases', [])
# 统一转为列表处理
if isinstance(current_aliases, str):
current_aliases = [current_aliases]
elif not isinstance(current_aliases, list):
current_aliases = []
if original_title not in current_aliases:
current_aliases.append(original_title)
post.metadata['aliases'] = current_aliases
# 3. 生成新文件名(保持在原目录)
new_filename = get_hash(original_title) + ".md"
new_path = os.path.join(root, new_filename)
# 碰撞处理
if os.path.exists(new_path) and old_path != new_path:
new_filename = get_hash(original_title + "_alt") + ".md"
new_path = os.path.join(root, new_filename)
# 4. 写回更新后的 YAML
with open(old_path, 'w', encoding='utf-8') as f:
f.write(frontmatter.dumps(post))
# 5. 执行原地重命名
os.rename(old_path, new_path)
print(f"✅ 处理成功: {original_title} -> {new_filename} (已注入别名)")
except Exception as e:
print(f"❌ 处理 {file} 失败: {e}")
if __name__ == "__main__":
safe_rename()配置
-
将
slugify.py放到项目根目录 -
由于引入了第三方库
python-frontmatter,你需要告诉 CF Pages 在构建前安装它。使用 requirements.txt
-
在仓库根目录新建
requirements.txt,内容写:python-frontmatter -
CF Pages 会检测到该文件并自动安装。你的构建命令只需改为:
bash gen-changelog.sh && python3 slugify.py && npx quartz build
-
关于bash脚本是哪来的:实现 Changelog 的自动化更新
AI说安装第三方库会损失一点部署速度,实测下来就五秒左右,完全可以忽略

Gemini 回答
Q:为什么不用 Quartz 自带的 frontmatter 的permalink参数
Gemini:
阅读了你提供的 frontmatter.ts 源码后,我终于发现了为什么你的 permalink 虽然生效了,但还是会“跳转”或保留中文路径的深层原因。
1. 源码逻辑漏洞:permalink 被当成了“别名”
看这一段核心代码:
if (data.permalink != null && data.permalink.toString() !== "") {
data.permalink = data.permalink.toString() as FullSlug
const aliases = file.data.aliases ?? []
aliases.push(data.permalink) // ⚠️ 重点:它只是把 permalink 放进了 aliases 数组
file.data.aliases = aliases
allSlugs.push(data.permalink)
}发现的问题:
在 Quartz 的这个逻辑里,permalink 并没有被用作主路径(Primary Slug),而是被添加到了 aliases(别名) 数组里。
-
现象:当你访问
domain.com/125b0b时,Quartz 确实能通过别名找到文章,所以能打开。 -
副作用:因为它的“主身份”依然是那个基于文件名的中文路径,Quartz 的 SPA 路由或内链组件在渲染时,依然会取
file.slug(即中文路径),导致地址栏跳回长链接。
2. 彻底解决:重命名才是“版本答案”
既然 Quartz 源码将 permalink 视为别名而非强制覆盖主路径,那么最稳健、最简单粗暴的方法就是直接把 .md 文件重命名为短 ID。
这样做有三个绝对优势:
-
主身份即短链:文件叫
125b0b.md,它的 Primary Slug 就是125b0b,不存在跳转问题。 -
保持中文标题:通过 YAML 里的
title字段,网页上显示的依然是中文。 -
彻底清理
public:不会再生成那些乱码般的中文文件夹。