前言

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

现在的结构大概是这样:url/文件夹/笔记,但是中文路径在浏览器地址栏会被进行 URL 编码(Percent-encoding),原本三个字的标题瞬间变成一串几十个字符

解决思路

将文件夹和笔记名称本身都改为英文,使用 Quartz 支持的title参数将名称变回中文

为了不影响我自己在 Obsidian 客户端上的编辑,最终选择的的解决方法是:

在 CF Pages 部署的时候运行一个python脚本,遍历项目的 content 文件夹,将所有中文命名的文件重命名唯一的为 6 位哈希,并将原来的中文标题放到 YAML 的titlealiases参数里

如果不添加aliases参数,Quartz 的双链会断
因为 Quartz 的链接解析器(Link Resolver) 是基于文件系统路径工作的。当你通过脚本把文件重命名为 df0251.md 后,Quartz 的索引中就不再存在“中文标题”这个路径,因此双链 [[中文标题]] 就会因为找不到匹配的文件名而指向一个不存在的页面(404)

1. 文件夹

文件夹直接改名,在目录中加个index.md写上中文标题即可,顺便关闭不知道为什么会出现的 giscus 评论区

这个博客是怎么创建的

---
title: 博客相关	
comments: "false"	
---

2. 笔记

slugify.py

为什么叫这个名:因为本来听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()

配置

  1. slugify.py放到项目根目录

  2. 由于引入了第三方库 python-frontmatter,你需要告诉 CF Pages 在构建前安装它。

    使用 requirements.txt

    1. 在仓库根目录新建 requirements.txt,内容写:

      python-frontmatter
    2. CF Pages 会检测到该文件并自动安装。你的构建命令只需改为:

        bash gen-changelog.sh && python3 slugify.py && npx quartz build

关于bash脚本是哪来的:实现 Changelog 的自动化更新
AI说安装第三方库会损失一点部署速度,实测下来就五秒左右,完全可以忽略


Gemini 回答

Q:为什么不用 Quartz 自带的 frontmatterpermalink参数

Gemini:

阅读了你提供的 frontmatter.ts 源码后,我终于发现了为什么你的 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

这样做有三个绝对优势:

  1. 主身份即短链:文件叫 125b0b.md,它的 Primary Slug 就是 125b0b,不存在跳转问题。

  2. 保持中文标题:通过 YAML 里的 title 字段,网页上显示的依然是中文。

  3. 彻底清理 public:不会再生成那些乱码般的中文文件夹。