Skip to main content

Request-safe gettext localization and locale negotiation for Starlette

Project description

starlette-i18n

Request-safe gettext localization and locale negotiation for Starlette applications.

Requirements

  • Python 3.11+ (CI validates Python 3.11, 3.12, 3.13, and 3.14)
  • Starlette newer than 1.2.0 and below 2.0

Install

uv add starlette-i18n
pip install starlette-i18n

Quick start

Compile gettext catalogs before starting the application, then load them once:

from starlette.applications import Starlette
from starlette.responses import PlainTextResponse

from starlette_i18n import LocaleMiddleware, TranslationCatalog, gettext

catalog = TranslationCatalog.load("locales", domain="messages", default_locale="en")
app = Starlette()
app.add_middleware(LocaleMiddleware, catalog=catalog)


async def homepage(request):
    return PlainTextResponse(gettext("Success"))


app.add_route("/", homepage)

The selected Locale is also available as request.state.locale. The public translation helpers are gettext, ngettext, gettext_lazy, and get_locale.

Locale selection

LocaleMiddleware selects one catalog for each HTTP request in this order:

  1. A valid configured locale cookie (locale by default).
  2. The request's Accept-Language header.
  3. The catalog's configured default locale.

Header values are parsed as a q-weighted priority list and resolved using RFC 4647 lookup. Exact complex tags work: a zh-CN request selects a zh_CN catalog directory, while pt-BR can fall back to a pt catalog. Invalid values and q=0 entries are ignored. A wildcard falls back to the configured default when no explicit supported locale matches.

Responses include Content-Language. The middleware adds configured selection inputs (Cookie and Accept-Language by default) to Vary so shared caches do not serve the wrong representation.

Configure another cookie name, or disable cookies entirely:

app.add_middleware(LocaleMiddleware, catalog=catalog, cookie_name="language")
app.add_middleware(LocaleMiddleware, catalog=catalog, cookie_name=None)

ASGI and Jinja safety

The active locale is stored in a ContextVar and is reset after every request, including exceptions. Concurrent requests therefore cannot leak translations into one another.

Do not install gettext functions into a shared Jinja environment per request: Jinja's installation mutates global environment state. Pass a request-local callable instead:

html = template.render(request=request, _=request.state.locale.translate)

Then use {{ _("Success") }} in the template. See examples/jinja_app.py.

Examples

uv run --with uvicorn examples/basic_usage.py
uv run --with 'uvicorn[standard]' uvicorn examples.uvicorn_runner:app --reload
uv run --with gunicorn --with uvicorn gunicorn -c examples/gunicorn_conf.py examples.gunicorn_runner:app
curl -H 'Accept-Language: ru' http://127.0.0.1:8000/

Migrating from 2.x

3.0 is a breaking release. It requires Python 3.11+, moves the package to src/, uses pyproject.toml and uv, and replaces LocaleDefaultMiddleware, LocaleFromCookieMiddleware, LocaleFromHeaderMiddleware, and the old ordering-dependent LocaleMiddleware with the single LocaleMiddleware shown above. Load catalogs with TranslationCatalog.load() instead of load_gettext_translations().

Further documentation

Development

uv sync --all-groups
make lint
make test
make build

License

starlette-i18n is distributed under the Apache License 2.0. Report vulnerabilities through the security policy.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

starlette_i18n-3.0.0.tar.gz (64.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

starlette_i18n-3.0.0-py3-none-any.whl (12.5 kB view details)

Uploaded Python 3

File details

Details for the file starlette_i18n-3.0.0.tar.gz.

File metadata

  • Download URL: starlette_i18n-3.0.0.tar.gz
  • Upload date:
  • Size: 64.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for starlette_i18n-3.0.0.tar.gz
Algorithm Hash digest
SHA256 af4557c07158b6b785ba6b18bff108ef4a914f041f9ac52cb424cab85a76880f
MD5 8f529c2c32b9e672709e9edd8750761d
BLAKE2b-256 b0991c82ee6457fce547fc1d87cd6be92a5dd6f6f12a9154754c28d67cf6e440

See more details on using hashes here.

Provenance

The following attestation bundles were made for starlette_i18n-3.0.0.tar.gz:

Publisher: publish.yml on bigbag/starlette-i18n

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file starlette_i18n-3.0.0-py3-none-any.whl.

File metadata

  • Download URL: starlette_i18n-3.0.0-py3-none-any.whl
  • Upload date:
  • Size: 12.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for starlette_i18n-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 62319160d97090736abb888d7f4bc2e3218b5599dc6515916145dc5d3da7654a
MD5 9db008f61a5cccd375fb8cb799c5e749
BLAKE2b-256 0d9e20630439800ad775b17766f9ed93f393a37812104f052c2da8b150d02828

See more details on using hashes here.

Provenance

The following attestation bundles were made for starlette_i18n-3.0.0-py3-none-any.whl:

Publisher: publish.yml on bigbag/starlette-i18n

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page