双语版本逐渐漂移:6 种漂移类型 + 按对决定「翻译 / 同步 / 标单语」

EN 更新 5 次 ZH 还停在两年前——审计漂移、按对决定(翻 / 同步 / 标单语)、自动化未来同步。

两年前上线双语。今天:EN 文章上线后每篇更新 5 次,ZH 多数没动过。ZH 页引用过期截图、缺失只在 EN 有的章节、cross-link 指向 EN 里被改名的页面——Search Console 几十对 hreflang 警告。你骄傲启动的双语站点其实是一个站点 + 一份褪色的影子。

双语内容是承诺不是发版事件。漂移会复利:6 个月单边更新 = 1 年补课债务。修法不是”现在全翻一遍”——是审计 + 按对决定,未来更新自动同步,接受有些文章应标单语而不是维护得糟。

常见原因

按命中率从高到低:

1. 翻译只在上线时做了一次,更新只动主语言

上线时翻了 200 篇 EN 到 ZH。之后每次更新只动英文——ZH 时间冻结、EN 持续演进。

如何判断:对同一 translationKey 比 en/ vs zh/ 的 git log——50%+ 的对 en 更新——系统性漂移。

2. 一边改名 translationKey 配对断了

gpt-tips.mdx 在 EN 改成 chatgpt-tips.mdx,ZH 还是 gpt-tips.mdx——不再配对,两页 hreflang 都坏。

如何判断:找只在一边有的 translationKey

diff \
  <(grep -h "translationKey:" src/content/articles/en/**/*.mdx | sort -u) \
  <(grep -h "translationKey:" src/content/articles/zh/**/*.mdx | sort -u)

3. 一边发了新文章,另一边没翻

你每月加 5 篇 EN,只翻 1-2 篇到 ZH——翻译 backlog 每月长。一年后 50+ 篇仅 EN。

如何判断:en/ 有 translationKey 但没对应 zh/ 文件的——数它们就是翻译债务。

4. 机翻没审就用了

为”补漂移”机翻了缺失的——ZH 听着流畅但 context 错(页面写「click 提交」但 ZH 文字写「submit按钮」)。双语存在但质量比单语差。

如何判断:随机读 5 篇最近 ZH 翻译——有 AI 翻译典型征兆(直译习语、英文残留、UI 词不匹配)——就是没审的机翻。

EN 文章 link 到 /zh/articles/old-name/——因为 EN 先写、作者 copy-paste link 没改 locale。ZH 读者点 → 404。

如何判断:grep en/ 文件里的 /zh/articles/(反之亦然)——通常错的,除非显式双语引用。数错 locale 的 link。

6. 翻译在内容上分叉了

EN 扩了新例子,ZH 为简洁被精简。现在两边不是翻译——是相关文章。hreflang 暗示它们一样——读者发现不是。

如何判断:字数比——en > 1.5x zh(或反过来),不能用语言冗余解释——内容真分叉了。

最短修复路径

按收益从高到低。Step 1 审计,其余按漂移类型决定怎么办。

Step 1:建漂移审计

脚本检查配对:

# scripts/audit-bilingual.mjs
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";

const en = collectKeys("src/content/articles/en/troubleshooting");
const zh = collectKeys("src/content/articles/zh/troubleshooting");

console.log("仅 EN:", [...en.keys()].filter(k => !zh.has(k)));
console.log("仅 ZH:", [...zh.keys()].filter(k => !en.has(k)));
console.log("都有,EN 更新:", [...en.keys()].filter(k => zh.has(k) && en.get(k).mtime > zh.get(k).mtime));

function collectKeys(dir) {
  const map = new Map();
  for (const f of fs.readdirSync(dir)) {
    const p = path.join(dir, f);
    const { data } = matter(fs.readFileSync(p, "utf8"));
    if (data.translationKey) {
      map.set(data.translationKey, { path: p, mtime: fs.statSync(p).mtime });
    }
  }
  return map;
}

输出:仅 EN、仅 ZH、EN 较新对——这是你的漂移清单。

Step 2:每对决定翻 / 同步 / 标单语

对类型动作
仅 EN(a) 翻译到 ZH,或 (b) 标单语 hreflang 声明
仅 ZH同理反向
两边都有、EN 较新按现 EN 同步 ZH
两边都有、ZH 较新按现 ZH 同步 EN
两边都有、内容分叉选 canonical 同步另一边,或拆成两篇

不是每篇都要双语。「标单语」是合理选择——比维护得糟好。

Step 3:先同步高价值对

不要一周末搞 200 对——按流量优先:

# GSC:按 URL 模式过滤 /zh/articles/*
# 按 impression / 点击排序
# Top 20 有流量的 ZH 页优先同步

月 100 impression 的 ZH 值得同步,月 0 impression 的不值。

Step 4:未来更新自动化翻译队列

加 CI check 或 pre-commit hook:

# scripts/check-translation-sync.sh
# en/*.mdx 改了就检查对应 zh/*.mdx
# zh 比 en 前一个版本旧就 fail

CHANGED=$(git diff --name-only origin/main -- 'src/content/articles/en/' | grep '\.mdx$')
for f in $CHANGED; do
  zh=$(echo "$f" | sed 's/\/en\//\/zh\//')
  if [ -f "$zh" ]; then
    # ZH 存在,flag 让人 review
    echo "::warning::EN 更新:$f——ZH 可能要同步:$zh"
  fi
done

不自动翻译,PR 阶段把漂移浮出来让你决定。

Step 5:显式修 hreflang

sitemap / 页面 metadata:

<url>
  <loc>https://site.com/en/articles/topic/</loc>
  <xhtml:link rel="alternate" hreflang="en" href="https://site.com/en/articles/topic/" />
  <xhtml:link rel="alternate" hreflang="zh" href="https://site.com/zh/articles/topic/" />
  <xhtml:link rel="alternate" hreflang="x-default" href="https://site.com/en/articles/topic/" />
</url>

没对应物的文章不要有 hreflang 条目——单语声明。别做一半。

Step 6:拒盲目机翻

必须自动翻译补课:

- 自动翻到 draft 分支
- 每篇人审 UI 词、习语、tone
- 验证 cross-link 是 localized 的
- 才发

差 ZH 比没 ZH 差。AdSense 和 Google 都检测到未审 MT 内容。

预防建议

  • CI 加漂移自动检查——EN 更新但 ZH 没(反之亦然)触发警告
  • 翻译债务是追踪指标,月审一次刻意降低
  • 没双语维护的文章标单语,不是半维护
  • 高流量页强制同步,0 流量页单语就够
  • 拒未审机翻——差双语比单语差
  • translationKey 是合同——改名一个文件就要改名另一边

相关阅读

标签: #内容运营 #站点质量 #站点审计 #排查 #双语