Litefs 国际化(i18n)
更新日期: 2026-10-21 适用版本: v0.9.0(开发中)
概述
Litefs 内置国际化支持,基于 Python 标准库 gettext 实现,零新增第三方依赖,提供:
locale 协商:解析
Accept-Language请求头,结合 URL 参数与 Cookie 选定本次请求的语言请求级 locale:
request.locale、g.locale与线程局部存储三处同步写入,视图内可直接取用翻译函数:模块级
gettext/ngettext与模板上下文注入的_/gettext/ngettext中间件:同步 / 异步双路径实现,响应阶段回写
Content-Language响应头
设计原则:
原则 |
实现方式 |
|---|---|
零行为变化 |
未启用 i18n 时注入恒等函数,模板渲染结果与未注入前完全一致 |
标准兼容 |
翻译文件遵循 gettext 标准目录结构,可直接用 |
请求隔离 |
locale 存于 |
显式优先 |
URL 参数 > Cookie > |
依赖隔离 |
|
快速开始
启用国际化
from litefs.core import Litefs
app = Litefs(
i18n_enabled=True,
i18n_supported_locales=['zh_CN', 'en'],
i18n_default_locale='zh_CN',
i18n_locale_dir='locales',
)
启用后框架会自动挂载 LocaleMiddleware,无需手工 add_middleware。
config.yaml 配置方式:
i18n_enabled: true
i18n_supported_locales: ['zh_CN', 'en']
i18n_default_locale: zh_CN
i18n_locale_dir: locales
i18n_cookie_name: locale
i18n_param_name: locale
手工挂载中间件
from litefs.core import Litefs
from litefs.middleware import LocaleMiddleware
app = Litefs()
app.add_middleware(
LocaleMiddleware,
supported_locales=['zh_CN', 'en'],
default_locale='zh_CN',
locale_dir='locales',
)
模板中使用
<h1>${_('Welcome')}</h1>
<p>${_('You have %s messages') % message_count}</p>
render_template() 会自动向模板上下文注入 _ / gettext / ngettext 三个函数。
视图中使用
from litefs.routing import get
from litefs.i18n import gettext, ngettext
@get('/index', name='index')
def index_handler(request):
# request.locale 由 LocaleMiddleware 写入
title = gettext('Welcome') # 使用当前请求的 locale
items = ngettext('item', 'items', len(items))
return {'title': title, 'items': items}
翻译文件组织
翻译文件遵循 gettext 标准目录结构:
locales/
├── zh_CN/
│ └── LC_MESSAGES/
│ └── messages.mo
└── en/
└── LC_MESSAGES/
└── messages.mo
locales/为i18n_locale_dir指定的根目录zh_CN/为 locale 目录名(下划线格式,不是zh-CN)messages.mo为翻译域(domain)对应的编译产物,默认域为messages
生成翻译文件
# 1. 提取源码中的可翻译字符串
xgettext -L Python -o messages.pot src/**/*.py
# 2. 创建并编辑翻译
msginit -i messages.pot -l zh_CN -o zh_CN.po
vim zh_CN.po
# 3. 编译为 .mo
msgfmt -o locales/zh_CN/LC_MESSAGES/messages.mo zh_CN.po
模板字符串需要在源码中显式标记,可使用 pybabel 的提取指令:
<% trans name="title">Welcome</% trans %>
或直接使用 Mako 的 Python 表达式形式 $_('Welcome')(_ 由框架注入)。
复数形式
.mo 文件中的复数条目 msgid 形如 单数\x00复数,由 msgfmt 根据 Plural-Forms 头自动生成:
ngettext('apple', 'apples', 3) # 中文返回「苹果们」
locale 协商
优先级
URL 查询参数 (?locale=) > Cookie > Accept-Language 请求头 > i18n_default_locale
来源 |
读取方式 |
说明 |
|---|---|---|
URL 参数 |
|
显式指定,优先级最高,如 |
Cookie |
|
由上一次响应自动回写,记住用户选择 |
请求头 |
|
浏览器自动发送 |
默认值 |
|
兜底 |
匹配规则
对每个候选 locale,按以下顺序匹配受支持列表:
精确匹配:
zh_CN命中zh_CN前缀回退:
zh_CN回退到zh未命中:继续尝试下一个候选,最终使用默认 locale
Accept-Language 解析规则:
按
q值降序排列,q值相同时保持请求头中的原始顺序(稳定排序)非法
q值记为0.0*通配符视为「接受任意语言」,直接采用默认 locale
示例:
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
若受支持列表为 ['zh_CN', 'en'],则 zh-CN 精确命中 zh_CN,最终 locale 为 zh_CN。
locale 规范化
normalize_locale() 统一不同书写形式:
输入 |
输出 |
说明 |
|---|---|---|
|
|
连字符转下划线,地区码转大写 |
|
|
地区码转大写 |
|
|
纯语言码转小写 |
|
|
3 位数字地区码补零 |
|
|
变体标识保持原样 |
请求级 locale
协商完成后,locale 同时写入三处:
位置 |
类型 |
用途 |
|---|---|---|
|
请求处理器实例属性 |
视图内直接读取 |
|
请求上下文全局对象 |
跨模块共享(无请求上下文时自动忽略) |
线程局部存储 |
|
翻译函数隐式取用,无需层层传参 |
@get('/index', name='index')
def index_handler(request):
print(request.locale) # 'zh_CN'
翻译目录切换
除 locale 外,翻译目录也支持请求级切换(set_current_locale_dir()),
适用于多租户场景——不同租户使用不同的翻译文件目录。
LocaleMiddleware 会在每次请求时把自身配置的 locale_dir 同步到线程局部存储,
因此 i18n_locale_dir 配置对模板翻译函数自动生效:
app = Litefs(
i18n_enabled=True,
i18n_locale_dir='/srv/tenants/acme/locales', # 模板翻译函数自动使用该目录
)
也可以在代码中手动切换(会覆盖中间件写入的值):
from litefs.i18n import set_current_locale_dir
set_current_locale_dir('/srv/tenants/acme/locales')
模板翻译函数注入
注入规则
inject_translations() 向模板上下文注入三个键:
键 |
对应函数 |
|---|---|
|
|
|
翻译单数形式文本 |
|
按数量翻译复数形式文本 |
两个关键约束:
不覆盖已有键(使用
setdefault)——调用方显式传入的同名变量优先级最高无 locale 时注入恒等函数——无请求上下文、未启用 i18n 或翻译表缺失时, 渲染结果与未注入前完全一致
# 调用方显式传入的 _ 不会被覆盖
handler.render_template('a.html', value='x', _=my_func)
在模板中切换语言
## 简体中文
${_('Welcome')}
## English
${translator.gettext('Welcome')}
需要固定语言的场景(如多语言邮件),可显式构造翻译器:
from litefs.i18n import Translator
translator = Translator('locales', 'en')
translator.gettext('Welcome')
配置项
配置项 |
默认值 |
说明 |
|---|---|---|
|
|
是否启用国际化(启用后自动挂载中间件) |
|
|
默认 locale |
|
|
受支持的 locale 列表 |
|
|
翻译文件根目录 |
|
|
存放 locale 的 Cookie 名称 |
|
|
存放 locale 的查询参数名称 |
API 参考
litefs.i18n.locale
名称 |
说明 |
|---|---|
|
规范化 locale 字符串 |
|
解析请求头,返回 |
|
在受支持列表中匹配,返回命中的 locale 或 |
|
完整协商流程 |
|
通配符常量 |
litefs.i18n.translator
名称 |
说明 |
|---|---|
|
获取(并缓存)翻译表,缺失时回退 |
|
清空翻译表缓存(测试与热重载场景) |
|
绑定固定 locale 的翻译器,提供 |
|
请求级 locale 读写 |
|
请求级翻译目录读写 |
|
安装到 |
|
请求级翻译(模块级函数) |
|
请求级复数翻译(模块级函数) |
litefs.i18n.template
名称 |
说明 |
|---|---|
|
向模板上下文注入翻译函数 |
|
构造独立翻译函数字典 |
最佳实践
locale 目录名统一用下划线。
zh_CN而不是zh-CN,否则 gettext 找不到文件。.mo文件要进版本库或构建产物。部署时确保i18n_locale_dir指向实际存在的路径。翻译缺失不报错。
gettext对未收录的字符串原样返回,缺失.mo时同样回退原文, 因此可以安全地「先上线英文,再逐步补翻译」。CDN 缓存要带
Content-Language。多语言页面必须按该头区分缓存,否则会出现语言串台。后台任务显式指定 locale。Celery 等无请求上下文的场景,用
Translator(dir, locale)显式绑定, 不要依赖线程局部状态。翻译字符串用占位符。使用
%(name)s形式而非字符串拼接,避免语序调整时破坏语义。
相关文档
:doc:
mail-service—— 邮件发送(多语言邮件模板):doc:
middleware-guide—— 中间件系统总览:doc:
configuration—— 配置项完整列表:doc:
celery-integration—— Celery 任务队列集成