2014-09-23T16:38:06Z
Using Flask-Babel with Flask 0.10
One of the interesting problems that I had to address when porting my Flask Mega-Tutorial to Flask 0.10 was in supporting Flask-Babel. There is an issue when a "lazy" text generated with the lazy_gettext
function is flashed. This issue was reported more than a year ago and hasn't been addressed yet.
In this short post I will show you how I solved this issue for the Mega-Tutorial, as this solution is applicable to any Flask project.
The Problem
When you call lazy_gettext()
to make a lazily evaluated translated text Flask-Babel uses the services of another package called speaklater, also written by Armin Ronacher.
Speaklater supports lazily evaluated texts through a string-like class called _LazyString
. Instances of this class take a lookup function (provided by Flask-Babel) and the text to translate. Each time an instance of this class is evaluated to a string the lookup function is invoked with the text as argument, and its return value replaces the _LazyString
object. This works great in most cases, you can treat the _LazyString
instance as a regular string most of the time.
However, a common use of lazy texts is to flash them using Flask's flash()
function. For example, consider this example:
login_manager.login_message = lazy_gettext('Please log in to access this page.')
This is how Flask-Login is configured to show an error message when a user tries to access a protected page. When Flask-Login needs to show this message it flashes it, so that the application's template can pick it up. This text needs to appear in several languages, with the chosen language for a given user depending on the language configuration in the user's web browser. By making this text lazy the message will be auto-translated each time it is used.
The problem is that flashed messages are written to the user session, which in Flask 0.10 is serialized to JSON. But the JSON encoder does not know how to deal with the _LazyString
instance and fails.
The Solution
It was nice to find that Flask 0.10 allows applications to install a custom JSON encoder, so all I needed to do to address this bug is to create my own JSON encoder that forces the _LazyString
instance to render to a regular string. The code is shown below:
from flask import Flask
from flask.json import JSONEncoder
app = Flask(__name__)
class CustomJSONEncoder(JSONEncoder):
"""This class adds support for lazy translation texts to Flask's
JSON encoder. This is necessary when flashing translated texts."""
def default(self, obj):
from speaklater import is_lazy_string
if is_lazy_string(obj):
try:
return unicode(obj) # python 2
except NameError:
return str(obj) # python 3
return super(CustomJSONEncoder, self).default(obj)
app.json_encoder = CustomJSONEncoder
The default()
method of the JSON encoder is invoked for any complex objects that have no known translation. If I find that the obj
is a lazy string I just convert it to a string. I first try unicode(obj)
, which will only work in Python 2, and if that fails then I do it the Python 3 way, which is str(obj)
.
With this simple fix it is possible to flash Flask-Babel lazy texts with Flask 0.10, in both Python 2 and Python 3.
Miguel
#1 Andy said 2014-09-24T12:52:41Z
#2 Miguel Grinberg said 2014-09-24T14:22:56Z
#3 Andy said 2014-09-24T14:46:12Z
#4 Miguel Grinberg said 2014-09-24T15:52:00Z
#5 Andy said 2014-09-25T05:39:34Z
#6 Miguel Grinberg said 2014-09-25T16:52:38Z
#7 Andy said 2014-09-26T03:39:03Z
#8 Andy said 2014-09-27T02:39:11Z
#9 Jon said 2016-06-25T15:38:28Z
#10 Miguel Grinberg said 2016-07-14T17:59:16Z