Astro 博客新增语言:零复制工作流
背景
博客上线初期只支持中英文。随着内容积累,我们于 2026-04-07 正式接入俄语。从决定到上线只用了很短时间,这得益于一套在重构阶段就打好的参数化 i18n 架构。
本文不重述架构细节(可参考前文《Astro 国际化重构:从平行目录树到参数化路由》),而是把新增语言的具体操作步骤固定成可复用的工作流。
前置要求
博客已具备以下基础设施:
src/i18n/config.ts— 语言配置中心src/i18n/utils.ts—t()翻译函数src/i18n/{zh,en}.json— 现有翻译文件src/layouts/Layout.astro— 国旗下拉菜单
标准流程(5 步)
Step 1 — 注册语言代码
修改文件: src/i18n/config.ts
在三个配置项中各添加一行:
export const locales = ['zh', 'en', 'ru'] as const;
// ↑ 新语言代码
export const localeNames: Record<Locale, string> = {
zh: '中文',
en: 'EN',
ru: 'RU', // ← 新增
};
export const localePaths: Record<Locale, string> = {
zh: '',
en: 'en',
ru: 'ru', // ← 新增
};
localePaths中的值必须与locales数组中的字面量一致,用于 URL 前缀生成。
Step 2 — 创建翻译文件
新增文件: src/i18n/{code}.json
从 zh.json 复制全套 key,将中文值替换为新语言。命名规范:
- 所有 key 使用 snake_case(如
hero_title而非heroTitle) - 翻译函数
t()在utils.ts中按dict[lang][key]取值,严格区分大小写
必须包含的顶级 key:
| 顶级 key | 说明 |
|---|---|
nav | 导航链接 |
home | 首页内容(标题、副标题) |
archive | 归档页(分页、筛选) |
tags | 标签页 |
about | 关于页(使命卡片、技术栈、链接) |
changelog | 变更日志 |
post | 文章页(翻页、标签、目录) |
search | 搜索 |
404 | 404 迷航页 |
footer | 页脚 |
Step 3 — 注册翻译字典
修改文件: src/i18n/utils.ts
在 dicts 对象中注册新语言:
const dicts: Record<Locale, Record<string, any>> = {
zh,
en,
ru, // ← 新增
};
Step 4 — 添加国旗下拉选项
修改文件: src/layouts/Layout.astro
在语言下拉菜单的 HTML 中添加新选项,使用 flagcdn.com 国旗图片:
<!-- 当前语言按钮显示 -->
{lang === 'ru' ? <span class="flag-label">
<img src="https://flagcdn.com/w20/ru.png" width="20" height="14" alt="Русский" class="flag-img"/>
Русский
</span> : ...}
<!-- 下拉菜单选项 -->
<a href="/ru/" class="lang-option" data-lang="ru">
<img src="https://flagcdn.com/w20/ru.png" width="20" height="14" alt="Русский" class="flag-img"/>
Русский
</a>
国别代码使用 ISO 3166-1 alpha-2(俄罗斯 → ru,英国 → gb,中国 → cn)。
Step 5 — 构建部署
npm run build
docker build -t astro-blog:latest .
docker stop astro-blog && docker rm astro-blog
docker run -d --name astro-blog --network astro-net -p 8083:80 --restart unless-stopped astro-blog:latest
git add -A && git commit -m "i18n: add Russian language" && git push
避坑指南
key 命名不一致
症状: 页面显示 key 名(如 about.intro_p1)而非翻译文本。
原因: zh.json 用 snake_case,翻译者写成了 camelCase。
解法: 始终从 zh.json 复制 key 名,不要自己发明。
about.astro 残留硬编码
症状: about 页部分内容不翻译(其他页面正常)。
原因: about.astro 中曾使用 lang === 'en' ? '英文文本' : '中文文本' 内联判断,未走 t() 函数。
解法: 将组件中所有内联文案抽取为 t() 调用,新增语言后自然生效。
flagcdn 无法访问
症状: 国旗图片在服务器上 404。
原因: 本机网络无法访问 flagcdn.com。
解法: 将国旗图片下载到本地 public/flags/ 目录,改用相对路径引用。
总结
这套流程的核心优势在于:新增语言是数据操作,而非工程操作。不再需要复制页面文件、不再需要修改每个组件的路由逻辑、不再需要维护散落在各处的语言判断。
架构设计决定了维护成本的上限。i18n 重构的投资在今天得到了回报——俄语上线几乎没有摩擦。