KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
06 · 国际化与多区域:hreflang 的完整语义 — keel 龙骨
本章目标:把 hreflang 从"加几行标签"讲成一套可验证、可排错的多区域方案。 验收标准:语言区域映射双向自洽、无回链错误、canonical 与 sitemap 策略一致。
本章目标:把 hreflang 从"加几行标签"讲成一套可验证、可排错的多区域方案。
验收标准:语言区域映射双向自洽、无回链错误、canonical 与 sitemap 策略一致。
为什么这是深水区:hreflang 的规则看似简单,但错误率极高——因为它要求双向对称,任何一侧漏写都会整组失效。而且它常与 canonical 打架。这一章把语义、矩阵与验证方法一次讲透。
一、hreflang 要解决什么
同一内容有多个语言/地区版本时,用户该看到哪个?hreflang 就是给搜索引擎的"版本路由表"。
- 语言:
zh、en、ja - 地区(可选,用
-连接):zh-Hans、en-US、en-GB、pt-BR
<link rel="alternate" hreflang="zh-Hans" href="https://www.xxxxxx.cn/learning/" />
<link rel="alternate" hreflang="en" href="https://www.xxxxxx.cn/en/learning/" />
<link rel="alternate" hreflang="x-default" href="https://www.xxxxxx.cn/learning/" />
| 值 | 含义 |
|---|---|
zh-Hans |
简体中文(语言+书写系统) |
en-US |
美式英语(语言+地区) |
x-default |
默认/兜底版本(无匹配语言时) |
二、三条硬性语义
- 必须双向自洽:A 声明指向 B,则 B 必须声明指回 A。单向声明会被忽略。
- 必须互相引用,含自身:每个版本都要列出包括自己在内的所有版本。
- URL 必须是绝对地址,且是规范的、可索引的终址(不是 301 前的地址)。
❌ 错误:只有 zh 页声明指向 en 页,en 页没指回
✅ 正确:zh 和 en 两页都列出 {zh, en, x-default} 三条
三、常见错误矩阵
| 错误 | 症状 | 结果 |
|---|---|---|
| 单向声明 | 只有一侧有 hreflang | 整组被忽略 |
| 遗漏自身 | 自己不在列表里 | 该版本不受益 |
| 指向 301 地址 | href 是重定向前的 URL | 无法确认映射 |
| 语言代码错误 | 用 zh-CN 当地区(应结合规范) |
可能不被识别 |
x-default 缺失 |
无兜底版本 | 无匹配用户落到哪不确定 |
| hreflang 与 canonical 冲突 | canonical 指向另一语言版本 | 版本被合并 |
| 用 JS 注入 | 第一波看不到 | 不生效 |
| 只在 sitemap、漏了 head | 两处不一致 | 混乱 |
最容易致命的组合:hreflang 指向 X,canonical 却指向 Y。这等于同时说"我是 X 的替代"和"我的规范版本是 Y",自相矛盾。正确做法:每个语言版本的 canonical 指向自己,用 hreflang 表达替代关系。
四、canonical 与 sitemap 的策略
| 场景 | canonical | hreflang |
|---|---|---|
| 各语言独立内容 | 各自指向自己 | 互相引用全部版本 |
| 地区变体(en-US/en-GB) | 各自指向自己 | 互相引用 + x-default |
| 语言版本是机器翻译、质量低 | 指向主语言版本 | 建议不声明,或 noindex |
多语言 sitemap 的两条路线:
<!-- 路线 A:在 sitemap 里用 xhtml:link 声明 -->
<url>
<loc>https://www.xxxxxx.cn/learning/</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://www.xxxxxx.cn/en/learning/"/>
</url>
- 路线 A:hreflang 集中在 sitemap,便于集中维护,但解析更严格。
- 路线 B:hreflang 放在每页
<head>,直观但改动要动每个模板。
选择建议:页数少用 B,页数多用 A;两者择一,不要混用造成不一致。
五、验证方法
hreflang 的验证核心是双向自洽检查——这可以脚本化:
# 抓取两个语言版本,确认各自都声明了对方与自身
check() {
u=$1
echo "== $u =="
curl -s "$u" | grep -oE '<link[^>]*hreflang="[^"]+"[^>]*>'
}
check https://www.xxxxxx.cn/learning/
check https://www.xxxxxx.cn/en/learning/
逐条核对清单:
| 检查 | 通过标准 |
|---|---|
| 组内每一页都列出全部版本 | 是 |
| 每页列表包含自身 | 是 |
| 任意两页互为声明 | 成立 |
| href 是 200 的终址 | 是 |
| hreflang 与 canonical 不冲突 | 各指自己 |
| 存在 x-default | 是(建议) |
# 遍历多语言 URL 做一致性抽样(示例)
for p in /learning/ /en/learning/ /ja/learning/; do
printf "%-20s hreflang_count=%s\n" "$p" \
"$(curl -s https://www.xxxxxx.cn$p | grep -c 'hreflang=')"
done
六、多区域架构的选型
hreflang 之外,还要先决定"不同语言/地区放在哪",这决定了权重能不能集中:
| 架构 | 例子 | 权重集中度 | 运维成本 | 适用 |
|---|---|---|---|---|
| ccTLD(国别域) | xxxxxx.cn / xxxxxx.de |
分散(各自独立) | 最高 | 强地域合规要求 |
| 子域名 | en.xxxxxx.cn |
中等 | 中 | 多团队独立运维 |
| 子目录(推荐) | xxxxxx.cn/en/ |
最高 | 最低 | 多数中小站 |
| 参数 | xxxxxx.cn/?lang=en |
低,易重复 | 低 | 不推荐 |
选型判据:
- 优先子目录——权重集中在同一域名,运维最简单,hreflang 也最易对齐。
- 子域名与 ccTLD 会分散权重,只有在合规或组织要求下才用。
- 不要用参数表达语言——容易产生重复内容与 canonical 争议。
无论选哪种,每个语言版本都要有唯一、稳定的 URL,且能与 canonical、sitemap、hreflang 三者对齐。
七、故障速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 版本不互跳 | 单向声明 | 补双向 |
| 某语言不收录 | canonical 指到别语言 | 改自引用 |
| 搜索结果显示错误语言 | x-default 缺失/错配 | 补 x-default |
| 改版后 hreflang 失效 | href 指向旧 URL | 更新为终址 |
| sitemap 与 head 冲突 | 两处都写了且不一致 | 择一 |
| 机器翻译页拉低质量 | 薄内容多语言 | noindex 或改进质量 |
八、本章验收
- 每个语言/地区页都列出全部版本(含自身)
- 组内两两互为声明(双向自洽)
- 每页 canonical 指向自己,与 hreflang 无冲突
- 有 x-default 兜底
- hreflang 在 head 或 sitemap 中只出现一处,且可控
- 有抽样验证脚本记录
# 本章核心验收:抽取所有 hreflang 声明
curl -s https://www.xxxxxx.cn/learning/ | grep -oE 'hreflang="[^"]+"'
多区域就绪后,最大的一次高风险操作是站点迁移——那需要可回滚的映射工程。