Construire un site web multilingue Flask avec Flask-Babel
Il n'existe pas beaucoup d'exemples de multilinguisme Flask. Nous suivons ici les suggestions de la Flask documentation.
Avec une seule langue, il n'y a pas vraiment de problème, nous oublions simplement le reste du monde et construisons notre Flask application en une seule langue. Nous commençons à avoir mal à la tête quand le site doit supporter plusieurs langues. Qu'est-ce qu'un site web supportant plusieurs langues exactement ? Combien de langues seront prises en charge, et quelles langues ? Pour l'anglais, il existe par exemple en-GB et en-US. Quelles parties du site Web doivent être disponibles dans toutes les langues ? Quelles sont les parties d'un site web de toute façon ? Je me limiterai ici à répondre aux questions les plus triviales. Pour une bonne introduction, vous pouvez également consulter par exemple le guide du plugin Wordpress Polylang, voir ci-dessous.
Lorsque vous construisez un site web sans supporter plusieurs langues, vous utilisez des urls pour votre contenu, lorsque vous voulez ajouter plus de langues, vous devez maintenir toutes les urls, c'est-à-dire ce qui a déjà été créé. D'un point de vue SEO, vous feriez mieux de commencer par tous les composants multilingues en place, même si vous doutez maintenant si vous allez ajouter une autre langue.
Sélection de la langue
Lorsque la langue est dans un cookie (session), l'affichage correct de la langue peut être un problème lorsque les cookies sont désactivés. Avec la langue dans le domaine / url ce problème est éliminé et aussi le site est plus convivial SEO.
Option 1 : langue dans l'extension du domaine
example.com
example.es
example.pl
Exemple : toyota
https://www.toyota.de
https://www.toyota.es
Option 2 : langue dans le sous-domaine
en.example.com
es.example.com
pl.example.com
Exemple : cnn
https://edition.cnn.com
https://cnnespanol.cnn.com
Option 3 : langue dans le chemin de l'url
example.com/fr
example.com/es
example.com/es /pl
URL traduites
Les urls traduites sont très conviviales, mais introduisent aussi plus de complexité :
https://edition.cnn.comentertainment
https://cnnespanol.cnn.com/seccion/entretenimiento /entertainment /seccion/entretenimiento
Certains sites Web n'ont pas d'urls traduites :
https://www.tesla.com/nl_NL/model3/design#battery
https://www.tesla.com/de_CH/model3/design#battery
Si nous voulons avoir des urls de langue traduites, et pourquoi ne le ferions-nous pas, alors nous avons aussi besoin de plusieurs points finaux pour une fonction de vue :
@pages.route('/<lang_code>/about-us', methods=['GET', 'POST'])
@pages.route('/<lang_code>/uber-uns', methods=['GET', 'POST'])
def about():
return render_template(...)
Bien sûr, nous voulons que ce soit automatisé... difficile.
Faire des choix
Pour le moment, je me concentre sur un site web avec le'language id' dans le chemin de l'url, voir option 3 ci-dessus, et je ne m'occuperai pas des urls traduites. Parce que nous utilisons Flask, nous utilisons Flask-Babel pour nos traductions. Certains textes, comme les blogs, les catégories, les tags, sont dans la base de données. Nous y reviendrons plus tard.
Vous pouvez vouloir un site Web avec la langue principale sans l''id de langue' dans le chemin de l'url et les autres langues avec l''id de langue' dans le chemin de l'url. Je ne sais pas vraiment comment faire cela pour Flask le moment et je crois aussi que cela complique beaucoup les choses, pensez aussi au référencement. En supposant que vous voulez ajouter une autre langue après quelques années, vous ne pouvez pas changer vos urls. résumé : je pense que ce n'est pas une mauvaise idée de commencer par la langue dans le chemin url.
Dans une première implémentation, j'ai utilisé un cookie pour la sélection de la langue, maintenant je dois supprimer ceci et utiliser la langue dans l'url. Heureusement, la Flask documentation donne de bonnes informations sur l'internationalisation. Je vous suggère de lire ceci (je l'ai fait plusieurs fois). J'ai beaucoup blueprints, pour la page d'accueil view.py j'ajoute ce qui suit à la vue :
...
home_blueprint = Blueprint('home', __name__)
# lang_code in urls
@home_blueprint.url_defaults
def add_language_code(endpoint, values):
values.setdefault('lang_code', g.lang_code)
@home_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
url_lang_code_items_values = get_url_lang_code_items_values()
url_lang_code_default_item_value = get_url_lang_code_default_item_value()
g.lang_code = url_lang_code_default_item_value
if values:
if 'lang_code' in values:
if values['lang_code'] in url_lang_code_items_values:
g.lang_code = values.pop('lang_code', None)
else:
pass
...
La fonction get_url_lang_code_items_values() retourne une liste de lang_codes : en, nl, es, et la fonction get_url_lang_code_default_item_value() retourne en, l'anglais est donc la langue par défaut. Puis dans __init__.py j'enregistre la maison blueprint:
from .blueprints.home.views import home_blueprint
app.register_blueprint(home_blueprint, url_prefix='/<lang_code>')
Que se passe-t-il si on tape une url sans chemin, ou une url totalement aléatoire ? Vous obtiendrez un message d'erreur :
TypeError: homepage() got an unexpected keyword argument 'lang_code'
La Flask documentation ne donne pas de solution, mais après quelques maux de tête (de nouveau) je pense que j'ai trouvé une solution pour résoudre ce problème en utilisant le gestionnaire before_request. Dans ce gestionnaire, je regarde l'url de la requête. Le chemin de cette url est divisé en plusieurs parties. La première partie doit être notre langue. Si la première partie est dans notre liste de langues, alors continuez. Si la page ne peut pas être trouvée Flask générera une 404, page non trouvée, ce qui est bien. Si la première partie n'est pas notre liste de langues, alors le gestionnaire before_request retourne une url de redirection vers la page d'accueil de la langue par défaut.
Après l'implémentation, le site a été affiché sans styles et j'ai reçu des messages d'erreur étranges dans la console d'outils Developer. La solution était d'exclure le répertoire statique. Voici donc ce qui se passe dans le gestionnaire before_request :
@app.before_request
def before_request():
# try to handle missing lang_code in url interceptor
url_lang_code_items_values = get_url_lang_code_items_values()
url_lang_code_default_item_value = get_url_lang_code_default_item_value()
# check for a valid url = starts with /lang_code/
request_path = request.path.strip('/')
request_path_parts = urlparse(request_path).path.split('/')
if request.method in ['GET'] and len(request_path_parts) > 0:
request_path_part_0 = request_path_parts[0]
# do nothing with static urls !!!
if request_path_part_0 != 'static' and request_path_part_0 not in url_lang_code_items_values:
# fucked up url
redir_url_parts = []
redir_url_parts.append( request.url_root.strip('/') )
redir_url_parts.append( url_lang_code_default_item_value.strip('/') )
redir_url = '/'.join(redir_url_parts)
return redirect(redir_url)
Cela fonctionne mais comment changer la langue ?
Dans le template base.html, j'ai un sélecteur de langue déroulant :
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ language_selected }}
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ request.path }}?lc=en_US">{{ _('EN') }}</a>
<a class="dropdown-item" href="{{ request.path }}?lc=nl_NL">{{ _('NL') }}</a>
<a class="dropdown-item" href="{{ request.path }}?lc=es_ES">{{ _('ES') }}</a>
</div>
</li>
Ensuite, nous déclenchons la Babel langue localeselector lorsque la langue est changée :
@babel.localeselector
def get_locale():
request_lc = request.args.get('lc')
if not request_lc:
if not 'lang_code' in g:
# use default
g.lang_code = 'en'
request_lc = 'en_US'
else:
if g.lang_code == 'es':
request_lc = 'es_ES'
elif g.lang_code == 'nl':
request_lc = 'nl_NL'
else:
request_lc = 'en_US'
else:
# set g.lang_code to the requested language
if request_lc == 'nl_NL':
g.lang_code = 'nl'
elif request_lc == 'es_ES':
g.lang_code = 'es'
else:
request_lc = 'en_US'
g.lang_code = 'en'
#sys.exit()
session['lc'] = request_lc
return request_lc
Ok, ça marche maintenant mais doit être optimisé. En outre, il y a encore deux problèmes en ce moment :
- Lorsque la langue est changée, ceci n'est pas affiché dans l'url par la suite.
Vous devez aller sur une nouvelle page. Après avoir changé la langue avec la liste déroulante, l'url n'affiche pas la langue dans l'url immédiatement, seulement au clic suivant. Peut-être que je devrais déplacer le Babel code vers le gestionnaire before_request ? - Beaucoup d'appels à url_defaults sur chaque appel
que je voudrais avoir le code :
@home_blueprint.url_defaults
@home_blueprint.url_value_preprocessor
dans le __init__.py au lieu de dans blueprint views.py mais cela ne fonctionne pas.
Pourquoi est-ce que je veux ça ? Parce que je n'aime pas dupliquer le même code et je vois beaucoup d'appels à @auth_blueprint.url etc. Je crois qu'elles devraient être faites une fois dans __init__.py mais peut-être que j'ai tort.
Liens / crédits
Flask Series: Internationalization
https://damyanon.net/post/flask-series-internationalization/
Flask-multilang-demo
https://github.com/DusanMadar/Flask-multilang-demo
How to Easily Create a Multilingual WordPress Site
https://www.wpbeginner.com/beginners-guide/how-to-easily-create-a-multilingual-wordpress-site/
Internationalized Application URLs
https://flask.palletsprojects.com/en/1.1.x/patterns/urlprocessors/
Multilingual flask application
https://stackoverflow.com/questions/3420897/multilingual-flask-application
Set languages and locales
https://docs.microsoft.com/en-us/windows-hardware/customize/mobile/mcsf/set-languages-and-locales
En savoir plus...
Babel Flask Multilanguage
Laissez un commentaire
Commentez anonymement ou connectez-vous pour commenter.
Commentaires (1)
Laissez une réponse
Répondez de manière anonyme ou connectez-vous pour répondre.
From where 'get_url_lang_code_items_values()' and 'get_url_lang_code_default_item_value()' come from?
Récent
- Utilisation de Ingress pour accéder à RabbitMQ sur un cluster Microk8s
- Galerie vidéo simple avec Flask, Jinja, Bootstrap et JQuery
- Planification de base des tâches avec APScheduler
- Un commutateur de base de données avec HAProxy et HAProxy Runtime API
- Docker Swarm rolling updates
- Masquer les clés primaires de la base de données UUID de votre application web
Les plus consultés
- Utiliser PyInstaller et Cython pour créer un exécutable Python
- Réduire les temps de réponse d'un Flask SQLAlchemy site web
- Utilisation des Python's pyOpenSSL pour vérifier les certificats SSL téléchargés d'un hôte
- Connexion à un service sur un hôte Docker à partir d'un conteneur Docker
- Utiliser UUIDs au lieu de Integer Autoincrement Primary Keys avec SQLAlchemy et MariaDb
- SQLAlchemy : Utilisation de Cascade Deletes pour supprimer des objets connexes