Flask traitement multilingue, commutation et l'exception 404 Page Not Found
Dans ce billet, nous discutons de plusieurs conditions lors du traitement de la langue dans l'url, en utilisant une langue par défaut et en utilisant une liste des langues disponibles.
Comment implémenter Flask multilangue est expliqué dans la documentation Flask , voir liens ci-dessous. Mais ce n'est qu'un point de départ. Vous avez besoin d'une compréhension plus approfondie du processus pour traiter des cas particuliers comme le retour à une langue par défaut, le changement de langue et l'exception 404 Page Not Found.
Hypothèses
Dans le reste de ce post, nous utilisons un code de langue,'lang_code', qui est disponible dans l'url, c'est la première partie de l'url, par exemple :
https://www.example.com/en
https://www.example.com/en/login
Nous utilisons également Flask-Babel pour les traductions. J'utilise blueprints et je les enregistre dans create_app() comme suit :
# authentication (shared)
from shared.blueprints.auth.views import auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/<lang_code>/auth')
# pages (shared)
from shared.blueprints.pages.views import pages_blueprint
app.register_blueprint(pages_blueprint, url_prefix='/<lang_code>')
Nous utilisons g.lang_code comme valeur de la langue sélectionnée. Je n'utilise pas de variable de session pour mémoriser la langue, mais je compte plutôt sur la disponibilité de la langue dans chaque requête.
Flask traitement multilingue
La façon dont Flask gère le multilangage sur une requête est la suivante :
Tout d'abord, url_value_preprocessor() est appelé pour extraire la langue de l'exemple url :
@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
# pop lang_code from url and set g.lang_code
...
if 'lang_code' in values:
g.lang_code = values.pop('lang_code')
Ensuite, Babels locale_selector() est appelé pour fournir les traductions de la page, exemple :
@babel.localeselector
def get_locale():
return g.get('lang_code')
Enfin url_defaults() est appelé avant que la page soit construite pour remplacer <lang_code> par lang_code dans les urls, exemple :
@pages_blueprint.url_defaults
def add_language_code(endpoint, values):
# stuff g.lang_code in urls
...
values.setdefault('lang_code', g.lang_code)
C'est assez simple, mais comme vous pouvez le constater, des conditions peuvent se produire lorsque le code de langue est modifié, non disponible ou même non valide. La chose la plus importante est de s'assurer que g.lang_code est toujours mis à une langue valide avant que localeselector() et url_defaults() soient appelés. Un certain nombre de conditions sont examinées ci-dessous.
Flask traitement multilingue : le visiteur passe à une autre langue
Pour passer à une autre langue, nous pouvons utiliser un GET à la requête courante avec un paramètre lang_code supplémentaire :
<a href="{{ request.script_root + request.path }}?lc=en">English</a>
<a href="{{ request.script_root + request.path }}?lc=de">Deutsch</a>
Nous devons étendre la fonctionnalité url_value_preprocessor() pour supporter le changement de langue. Simplifiée, cette option ressemble à :
@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
...
request_lang_code = request.args.get('lc')
if request_lang_code:
g.lang_code = request_lang_code
...
Flask traitement multilingue : langue manquante dans l'url et langue par défaut
Ceci peut ne pas être une erreur parce que vous pouvez vouloir que le vistor tape l'url de votre domaine et soit"redirigé" vers les pages de langue par défaut. Mais cela peut aussi arriver si un visiteur tape une mauvaise url, un bot (de recherche) appelle une mauvaise url. Encore une fois, nous pouvons gérer cela dans le préprocesseur url_value_preprocessor(). Dans ce cas, nous réglons le lang_code au lang_code de la langue par défaut :
@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
...
if g.get('lang_code') is None:
g.lang_code = default_lang_code
Flask traitement multilingue : langue non supportée
Notre application ne supporte qu'un nombre limité de langues, par exemple :
available_lang_codes = ['en', 'de']
Encore une fois, nous pouvons gérer le cas de langage invalide dans le préprocesseur url_value_preprocessor(). Si la langue n'est pas valide, nous réglons le lang_code au lang_code de la langue par défaut :
@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
...
if 'lang_code' in values:
lang_code = values.pop('lang_code')
if lang_code not in available_lang_codes:
g.lang_code = default_lang_code
Flask traitement multilingue : Page introuvable (erreur 404)
Celui-ci m'a donné des maux de tête, il m'a fallu du débogage pour voir que le flux est différent dans ce cas. Ce qui se passe ici est que si aucun des blueprints ne correspond à l'url de requête, url_value_preprocessor() n'est jamais appelé. Par exemple, avec le blueprints montré précédemment, c'est une url valide :
http://127.0.0.1:8000/en/auth/login
mais cette url donne une exception 404 :
http://127.0.0.1:8000/en/auth/login/something
Que faire ici ? La réponse est de traiter cette condition dans Flask @before_request. Sur un flux normal, la fonction before_request() est appelée après ( !) url_value_preprocessor() :
@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
....
@app.before_request
def before_request():
....
Dans le cas d'une exception 404, url_value_preprocessor() n'est PAS appelé mais before_request() est toujours appelé :
@app.before_request
def before_request():
....
Normalement url_value_preprocessor() mettra g.lang_code à une valeur, un code de langue. Mais sur un 404, url_value_preprocessor() n'est pas appelé et g.lang_code n'est pas défini. Dans la fonction before_request() nous vérifions la valeur de g.lang_code. Si ce n'est pas le cas, nous pouvons traiter l'url de la requête nous-mêmes. Si la première partie est un code de langue valide, nous supposons que c'est ce dont nous avons besoin et mettons g.lang_code. Sinon, nous mettons g.lang_code à la langue par défaut. Ensuite, lorsque le gestionnaire 404 est appelé, la page peut être affichée dans la langue appropriée.
Résumé
Je n'ai pas utilisé une variable de session pour stocker la langue sélectionnée, mais plutôt compter sur la langue dans l'url. Cela fonctionne bien si nous gérons toutes sortes de conditions comme une langue manquante. Le plus important est de régler la langue en g avant d'effectuer tout autre traitement.
Liens / crédits
Flask Series: Internationalization
https://damyanon.net/post/flask-series-internationalization/
Flask-Babel
https://pythonhosted.org/Flask-Babel/
Using URL Processors
https://flask.palletsprojects.com/en/1.1.x/patterns/urlprocessors/
En savoir plus...
Babel Flask Multilanguage
Récent
- Masquer les clés primaires de la base de données UUID de votre application web
- Don't Repeat Yourself (DRY) avec Jinja2
- SQLAlchemy, PostgreSQL, nombre maximal de lignes par user
- Afficher les valeurs des filtres dynamiques SQLAlchemy
- Transfert de données sécurisé grâce au cryptage à Public Key et à pyNaCl
- rqlite : une alternative à haute disponibilité et dist distribuée SQLite
Les plus consultés
- Utilisation des Python's pyOpenSSL pour vérifier les certificats SSL téléchargés d'un hôte
- Utiliser UUIDs au lieu de Integer Autoincrement Primary Keys avec SQLAlchemy et MariaDb
- Connexion à un service sur un hôte Docker à partir d'un conteneur Docker
- Utiliser PyInstaller et Cython pour créer un exécutable Python
- SQLAlchemy : Utilisation de Cascade Deletes pour supprimer des objets connexes
- Flask RESTful API validation des paramètres de la requête avec les schémas Marshmallow