fastapi-babel icon indicating copy to clipboard operation
fastapi-babel copied to clipboard

Allow overriding how the locale is set

Open aceisace opened this issue 11 months ago • 0 comments

Analog to the flask-babel locale_selector, these small, but effective changes provide a much higher user-comfort as they are not restricted to only the integrated locale-selection method.

For instance, some users may want to save the locale of a logged-in-user which has a preferred locale in the request session or even as a cookie. Currently, it is not possible except to override the locale inside a view function like this:

from app.main import babel
async def some_function(abc: str):
    babel.locale = "de"

Which is fairly un-pythonic and adds a lot of repetitive code when you have a lot of view functions. Instead of this, one can, optionally override this behaviour like this:

In main.py:

# code omitted before
...

def get_locale_from_session(request: Request) -> str:
    # If user logged in, we stored locale in session:
    session_locale = request.session.get("locale")
    print(f"session_locale: {session_locale}")
    if session_locale and session_locale in settings.SUPPORTED_LANGUAGES:
        return session_locale

    # Fallback to Accept-Language header, or Babel default locale
    accept_language = request.headers.get("accept-language", "")

    if accept_language:
        languages = [lang.split(";")[0] for lang in accept_language.split(",")]

        # Check if any of the user's preferred languages match supported ones
        for lang in languages:
            if lang in settings.SUPPORTED_LANGUAGES:
                print("get_locale: ", lang)
                return lang

    return babel.default_locale  # Fallback if no match is found

# before 
app.add_middleware(BabelMiddleware, babel_configs=babel_configs, jinja2_templates=templates)
# after
app.add_middleware(BabelMiddleware, babel_configs=babel_configs, jinja2_templates=templates, locale_selector=get_locale_from_session)

Which does the following; if a user is logged in, take their preferred locale to set the babel locale. If no user was found (i.e. not logged in), use the accept-language header instead. Of course, the user is free to set the locale in whichever way they want, e.g. database queries, cookies, sessions etc.

On my end, I have done some testing with some fairly good results. I kindly ask to merge the changes into main after doing some tests on your end too and having these changes available in the next release in the near future.

aceisace avatar Feb 09 '25 14:02 aceisace