Internationalization
SQLAdmin can translate its interface into multiple languages using
Babel. Compiled catalogs for Azerbaijani (az),
German (de), English (en), Russian (ru) and Turkish (tr) ship with the
package.
Translation is optional. Install the extra to enable it:
pip install sqladmin[i18n]
Enabling i18n
Pass an I18nConfig to Admin. Setting language_switcher also renders a
language switcher in the navigation:
Example
from sqladmin import Admin
from sqladmin.i18n import I18nConfig
admin = Admin(
app,
engine,
i18n_config=I18nConfig(
default_locale="en",
language_switcher=["en", "az", "de", "ru", "tr"],
),
)
I18nConfig accepts:
default_locale: locale used when nothing else is detected.language_cookie_name: cookie that persists the visitor's choice ("language"by default; set toNoneto disable).language_header_name: request header inspected for a preferred locale ("Accept-Language"by default; set toNoneto disable).language_switcher: locale codes shown in the switcher.Nonehides it.
How the active locale is resolved
For every request the locale is resolved in this order, highest priority first:
- The
?lang=query parameter set by the switcher (persisted to the cookie). - The language cookie.
- The
Accept-Languageheader, negotiated against the supported locales. default_locale.
Only locales in sqladmin.i18n.SUPPORTED_LOCALES are accepted.
Translating your own strings
The translation functions resolve against the visitor's active locale, so they work for flash messages and any custom output:
Example
from sqladmin import Flash
from sqladmin.i18n import gettext as _
class UserAdmin(ModelView, model=User):
async def after_model_change(self, data, model, is_created, request):
Flash.success(request, _("User saved."))
For strings defined at import time, use lazy_gettext, which defers
translation until the string is rendered:
Example
from sqladmin.i18n import lazy_gettext as _
SAVE_LABEL = _("Save")
In custom templates the functions are installed by the Jinja i18n extension,
so both {{ _("...") }} and {% trans %} blocks work:
Example
{% extends "sqladmin/list.html" %}
{% block content %}
{{ super() }}
<p>{{ _("Export is ready.") }}</p>
{% endblock %}
Plurals
ngettext selects the form using the Plural-Forms rule compiled into each
catalog, so languages with more than two forms (Russian, Polish, Arabic, ...)
are handled correctly:
Example
from sqladmin.i18n import ngettext
ngettext("%(num)d row", "%(num)d rows", 5) % {"num": 5}
Adding a language
New languages are shipped with the package rather than registered at runtime. The set of interface strings is small and stable, so bundling a compiled catalog keeps every install working out of the box with no per-application setup, and the translations stay quality-controlled. To add one, translate a catalog and open a pull request.
SQLAdmin marks its translatable text with _("...") in the Jinja templates.
The workflow uses Babel through the Makefile
targets (each wraps the equivalent pybabel command). The extraction config
lives in the [tool.babel] table of pyproject.toml — run these commands from
the repository root:
# 1. Refresh the .pot template and sync every existing catalog
make i18n-extract
# 2. Create a catalog for the new locale, e.g. French
make i18n-init LOCALE=fr
# 3. Translate the messages in
# sqladmin/translations/fr/LC_MESSAGES/admin.po
# then remove the `#, fuzzy` line from its header
# 4. Compile every .po into the binary .mo files loaded at runtime
make i18n-compile
make i18n-init creates the fr/LC_MESSAGES/ directory for you, so nothing
needs to be created by hand. Finally, add the locale code to
SUPPORTED_LOCALES in sqladmin/i18n.py and commit both the .po (source)
and .mo (compiled) files.
Note
Babel marks new or changed catalog entries #, fuzzy, and
pybabel compile skips a catalog whose header is fuzzy. After reviewing a
translation, delete the #, fuzzy line so make i18n-compile picks it up.
Note
Model field labels and WTForms validation messages are not covered by this
layer. Field labels come from your SQLAlchemy columns (use column_labels
to customise them), and WTForms ships its own validator translations.