💡 采用“单一模板 + 翻译字典(语言包)”方案,而非为每种语言创建独立模板
国际化(Internationalization,简称 i18n)是指:
在软件设计和开发阶段,就考虑到不同国家、地区、语言和文化的需求,使软件能够轻松地适应各种本地化要求。
来源:
- 单词:internationalization
- 首字母:i
- 末尾字母:n
- 中间字母数:18个字母
- 缩写:i18n
类比:
- localization → l10n(本地化)
- accessibility → a11y(无障碍)
这是软件开发中的标准术语,由 DEC(Digital Equipment Corporation)在 1970-80 年代首创。
| 概念 | 英文 | 缩写 | 含义 | 阶段 |
|---|---|---|---|---|
| 国际化 | Internationalization | i18n | 让软件“能够”支持多语言 | 设计/开发阶段 |
| 本地化 | Localization | l10n | 让软件“实际”支持某种语言 | 翻译/适配阶段 |
关系:
国际化(i18n)是基础
↓
本地化(l10n)是应用
↓
支持中文、英文、日文等
templates/
├── zh/
│ ├── index.html
│ └── about.html
├── en/
│ ├── index.html
│ └── about.html
└── ja/
├── index.html
└── about.html
缺点: - ❌ 维护成本高(5种语言 = 5套模板) - ❌ 修改困难(改一处要同步5个文件) - ❌ 容易遗漏(中文改了,英文忘改)
templates/
├── index.html # 所有语言共用
└── about.html # 所有语言共用
routes/
└── i18n.py # 翻译字典(语言包)
优势: - ✅ 维护简单(只需一套模板) - ✅ 扩展方便(添加新语言只需加翻译) - ✅ 代码复用(业务逻辑完全共享) - ✅ 易于同步(修改一处,所有语言同步)
| 工具 | 语言 | 特点 |
|---|---|---|
| gettext | Python/C/C++ | 历史最悠久,使用 .po/.mo 文件 |
| react-i18next | React | React 生态最流行 |
| vue-i18n | Vue | Vue 官方推荐 |
| Angular i18n | Angular | Angular 内置 |
| 自定义字典 | 任意 | 轻量灵活,适合小项目 |
我们的选择:自定义翻译字典方案(轻量、灵活、易维护)
i18n.py 就是一个多语言字典,包含三个核心部分:
LANGUAGES = {
'zh': {'name': '中文', 'flag': '🇨🇳', 'locale': 'zh-CN'},
'en': {'name': 'English', 'flag': '🇬🇧', 'locale': 'en-US'},
'ja': {'name': '日本語', 'flag': '🇯🇵', 'locale': 'ja-JP'},
'es': {'name': 'Español', 'flag': '🇪🇸', 'locale': 'es-ES'},
'nl': {'name': 'Nederlands', 'flag': '🇳🇱', 'locale': 'nl-NL'}
}
这是语言元数据字典,定义了系统支持哪些语言。
TRANSLATIONS = {
'home': {
'zh': '首页',
'en': 'Home',
'ja': 'ホーム',
'es': 'Inicio',
'nl': 'Home'
},
'about': {
'zh': '关于',
'en': 'About',
'ja': 'について',
'es': 'Acerca de',
'nl': 'Over'
}
}
这是核心翻译字典,结构是:
翻译键 → 语言代码 → 翻译文本
类比:就像一本多语言词典
home(键)
├─ 中文: 首页
├─ 英文: Home
├─ 日文: ホーム
├─ 西语: Inicio
└─ 荷语: Home
def get_translation(key, lang='zh'):
"""获取翻译文本"""
if key not in TRANSLATIONS:
return key
return TRANSLATIONS[key].get(lang, TRANSLATIONS[key].get(DEFAULT_LANGUAGE, key))
这是一个查字典的函数: - 输入:翻译键 + 语言代码 - 输出:对应的翻译文本
文件位置:routes/i18n_middleware.py
核心功能: - 语言检测(URL > Session > 浏览器 > 默认) - 翻译函数注入到所有模板 - 页面状态判断(is_home_page等)
文件位置:routes/__init__.py
from .i18n_middleware import init_i18n
def register_blueprints(app):
init_i18n(app) # 注册多语言中间件
# ... 注册其他蓝图
| 方案 | 模板数量 | 维护成本 | 扩展性 | 采用情况 |
|---|---|---|---|---|
| 单一模板 + 翻译字典 | 1套 | 低 | 高 | ✅ 已采用 |
| 多套独立模板(每种语言一套) | 5套 | 高 | 低 | ❌ 未采用 |
单一模板方案的优势: - ✅ 维护简单:只需维护一套 HTML 模板 - ✅ 扩展方便:添加新语言只需在翻译文件加内容,无需创建新模板 - ✅ 代码复用:所有业务逻辑完全共享 - ✅ 易于同步:修改一处模板,所有语言版本自动同步更新 - ✅ 避免遗漏:不会出现“中文改了但英文忘改”的问题
_() 的工作原理_ 这个名字?历史原因:
- 源自 Python 的 gettext 模块(1990年代)
- _ 在国际上是翻译函数的标准别名
- 简短、易读、符合业界惯例
对比:
# 长命名(啰嗦)
{{ translate('home') }}
{{ get_text('home') }}
{{ get_translation('home') }}
# 标准命名(推荐)
{{ _('home') }}
中间件代码(routes/i18n_middleware.py 第46-76行):
@app.context_processor
def inject_translations():
"""注入翻译函数到所有模板"""
# 1. 定义一个名为 _ 的函数
def _(key):
lang = getattr(g, 'current_lang', DEFAULT_LANGUAGE)
return get_translation(key, lang)
# 2. 把 _ 函数注入到所有模板
return {
'_': _, # ← 这里!
'current_lang': current_lang,
'languages': LANGUAGES,
'is_home_page': is_home,
'home_url': home_url
}
1. 模板中使用:{{ _('what_to_learn') }}
↓
2. 调用 _() 函数:_('what_to_learn')
↓
3. _() 内部调用:get_translation('what_to_learn', 'ja')
↓
4. 从 i18n.py 的 TRANSLATIONS 字典查找:
TRANSLATIONS['what_to_learn']['ja'] = '何を学ぶか'
↓
5. 返回翻译结果:'何を学ぶか'
@index_bp.route('/') # 中文首页(默认)
@index_bp.route('/<lang>/') # 多语言首页
特殊规则:
- /zh/ 自动重定向到 /(中文使用根路径)
- 其他语言使用 /en/, /ja/, /es/, /nl/
所有子页面保持原有路由不变,通过中间件自动识别语言前缀。
示例:
- 中文笔记:/notes/?category=extern
- 日文笔记:/ja/notes/?category=extern
- 英文笔记:/en/notes/?category=extern
<!-- 导航菜单 -->
<a href="{{ home_url }}">{{ _('home') }}</a>
<a href="/about/">{{ _('about') }}</a>
<!-- 页面标题 -->
<h3>{{ _('what_to_learn') }}</h3>
<p>{{ _('knowledge_system') }}</p>
<!-- 按钮 -->
<button>{{ _('back') }}</button>
<!-- 根据当前语言生成对应的首页URL -->
<a href="{{ home_url }}">{{ _('home') }}</a>
逻辑:
- 中文:home_url = '/'
- 其他语言:home_url = '/{lang}/'
从某语言页面进入子页面后,返回首页应回到该语言首页,而非中文首页。
示例:
1. 用户访问日文首页:/ja/
2. 点击进入笔记列表:/ja/notes/?category=extern
3. 点击"首页"链接:回到 /ja/(而非 /)
templates/base.html 顶部导航栏
function switchLanguage(lang) {
// 如果是切换到中文,直接跳转到根路径
if (lang === 'zh') {
window.location.href = '/';
return;
}
// 移除现有的语言前缀
let cleanPath = currentPath;
const langCodes = ['en', 'ja', 'es', 'nl'];
for (const code of langCodes) {
if (cleanPath.startsWith('/' + code + '/')) {
cleanPath = cleanPath.substring(code.length + 1);
break;
}
}
// 添加新的语言前缀
window.location.href = '/' + lang + cleanPath;
}
.back-btn {
white-space: nowrap;
flex-shrink: 0;
}
1. URL路径(最高优先级)
/ja/notes/ → 日语
2. Session存储
用户上次选择的语言
3. Accept-Language头
浏览器语言设置
4. 默认语言
中文(zh)
routes/i18n.py - 翻译字典(50+翻译项)routes/i18n_middleware.py - 多语言中间件routes/__init__.py - 初始化调用routes/index.py - 首页路由templates/base.html - 基础模板(导航栏、语言切换器)templates/index.html - 首页templates/notes_list.html - 笔记列表页templates/note.html - 笔记详情页templates/about.html - 关于页templates/stats.html - 统计页static/css/style.css - 响应式样式gunicorn_app.py - 移除多语言代码(简化66行)步骤1:在 routes/i18n.py 中添加语言配置
LANGUAGES = {
'zh': {'name': '中文', 'flag': '🇨🇳', 'locale': 'zh-CN'},
'en': {'name': 'English', 'flag': '🇬🇧', 'locale': 'en-US'},
'fr': {'name': 'Français', 'flag': '🇫🇷', 'locale': 'fr-FR'}, # 新增法语
}
步骤2:为所有翻译键添加法语翻译
TRANSLATIONS = {
'home': {
'zh': '首页',
'en': 'Home',
'ja': 'ホーム',
'es': 'Inicio',
'nl': 'Home',
'fr': 'Accueil' # 新增
},
# ... 其他翻译
}
步骤3:完成!无需修改任何模板文件。
TRANSLATIONS = {
# 在文件末尾添加
'new_key': {
'zh': '新文本',
'en': 'New Text',
'ja': '新しいテキスト',
'es': 'Nuevo Texto',
'nl': 'Nieuwe Tekst'
}
}
模板中使用:
{{ _('new_key') }}
推荐:
'knowledge_system' # ✅ 清晰
'what_to_learn' # ✅ 语义明确
不推荐:
'KS' # ❌ 缩写不清晰
'whatToLearn' # ❌ 驼峰命名
'learn_what' # ❌ 语序混乱
_()答:Flask-Babel 适合大型项目,但对于本项目:
- 翻译字典方案更轻量
- 无需额外的 .po/.mo 文件
- 更容易维护和扩展
- 性能更好(直接查字典)
答: - 按功能模块拆分(已有:导航、首页、笔记等) - 懒加载(按需加载翻译) - 使用数据库存储(超大规模时考虑)
答:这是第二阶段的工作,需要: 1. 创建多语言笔记目录结构 2. 开发AI翻译工具 3. 修改笔记路由支持语言前缀 4. 实现翻译版本回退机制
.i18n/ 目录存储翻译版本📝 笔记创建:2026-05-09
🔄 最后更新:2026-05-09
📖 相关文档:《多语种实施方案.md》、《笔记格式规范.md》