概述
本项目使用 Babel 进行国际化(i18n)管理,通过 tfcommon.utils.translation 模块提供翻译功能。当前支持的语言包括:
zh_CN(简体中文)- 默认语言
en_US(英文)
目录结构
project-tianlu/
├── locale/ # 国际化文件目录
│ ├── tianfu.pot # 翻译模板文件(由 Babel 自动生成)
│ ├── zh_CN/ # 中文翻译
│ │ └── LC_MESSAGES/
│ │ ├── tianfu.po # 可编辑的翻译源文件
│ │ └── tianfu.mo # 编译后的二进制翻译文件
│ └── en_US/ # 英文翻译
│ └── LC_MESSAGES/
│ ├── tianfu.po
│ └── tianfu.mo
├── babel/ # Babel 配置文件
│ ├── Taskfile.yml # Babel 任务定义
│ └── babel.cfg # Babel 提取配置
└── src/
└── translator.py # 翻译模块初始化
核心组件
1. 翻译模块 (src/translator.py)
init_translations()
初始化翻译系统,设置翻译文件目录。
def init_translations():
translation.init_translations(
localedirs=[
str(Path(__file__).parent.parent / "locale"),
]
)
调用时机:在应用启动时(src/app.py 的 lifespan 函数中)调用。
LocaleMiddleware
语言中间件,为每个请求设置默认语言为中文(zh_CN)。
class LocaleMiddleware(BaseHTTPMiddleware):
"""语言中间件:设置默认语言为中文(zh_CN)"""
async def dispatch(self, request: Request, call_next: Callable) -> Response:
# 直接设置默认语言为中文
translation.activate("zh_CN")
response = await call_next(request)
return response
注册位置:在 src/app.py 中通过 app.add_middleware(LocaleMiddleware) 注册。
2. 应用初始化 (src/app.py)
在 FastAPI 应用的生命周期中初始化翻译系统:
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理"""
# 应用启动时:初始化数据库(创建所有表)
await init_database()
init_translations() # 初始化翻译系统
yield
# 应用关闭时:清理资源
engine.dispose()
await async_engine.dispose()
Babel 任务
项目使用 Task 管理 Babel 相关任务。所有 Babel 任务定义在 babel/Taskfile.yml 中,通过主 Taskfile.yml 的 includes 引入。
Includes 机制
在主 Taskfile.yml 中,通过 includes 字段引入 Babel 任务:
includes:
babel: babel/Taskfile.yml
工作原理:
babel是命名空间前缀,用于区分不同 Taskfile 中的任务babel/Taskfile.yml是被引入的 Taskfile 路径(相对于主 Taskfile)引入后,可以通过
task babel:<任务名>的方式调用 Babel Taskfile 中定义的任务例如:
task babel:extract会执行babel/Taskfile.yml中定义的extract任务
优势:
模块化:将 Babel 相关任务独立管理,保持主 Taskfile 简洁
命名空间:通过
babel:前缀避免任务名冲突可维护性:Babel 配置集中在一个文件中,便于维护和更新
Babel Taskfile 详细说明
babel/Taskfile.yml 文件结构如下:
变量定义 (vars)
vars:
BABEL_CFG: "babel/babel.cfg" # Babel 提取配置文件路径
POT_FILE: "locale/tianfu.pot" # 翻译模板文件(POT)路径
DOMAIN: "tianfu" # 翻译域名称,用于区分不同的翻译集合
TRANSLATIONS_DIR: "locale" # 翻译文件根目录
PYBABEL: "uvx --managed-python --from babel pybabel" # pybabel 命令(通过 uvx 执行)
VENV_PACKAGES: ".venv/lib/site-packages" # 虚拟环境包目录(用于提取第三方库翻译)
变量说明:
BABEL_CFG:指定 Babel 如何从源码中提取翻译标记的配置文件POT_FILE:POT(Portable Object Template)文件,包含所有待翻译的原始文本DOMAIN:翻译域,用于在同一项目中管理多组翻译(当前使用tianfu)TRANSLATIONS_DIR:所有语言翻译文件的根目录PYBABEL:使用uvx工具执行babel包中的pybabel命令,确保使用正确的 Python 环境VENV_PACKAGES:虚拟环境中的第三方包路径,当前用于提取fitpy库的翻译
任务定义 (tasks)
可用任务
1. task babel:extract
从源码中提取待翻译文本到 POT 文件。
task babel:extract
任务定义:
extract:
desc: "从源码中提取待翻译文本到 POT 文件"
vars:
BABEL_SOURCES: "{{.VENV_PACKAGES}}/fitpy"
cmds:
- "{{.PYBABEL}} extract -F {{.BABEL_CFG}} -k gettext_lazy -k gettext_noop -k ngettext_lazy:1,2 -k pgettext_lazy:1c,2 -k npgettext_lazy:1c,2,3 -o {{.POT_FILE}} {{.BABEL_SOURCES}}"
功能:
扫描源码中的翻译标记(如
gettext_lazy、gettext_noop等)生成
locale/tianfu.pot模板文件
参数说明:
-F {{.BABEL_CFG}}:指定 Babel 提取配置文件-k <keyword>:指定要提取的翻译函数关键字gettext_lazy:延迟翻译函数(用于模型字段等)gettext_noop:标记但不翻译的函数ngettext_lazy:1,2:复数形式的延迟翻译(参数位置 1 和 2)pgettext_lazy:1c,2:带上下文的延迟翻译(1c 表示上下文,2 表示消息)npgettext_lazy:1c,2,3:带上下文的复数延迟翻译
-o {{.POT_FILE}}:指定输出的 POT 文件路径{{.BABEL_SOURCES}}:要扫描的源码路径(当前为.venv/lib/site-packages/fitpy)
注意:任务内部定义了 BABEL_SOURCES 变量,覆盖了全局变量,当前配置为提取 fitpy 第三方库的翻译。如需提取项目自身代码,可修改此变量为 src。
2. task babel:update
从 POT 文件更新所有语言的 PO 文件。
task babel:update
任务定义:
update:
desc: "从 POT 文件更新 PO 文件"
deps:
- extract
cmds:
- "{{.PYBABEL}} update -D {{.DOMAIN}} -i {{.POT_FILE}} -d {{.TRANSLATIONS_DIR}}"
功能:
自动依赖
babel:extract任务(确保 POT 文件是最新的)更新所有语言的 PO 文件(如
locale/zh_CN/LC_MESSAGES/tianfu.po和locale/en_US/LC_MESSAGES/tianfu.po)保留已有的翻译内容,只添加新的待翻译条目
标记已删除的翻译条目为过时(但不会删除)
参数说明:
deps: [extract]:任务依赖,执行前会自动运行babel:extract-D {{.DOMAIN}}:指定翻译域(tianfu)-i {{.POT_FILE}}:指定输入的 POT 文件路径-d {{.TRANSLATIONS_DIR}}:指定翻译文件目录(locale)
工作流程:
自动执行
babel:extract生成最新的 POT 文件扫描
locale目录下的所有语言目录对每个语言的 PO 文件进行更新:
添加新的待翻译条目
保留已有翻译
标记已删除的条目为过时
3. task babel:compile
编译 PO 文件到 MO 文件(二进制格式,运行时使用)。
task babel:compile
任务定义:
compile:
desc: "编译 PO 文件到 MO 文件"
sources:
- "{{.TRANSLATIONS_DIR}}/**/*.po"
generates:
- "{{.TRANSLATIONS_DIR}}/**/*.mo"
cmds:
- "{{.PYBABEL}} compile -D {{.DOMAIN}} -d {{.TRANSLATIONS_DIR}}"
功能:
将
*.po文件编译为*.mo文件(二进制格式)MO 文件是运行时实际使用的翻译文件,性能更好
参数说明:
sources:指定源文件模式,Task 会检查这些文件的变化来决定是否需要重新编译generates:指定生成的文件模式,用于增量构建-D {{.DOMAIN}}:指定翻译域(tianfu)-d {{.TRANSLATIONS_DIR}}:指定翻译文件目录(locale)
增量构建:
Task 会自动检查
sources中的 PO 文件是否有变化只有当 PO 文件被修改时,才会重新编译对应的 MO 文件
这提高了构建效率,特别是在大型项目中
注意:修改 PO 文件后必须执行此任务,否则翻译不会生效。应用运行时读取的是 MO 文件,不是 PO 文件。
4. task babel:init
初始化新的语言翻译文件。
task babel:init LANG=en_US DOMAIN=tianfu
任务定义:
init:
desc: "初始化翻译文件目录,注意文件已存在的话会被覆盖,需要 LANG 参数,例如 task babel:init LANG=en_US DOMAIN=tianfu"
cmds:
- "{{.PYBABEL}} init -D {{.DOMAIN}} -i {{.POT_FILE}} -d {{.TRANSLATIONS_DIR}} -l {{.LANG}}"
功能:
为新的语言创建初始 PO 文件
基于 POT 文件生成空的翻译模板
参数说明:
LANG:必需参数,语言代码(如en_US、zh_CN、ja_JP)DOMAIN:可选参数,翻译域(默认为tianfu,可通过全局变量覆盖)-D {{.DOMAIN}}:指定翻译域-i {{.POT_FILE}}:指定输入的 POT 模板文件-d {{.TRANSLATIONS_DIR}}:指定翻译文件目录-l {{.LANG}}:指定要初始化的语言代码
使用示例:
# 初始化英文翻译(使用默认域 tianfu)
task babel:init LANG=en_US
# 初始化日文翻译(使用默认域 tianfu)
task babel:init LANG=ja_JP
# 初始化英文翻译(指定域)
task babel:init LANG=en_US DOMAIN=myapp
生成的文件结构:
执行 task babel:init LANG=ja_JP 后,会创建:
locale/
└── ja_JP/
└── LC_MESSAGES/
└── tianfu.po # 新创建的翻译文件
注意:
如果目标 PO 文件已存在,会被覆盖(所有已有翻译会丢失)
建议在初始化前备份现有翻译文件
初始化后需要手动编辑 PO 文件添加翻译内容
工作流程
添加新翻译
提取待翻译文本:
task babel:extract更新 PO 文件:
task babel:update编辑翻译文件:
打开
locale/zh_CN/LC_MESSAGES/tianfu.po或locale/en_US/LC_MESSAGES/tianfu.po为
msgstr ""字段添加翻译内容
编译翻译文件:
task babel:compile重启应用:使新的翻译生效
添加新语言
初始化新语言文件:
task babel:init LANG=ja_JP DOMAIN=tianfu编辑翻译:
打开
locale/ja_JP/LC_MESSAGES/tianfu.po添加翻译内容
编译翻译文件:
task babel:compile更新代码:
在
src/translator.py的SUPPORTED_LANGUAGES中添加新语言根据需要修改
LocaleMiddleware或添加语言检测逻辑
在代码中使用翻译
使用 tfcommon.utils.translation
项目依赖 tfcommon 包提供的翻译功能。在代码中使用翻译时,需要:
导入翻译函数:
from tfcommon.utils.translation import gettext_lazy, gettext标记待翻译文本:
# 延迟翻译(用于模型字段等) name = gettext_lazy("User Name") # 立即翻译(用于视图、API 响应等) message = gettext("Operation successful")确保语言已激活:
通过
LocaleMiddleware自动为每个请求激活默认语言(zh_CN)如需动态切换语言,可调用
translation.activate(language_code)
打包部署
PyInstaller 打包配置
项目使用 PyInstaller 打包时,需要在 packup/tianlu_spc.spec 文件中配置国际化文件的打包。
国际化文件打包配置
在 Analysis 的 datas 参数中配置:
a = Analysis(
# ... 其他配置 ...
datas=[
# 打包国际化文件(locale 目录)
("../locale", "locale"), # (源路径, 目标路径)
],
# ... 其他配置 ...
)
配置说明:
("../locale", "locale"):将项目根目录的locale目录打包到可执行文件的locale目录格式为
(源路径, 目标路径)元组,源路径相对于 spec 文件位置这确保所有翻译文件(
.po和.mo)都被包含在打包结果中
工作原理:
PyInstaller 会将
../locale(相对于 spec 文件)目录下的所有文件复制到打包后的locale目录保持目录结构:
locale/zh_CN/LC_MESSAGES/tianfu.mo等文件都会被正确打包在打包环境中,
locale目录位于可执行文件同级的_internal目录中
运行时路径处理
翻译模块使用相对路径定位 locale 目录:
str(Path(__file__).parent.parent / "locale")
路径解析:
开发环境:
__file__指向src/translator.py,路径解析为项目根目录的locale打包环境:
__file__指向_internal/src/translator.pyc,路径解析为_internal/locale
这确保在开发环境和打包环境中都能正确找到翻译文件。
打包输出结构
打包完成后,国际化文件位于以下位置:
dist/tianlu_spc/
└── _internal/ # 内部文件目录
└── locale/ # 国际化文件(由 datas 配置打包)
├── zh_CN/
│ └── LC_MESSAGES/
│ ├── tianfu.po
│ └── tianfu.mo
└── en_US/
└── LC_MESSAGES/
├── tianfu.po
└── tianfu.mo
验证打包结果:
打包完成后,检查 dist/tianlu_spc/_internal/locale/ 目录,确认所有翻译文件都已包含。
注意事项
翻译文件编译:确保在打包前执行
task babel:compile,生成最新的.mo文件路径配置:如果修改了
locale目录的位置,需要同步更新 spec 文件中的datas配置测试验证:打包后应在目标环境中测试翻译功能是否正常工作
配置说明
Babel 配置 (babel/babel.cfg)
# https://babel.pocoo.org/en/latest/messages.html#extraction-method-mapping-and-configuration
[python: **.py]
配置 Babel 提取 Python 文件中的翻译标记。
Taskfile 变量 (babel/Taskfile.yml)
BABEL_CFG:Babel 配置文件路径POT_FILE:翻译模板文件路径DOMAIN:翻译域名称(tianfu)TRANSLATIONS_DIR:翻译文件目录PYBABEL:pybabel 命令(通过uvx执行)BABEL_SOURCES:待提取的源码路径
注意事项
修改 PO 文件后必须编译:只有编译后的 MO 文件才会被运行时使用。
默认语言:当前默认语言为中文(zh_CN),在
LocaleMiddleware中硬编码设置。翻译域:项目使用
tianfu作为翻译域,所有翻译文件都以此命名。提取源:当前 Babel 配置从
.venv/lib/site-packages/fitpy提取翻译,这是第三方库的翻译。如需提取项目自身代码的翻译,需要修改BABEL_SOURCES变量。语言检测:
get_supported_language()函数可以检测系统默认语言,但当前未在中间件中使用。
可以改进建议
动态语言切换:支持通过请求头(如
Accept-Language)或查询参数动态切换语言。项目代码翻译提取:配置 Babel 提取项目自身代码中的翻译标记,而不仅仅是第三方库。
翻译缓存:对于高频访问的翻译,可以考虑添加缓存机制。