为博客添加多语言 AI 翻译功能
在实现了 RAG 驱动的 AI 助手之后,我希望让博客内容能被全球读者访问。与其手动翻译每篇文章,我构建了一个按需 AI 翻译系统,支持 10 种语言。
目标
创建无缝的翻译体验:
- 按需将任何博客文章翻译成 10 种主要语言
- 保留 Markdown 格式(标题、代码块、链接)
- 缓存翻译结果以降低 API 成本
- 翻译时显示加载反馈
架构
┌─────────────────────────────────────────────────────────────────┐
│ 浏览器(前端) │
├─────────────────────────────────────────────────────────────────┤
│ 语言选择器 ──▶ 检查 localStorage 缓存 │
│ │ │ │
│ │◀── 缓存命中 ────────┘ │
│ │ │
│ ▼ 缓存未命中 │
│ POST /api/translate { content, targetLang, slug } │
└─────────┼───────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Worker (/api/translate) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 检查 KV 缓存 │───命中──▶│ 返回缓存结果 │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ 未命中 │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 调用 OpenAI API │───────▶│ 存入 KV 缓存 │ │
│ │ (gpt-4o-mini) │ │ (30天 TTL) │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
支持的语言
系统支持 10 种语言,覆盖全球大部分人口:
| 代码 | 语言 | 代码 | 语言 |
|---|---|---|---|
| en | English | fr | Français |
| zh | 中文 | de | Deutsch |
| ja | 日本語 | pt | Português |
| ko | 한국어 | ru | Русский |
| es | Español | ar | العربية |
实现
1. 语言选择器 UI
一个简洁的下拉菜单,出现在每篇博客文章上:
<div class="lang-selector" id="lang-selector">
<span class="lang-selector-label">
<svg><!-- 翻译图标 --></svg>
Language
</span>
<select class="lang-select" id="lang-select">
<option value="en">🇺🇸 English</option>
<option value="zh">🇨🇳 中文</option>
<option value="ja">🇯🇵 日本語</option>
<!-- ... 更多语言 -->
</select>
<button class="lang-original-btn" id="lang-original-btn">
Show Original
</button>
<span class="lang-status" id="lang-status"></span>
</div>
2. 加载动画
翻译期间显示纯 CSS 旋转动画:
.lang-status.translating {
color: var(--vermilion);
display: flex;
align-items: center;
gap: 0.5rem;
}
.lang-status.translating::before {
content: '';
width: 14px;
height: 14px;
border: 2px solid #f0f0f0;
border-top-color: var(--vermilion);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
3. 双层缓存策略
为了降低 API 成本,我在两个层面实现了缓存:
前端(localStorage) - 7 天 TTL:
function getTranslationCache(key) {
const raw = localStorage.getItem(key);
if (!raw) return null;
const data = JSON.parse(raw);
if (Date.now() - data.timestamp > 7 * 24 * 60 * 60 * 1000) {
localStorage.removeItem(key);
return null;
}
return data.content;
}
后端(Cloudflare KV) - 30 天 TTL:
// 检查缓存
const cached = await env.TRANSLATIONS.get(cacheKey);
if (cached) {
return Response.json({ translated: cached, cached: true });
}
// 翻译后缓存结果
await env.TRANSLATIONS.put(cacheKey, translated, {
expirationTtl: 30 * 24 * 60 * 60
});
4. 翻译 API(Cloudflare Worker)
Worker 通过精心设计的提示词处理翻译请求:
if (path === "/api/translate") {
const { content, targetLang, slug } = await request.json();
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [{
role: "system",
content: `You are a professional translator. Translate to ${langName}.
Rules:
- Preserve all Markdown formatting
- Keep code blocks unchanged
- Keep technical terms in original form when appropriate
- Do not add explanations
- Return only the translated content`
}, {
role: "user",
content
}],
max_tokens: 4096,
temperature: 0.3,
}),
});
const data = await response.json();
return Response.json({ translated: data.choices[0].message.content });
}
5. 智能语言检测
前端自动检测原始语言以设置正确的默认值:
function detectLanguage(text) {
const chineseChars = (text.match(/[\u4e00-\u9fff]/g) || []).length;
const totalChars = text.length;
if (chineseChars / totalChars > 0.3) return 'zh';
return 'en';
}
关键设计决策
翻译 Markdown 而非 HTML:在可用时,我翻译原始 Markdown 源码并重新渲染。这比翻译渲染后的 HTML 能更好地保留格式。
低温度值(0.3):翻译需要一致性,而非创造性。低温度值能产生更可靠的结果。
内容哈希作为缓存键:使用
slug_hash_lang作为缓存键确保原始内容变化时,翻译缓存自动失效。可选的 KV 绑定:Worker 在没有 KV 存储的情况下也能工作——只是没有服务端缓存。这使开发更容易。
成本分析
使用 gpt-4o-mini 进行翻译:
- 平均博客文章:约 3000 tokens 输入,约 3000 tokens 输出
- 每次翻译成本:约 $0.003
- 有缓存后,重复请求免费
对于中等流量的博客,预计翻译成本每月不超过 $5。
未来改进
- 构建时预翻译热门文章
- 为长文章添加流式传输
- 支持 RTL 语言(阿拉伯语)的正确 CSS
- 添加翻译质量反馈
完整实现在我的网站仓库中。
系列: blog-infra (3/19)
系列页
▼