- 后端
【免费下载链接】flask-admin
Simple and extensible administrative interface framework for Flask
本指南以 examples/babel 示例为骨架,完整讲解如何在 Flask-Admin 管理后台中集成 Flask-Babel,实现界面多语言切换:从前端?lang=参数与 Session 驱动的语言选择器,到后台翻译文件(.po/.mo)的生成、编译与维护,再到 Flask-Admin 内置的CustomDomain翻译域机制与 40+ 语言目录结构,帮助你为管理后台一键接入英、中、法、德、俄等语言。
一、示例概览:Babel 集成示例做了什么
examples/babel/README.md 是 Flask-Admin 官方示例仓库中的国际化演示,核心目的只有一个:展示如何用定制化的 Flask-Babel 版本把 Flask-Admin 翻译成不同语言。
在仓库中,这个示例由三部分构成:
| 文件 | 作用 |
|---|---|
| examples/babel/README.md | 示例说明与运行方式 |
| examples/babel/main.py | 完整的可运行应用:语言选择器 + 管理后台 + 两个示例模型 |
| examples/babel/pyproject.toml | uv依赖声明,内含flask-admin[sqlalchemy-with-utils,translation] |
此外,仓库根目录的 babel/babel.ini、babel/babel.sh、babel/babel.bat 与 babel/README.md 提供了翻译工作流脚本,后面第五节会专门讲解。
二、运行示例:用 uv 一键启动
该示例使用uv管理依赖与开发环境,因此不需要手工创建虚拟环境或单独pip install。
先克隆仓库并进入示例目录:
git clone https://github.com/pallets-eco/flask-admin.git cd flask-admin/examples/babel然后直接运行:
uv run main.pyuv会自动读取 examples/babel/pyproject.toml 解析依赖。该文件的关键声明是:
[project] name = "example-babel" version = "0.1.0" description = "Flask-Babel Integration Example." requires-python = ">=3.10" dependencies = [ "flask-admin[sqlalchemy-with-utils,translation]", ] [tool.uv.sources] flask-admin = { path = "../../", editable = true }两点值得注意:
- 依赖带
[translation]额外标记:只有安装了这个 extra,才会引入Flask-Babel与Babel工具链,flask_admin/babel.py 中的from flask_babel import Domain才能导入成功; [tool.uv.sources]指向仓库根目录:示例直接以可编辑(editable)方式依赖当前仓库的 flask-admin 源码,便于同步调试。
启动后,app.run(debug=True)会在本机 5000 端口启动开发服务器,浏览器访问http://127.0.0.1:5000/即可看到语言入口页,进入http://127.0.0.1:5000/admin/则是管理后台。
三、核心实现:语言选择器与应用的初始化顺序
examples/babel/main.py 是整个示例的灵魂,只有 88 行,却完整展示了 Flask-Admin + Flask-Babel + Flask-SQLAlchemy 三者协同的最小可行实现。
3.1 初始化顺序
from flask import Flask, request, session from flask_admin import Admin from flask_admin.contrib.sqla import ModelView from flask_babel import Babel from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config["SECRET_KEY"] = "secret" app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///db.sqlite" app.config["SQLALCHEMY_ECHO"] = False db = SQLAlchemy(app) admin = Admin(app, name="Example: Babel")注意这里的顺序是先初始化db与admin,再创建Babel实例。Flask-Babel 的locale_selector是惰性求值的,只要在第一个请求到达前注册即可,因此Babel(app, locale_selector=get_locale)放在后面完全没问题。三个组件的协作关系为:
Admin(app, name="Example: Babel")挂载管理后台,name决定导航栏标题;SQLAlchemy(app)提供 ORM 与数据表;Babel(app, locale_selector=get_locale)接管所有请求的 locale 判定。
3.2 语言选择器:Query 参数 + Session
def get_locale(): override = request.args.get("lang") if override: session["lang"] = override return session.get("lang", "en") babel = Babel(app, locale_selector=get_locale)这是示例中最核心的业务逻辑,工作机制分三步:
- 每次请求到达时,Flask-Babel 调用
locale_selector; - 若 URL 中携带
?lang=xx,将其写入session["lang"](Session 依赖 flask_admin/base.py 中同样要求的SECRET_KEY配置); - 返回
session.get("lang", "en"),首次访问无任何记录时默认英文。
get_locale返回值必须匹配某个翻译目录的 locale 名称,例如cs、de、zh_CN等,否则 Flask-Admin 会回退到英文原文。示例首页 main.py 的 index 路由 生成了 12 个语言入口链接:
<p><a href="/admin/?lang=en">Click me to get to Admin! (English)</a></p> <p><a href="/admin/?lang=cs">Click me to get to Admin! (Czech)</a></p> <p><a href="/admin/?lang=de">Click me to get to Admin! (German)</a></p> <p><a href="/admin/?lang=es">Click me to get to Admin! (Spanish)</a></p> <p><a href="/admin/?lang=fa">Click me to get to Admin! (Farsi)</a></p> <p><a href="/admin/?lang=fr">Click me to get to Admin! (French)</a></p> <p><a href="/admin/?lang=pt">Click me to get to Admin! (Portuguese)</a></p> <p><a href="/admin/?lang=ru">Click me to get to Admin! (Russian)</a></p> <p><a href="/admin/?lang=tr">Click me to get to Admin! (Turkish)</a></p> <p><a href="/admin/?lang=pa">Click me to get to Admin! (Punjabi)</a></p> <p><a href="/admin/?lang=zh_CN">Click me to get to Admin! (Chinese - Simplified)</a></p> <p><a href="/admin/?lang=zh_TW">Click me to get to Admin! (Chinese - Traditional)</a></p>这些链接全部指向/admin/?lang=xx,体现了“语言状态由 URL 触发、由 Session 保持”的切换模型:点一次链接后,Session 被写入对应语言,后续管理后台内所有页面(包括翻页、编辑、删除)都会持续使用该语言,直到再次带?lang=访问或 Session 过期。
3.3 数据模型与视图注册
示例定义了两个经典的一对多模型,均使用 SQLAlchemy 2.0 风格的Mapped/mapped_column类型注解:
class User(db.Model): id: Mapped[int] = mapped_column(Integer, primary_key=True) username: Mapped[str] = mapped_column(String(80), unique=True) email: Mapped[str] = mapped_column(String(120), unique=True) # flask-admin shows __repr__ output in its interface def __repr__(self): return self.username class Post(db.Model): id: Mapped[int] = mapped_column(Integer, primary_key=True) title: Mapped[str] = mapped_column(String(120)) text: Mapped[str] = mapped_column(Text) date: Mapped[DateTime] = mapped_column(DateTime) user_id: Mapped[int] = mapped_column(Integer(), ForeignKey(User.id)) user: Mapped[User] = relationship(User, backref="posts") def __repr__(self): return self.title视图注册与建表放在if __name__ == "__main__":块内:
if __name__ == "__main__": # admin.locale_selector(get_locale) admin.add_view(ModelView(User, db)) admin.add_view(ModelView(Post, db)) with app.app_context(): db.create_all() app.run(debug=True)第 82 行被注释掉的admin.locale_selector(get_locale)是历史遗留写法;在当前版本中 locale 选择完全由 Flask-Babel 的locale_selector参数接管,不需要再向 Admin 显式注册。
四、翻译机制原理:CustomDomain 与 translations 目录
Flask-Admin 的界面翻译并不依赖你额外配置任何东西——只要装了flask_babel,flask_admin/babel.py 会自动构建一个翻译域:
from flask_admin import translations class CustomDomain(Domain): def __init__(self) -> None: super().__init__(translations.__path__[0], domain="admin") @property def translation_directories(self) -> list[str]: view = get_current_view() if view is not None: dirname = view.admin.translations_path if dirname is not None: return [dirname] + super().translation_directories return super().translation_directories domain = CustomDomain() gettext = domain.gettext ngettext = domain.ngettext lazy_gettext = domain.lazy_gettext关键点拆解:
- 内置翻译包:
translations.__path__[0]指向 flask_admin/translations/ 目录,这是随 pip 包一起分发的一级翻译目录;domain="admin"使域名对应该目录下每个语言子目录中的admin.mo/admin.po; - 覆盖式翻译搜索路径:
translation_directories属性(来自 Babel 的Domain)会把view.admin.translations_path追加到内置目录之前。这意味着:如果你的Admin实例设置了translations_path,自定义翻译会优先于内置翻译被加载——这就是“定制化 Flask-Babel 版本”翻译 Flask-Admin 的底层通道; - 懒加载与 WTForms 修复:模块同时导出
lazy_gettext用于模板/字段定义阶段的惰性翻译;当检测到flask_babel不可用时,flask_admin/babel.py 会回退到一组gettext/ngettext/lazy_gettext透传实现和一个空Translations类,保证后台仍能正常渲染英文界面(WTForms 校验消息则由wtforms_domain = Domain(messages_path(), domain="wtforms")提供)。
4.1 仓库自带的 40+ 语言目录
仓库的 flask_admin/translations/ 下已内置 40 余种语言的翻译目录,例如de、es、fa(波斯语)、fr、pt、ru、tr、pa(旁遮普语)、zh_Hans_CN(简体中文)、zh_Hant_TW(繁体中文)等,每种语言下都有:
flask_admin/translations/<locale>/ └── LC_MESSAGES/ ├── admin.mo # 编译后的二进制翻译文件(应用实际加载) └── admin.po # 可编辑的源翻译文件以 flask_admin/translations/zh_Hans_CN/LC_MESSAGES/admin.po 为例,翻译头部声明了Language: zh_CN,内容如:
#: ../flask_admin/base.py:519 msgid "Home" msgstr "首页"每个msgid都带来源标注(文件与行号),方便追溯管理后台中哪一处文案对应哪条翻译。示例首页列出的en/cs/de/es/fa/fr/pt/ru/tr/pa/zh_CN/zh_TW12 种语言正是从这套目录中挑选的可展示子集。
4.2 自定义翻译目录的接入方式
如果你的团队需要为 Flask-Admin 定制文案(例如修改 “Create” 为自定义术语),可以创建自己的翻译目录并让 Admin 指向它:
admin = Admin(app, name="Example: Babel", translations_path="/path/to/my/translations")按 flask_admin/babel.py 的translation_directories逻辑,/path/to/my/translations/<locale>/LC_MESSAGES/admin.mo会被优先加载,未覆盖的文案自动回落到内置翻译,实现“部分覆盖”效果。
五、翻译工作流:提取、更新与编译
仓库根目录的babel/目录提供了完整的翻译维护脚本,适合为 Flask-Admin 项目本身贡献翻译或维护自己的翻译。
5.1 提取模板(babel.ini)
babel/babel.ini 声明了需要扫描的文件范围:
# Python [python: **.py] # Jinja2 [jinja2: **/templates/**.html] encoding = utf-8- 所有
.py源码按 Python 提取器处理; templates/下的.html按 Jinja2 提取器处理,并统一使用 UTF-8 编码。
5.2 一键脚本(babel.sh)
babel/babel.sh 封装了完整的提取、更新、编译流程:
#!/bin/sh uv run pybabel extract -F babel.ini -k _gettext -k _ngettext -k lazy_gettext -o admin.pot --project Flask-Admin ../flask_admin if [ "$1" = '--update' ]; then uv run pybabel update -i admin.pot -d ../flask_admin/translations -D admin -N fi uv run pybabel compile -f -D admin -d ../flask_admin/translations/Windows 用户可使用等价的 babel/babel.bat(核心命令同为pybabel extract,仅路径分隔符不同)。
各命令的作用:
| 命令 | 作用 |
|---|---|
pybabel extract -F babel.ini -k _gettext -k _ngettext -k lazy_gettext -o admin.pot --project Flask-Admin ../flask_admin | 扫描flask_admin源码,把_gettext、_ngettext、lazy_gettext标记的文案提取到admin.pot模板 |
pybabel update -i admin.pot -d ../flask_admin/translations -D admin -N | 用新模板更新所有语言的.po文件(-N不保留模糊匹配),并保留已有翻译 |
pybabel compile -f -D admin -d ../flask_admin/translations/ | 将.po编译为应用实际加载的.mo文件 |
babel/README.md 给出了三类角色的使用路径:
- 开发者改动文案后:运行
./babel.sh --update,同时完成提取、更新与编译; - 译者查找缺失翻译:运行
awk '/^msgid / {msgid=substr($0, 8, length($0)-8)} /^msgstr ""$/ {print msgid}' file.po,把file.po替换为目标语言文件,即可列出所有msgstr为空(尚未翻译)的条目; - 译者更新完 .po/.mo 后:运行
./babel.sh完成最终编译。
首次参与时先用uv sync --group docs同步开发环境,确保pybabel可用。
六、效果验证与常见问题
6.1 如何验证翻译生效
- 启动
uv run main.py; - 访问首页,点击任意语言链接(如
?lang=zh_CN); - 观察管理后台导航栏、按钮(如 Home/登录相关文案)是否变为简体中文;
- 由于语言写入了 Session,在后台内翻页、编辑、删除时语言保持不变;
- 用无痕窗口或清除 Cookie 后再访问,确认回退到默认英文
en。
6.2 常见问题排查
- 界面仍是英文,切换无效:检查
get_locale返回值是否与 flask_admin/translations/ 下某个目录名精确一致(如zh_CN而非zh-CN); .mo未更新:修改.po后必须执行pybabel compile(或./babel.sh),运行时加载的是admin.mo而非admin.po;- 提示找不到 Flask-Babel:确认依赖为
flask-admin[translation],flask_admin/babel.py 在缺失时会静默回退到英文直通实现,因此不会报错但也不会翻译; - 想覆盖内置文案:为
Admin设置translations_path指向自定义目录,优先级高于内置翻译(见 flask_admin/babel.py)。
七、小结
通过 examples/babel 示例可以看到,Flask-Admin 的国际化是一条开箱即用的路径:应用侧只需一个locale_selector回调 + 一行Babel(app, locale_selector=...);翻译侧依赖 flask_admin/babel.py 的CustomDomain自动加载内置 40+ 语言目录,并支持translations_path自定义覆盖;维护侧则有 babel/babel.sh 与 babel.ini 提供提取、更新、编译的完整工作流。无论是给现有后台增加一种新语言,还是为团队定制管理界面文案,这套组合都能以极小的代码量完成。
- 后端
【免费下载链接】flask-admin
Simple and extensible administrative interface framework for Flask
相关推荐
Flask-Admin 国际化实战:基于 Babel 的翻译文件维护与多语言集成指南
Flask Admin 国际化实战:基于 Babel 的翻译文件维护与多语言集成指南 Flask Admin 内置了一套基于 Babel 的翻译体系,内置 38
后端Beego 多语言支持实战:基于 go-i18n 的国际化与本地化集成指南
Beego 多语言支持实战:基于 go i18n 的国际化与本地化集成指南 本文以《build web application with golang》 第 1
文档教程gin-vue-admin国际化:多语言支持与i18n集成
gin vue admin国际化:多语言支持与i18n集成 前言:全球化时代的管理系统挑战 在当今全球化的商业环境中,企业管理系统需要面向不同国家和地区的用户。
后端前端认证鉴权低代码任务调度
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考