Caddy + Hugo 实现基于 Accept-Language 的多语言智能跳转
目录
需求背景
我的个人博客(基于 Hugo + FixIt 主题)支持中英文双语。目录结构如下:
content/
├── posts/
│ ├── podman.zh-CN.md
│ ├── podman.en.md
│ ├── custom-caddy-build.zh-CN.md
│ └── custom-caddy-build.en.md
└── ...Hugo 构建后,生成独立的语言目录:
/www/blog/
├── zh-cn/
│ ├── posts/
│ │ └── custom-caddy-build/
│ └── 404.html
├── en/
│ ├── posts/
│ │ └── custom-caddy-build/
│ └── 404.html
└── index.html (根路径重定向)根路径 / 通常是空的。访问者到达 www.example.com 时,我希望 Caddy 根据浏览器语言偏好自动把他送到正确的语言版本。如果目标语言的文章不存在,降级到另一种语言。如果都不存在,显示默认语言的 404 页面。
完整配置
www.example.com {
root * /www/blog
# 根路径语言跳转
route / {
@chinese header_regexp Accept-Language ^zh
redir @chinese /zh-cn/ 302
redir * /en/ 302
}
# 非根路径语言探测
@needs_localization not path / /en/* /zh-cn/*
route @needs_localization {
@zh_target_exists {
header_regexp Accept-Language ^zh
file {root}/zh-cn{uri} {root}/zh-cn{uri}/index.html
}
redir @zh_target_exists /zh-cn{uri} 301
@en_target_exists {
file {root}/en{uri} {root}/en{uri}/index.html
}
redir @en_target_exists /en{uri} 301
}
file_server
handle_errors 404 {
rewrite * {root}/en/404.html
file_server
}
}逐层解析
根路径语言跳转
route / {
@chinese header_regexp Accept-Language ^zh
redir @chinese /zh-cn/ 302
redir * /en/ 302
}访问 www.example.com/(根路径)时:
- 检查
Accept-Language请求头是否以zh开头(包括zh-CN、zh-TW、zh-HK等) - 中文用户 →
/zh-cn/(302 临时重定向) - 其他语言用户 →
/en/(默认回退)
子路径语言探测
@needs_localization not path / /en/* /zh-cn/*这个命名匹配器定义了什么路径需要语言探测。排除了三种情况:
/:根路径已在上一节处理/en/*:已经是英文路径/zh-cn/*:已经是中文路径
也就是说,当用户访问 /posts/custom-caddy-build/ 这样一个没有语言前缀的中性路径时,触发探测。
route @needs_localization {
@zh_target_exists {
header_regexp Accept-Language ^zh
file {root}/zh-cn{uri} {root}/zh-cn{uri}/index.html
}
redir @zh_target_exists /zh-cn{uri} 301
@en_target_exists {
file {root}/en{uri} {root}/en{uri}/index.html
}
redir @en_target_exists /en{uri} 301
}探测逻辑:
- 首先检查 中文条件:用户语言是中文
^zh且 中文版的路径存在(file指令探测文件) - 如果中文版存在 → 301 重定向到
/zh-cn{uri} - 然后检查 英文条件:英文版路径是否存在
- 如果英文版存在 → 301 重定向到
/en{uri} - 如果都不存在 → 继续执行后面的
file_server(会命中 404)
file 指令会尝试两个模式:{uri} 是类似目录的访问方式(Hugo 生成的目录结构),{uri}/index.html 是作为文件的访问方式。任何一个存在就认为语言版本可用。
404 错误处理
handle_errors 404 {
rewrite * {root}/en/404.html
file_server
}如果用户访问了一个不存在的路径(无论哪个语言版本),统一显示英文的 404 页面。当然也可以做得更精细——根据 Accept-Language 显示不同语言的 404,但我觉得一个简单的 404 页面不值得增加复杂度。
总结
这套用 Caddy 实现的多语言跳转方案,完全在反向代理层完成。配置的核心是 header_regexp + file 指令的组合探测,配合 301/302 跳转实现语言降级。与 Hugo 的静态站点结构配合得很好——Hugo 负责生成带语言前缀的目录,Caddy 负责把用户送到正确的目录。
参考链接
添加评论