Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to select locale when using Flask's app factory pattern and Flask-Babel 3+ #232

Open
nickjj opened this issue Aug 6, 2023 · 4 comments

Comments

@nickjj
Copy link

nickjj commented Aug 6, 2023

Hi,

I've been using Flask-Babel 2.x for a long time and when using @babel.localeselector along with the app factory pattern I was able to successfully select a locale from the current user.

Now I've updated to Flask-Babel 3.1.x and when I do this:

# extensions.py

from flask import request
from flask_babel import Babel
from flask_login import current_user


def get_locale():
    if current_user.is_authenticated:
        return current_user.locale

    return request.accept_languages.best_match(["en", "es"])


babel = Babel(locale_selector=get_locale)
# app.py

from extensions import babel


def create_app():
    app = Flask(__name__)

    babel.init_app(app)

My application loads successfully but when I set the locale on the current user, it's never selected. It always uses the default locale which I have defined with BABEL_DEFAULT_LOCALE = "en".

I've also tried to set the parameter like this instead of at Babel() instantiation time:

    babel.init_app(app, locale_selector=get_locale)

It results in the same issue of the new locale never being selected.

If I drop a print statement into the get_locale function, it never gets called. I also went as far as putting a print statement on the first line within this function:

def get_locale() -> Optional[Locale]:

And it doesn't print out when I reload any page.

I noticed a while back someone had the same issue which they mentioned on StackOverflow https://stackoverflow.com/questions/76539307/flask-babel-in-flask-app-factory-method-not-translating-text-but-does-translate but it didn't receive any replies. This SO question mentions everything works fine when you're not using an app factory pattern which is what the documentation has. There's no examples in the docs for using an app factory pattern and defining this parameter.

This project's integration tests at https://github.com/python-babel/flask-babel/blob/master/tests/test_integration.py don't define locale_selector but they do use an app factory pattern. The tests at https://github.com/python-babel/flask-babel/blob/master/tests/test_force_locale.py do define locale_selector but don't use an app factory pattern. This leaves defining locale_selector when using an app factory an untested code path which may have a bug?

Any suggestions on how to get this to work? Thanks.

@nickjj
Copy link
Author

nickjj commented Aug 7, 2023

I was able to narrow down the scope of this issue.

In my get_locale function, if current_user.is_authenticated always returns False during tests when using Flask's test client. In a browser when I check things manually, everything works. However all of my tests work from the authentication bits, as in I'm only able to access logged in routes when logged in. Inside of my test that logs in, user.is_authenticated() returns True too.

What could have changed between Flask-Babel 2.X vs 3.X to cause this behavior?

In 2.X I was using this and everything worked (tests included):

def locale():
    if babel.locale_selector_func is None:
        @babel.localeselector
        def get_locale():
            if current_user.is_authenticated:
                return current_user.locale

    return request.accept_languages.best_match(["en", "es"])

@TkTech
Copy link
Contributor

TkTech commented Sep 5, 2023

Hi!

I believe your only issue is because you're passing locale_selector into the Babel() constructor. I mention in the documentation:

If an application is passed, it will be configured with the provided
arguments. Otherwise, :meth:init_app can be used to configure the
application later.

Because you're using the app factory pattern, you need to pass locale_selector into init_app(). The configuration passed to Babel() is ignored unless you also passed an app.

I will probably change this behaviour to either use the globals as defaults (might have threading implications) or to raise an exception if you didn't also pass in an app.

@nickjj
Copy link
Author

nickjj commented Sep 5, 2023

Because you're using the app factory pattern, you need to pass locale_selector into init_app().

I tried both methods. The issue's description covers applying it through init_app too. It was the same result.

@UrbanoJVR
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants