Une application multi-langues, peut nécessiter d'avoir des urls distinctes pour chaque langue supportée. Les moteurs de recherche peuvent alors indexer le site dans chaque langue. Cette fonction n'est pas supportée en standard par le module d'internationalisation de Django mais heureusement django-localeurl permet de l'intégrer très facilement à un site existant. Je vous propose un aperçu rapide de cette application.
En prérequis à cet article, je vous invite à lire sur le blog de Valentin Bourgoin, un tutoriel très intéressant sur l'internationalisation d'applications.
Fonctionnement
L'idée est de préfixer les urls du projet avec le code langue. Si une page de votre projet, est accessible via l'url /ma-page. django-localeurl la rend alors disponible en anglais sur /en/ma-page, en français sur /fr/ma-page et ainsi de suite pour toutes les langues supportées par l'application.
Lorsque le navigateur demande une page, automatiquement celle-ci est traduite dans la langue définie dans l'url. Une page existe donc pour chaque langue avec une url différente ce qui permet aux robots de Google, d'indexer le site dans les différentes langues. Les fans de SEO apprécieront surement.
Transparent pour le projet
L'implémentation est très bien pensée puisque l'utilisation de django-localeurl est complètement transparente pour le projet. Inutile de retoucher les urls.py.
Cela s'appuie principalement sur 2 techniques:
- l'utilisation d'un middleware, qui va analyser l'url de chaque requette et basculer dans la langue demandée si l'url commence par un code langue. Le code langue est alors supprimé de l'url pour que l'urls.py appelle la bonne vue.
- un monkey-patch pour modifier le comportement de la fonction reverse de django. cette technique permet d'affecter une nouvelle implémentation à une fonction standard de Python.
django_reverse = None
def patch_reverse():
"""
Monkey-patches the urlresolvers.reverse function. Will not patch twice.
"""
global django_reverse
if urlresolvers.reverse is not reverse:
django_reverse = urlresolvers.reverse
urlresolvers.reverse = reverse
if settings.USE_I18N:
patch_reverse()
Le nouveau reverse préfixe l'url retrouvée avec le code langue. Vous devez bien entendu utiliser reverse et le templatetag url. Si vos urls sont en dur, elles ne prendront pas en compte la langue.
Le code précédent est défini dans le models.py, django l'exécutera au moment de la vérification du modèle. Tout sera automatique avec le serveur de développement. Cela n'est pas forcément vrai avec un site servie par Apache en WSGI. Dans ce cas, il est nécessaire de forcer l'appel au patch assez tôt dans le chargement de l'application, dans le settings.py par exemple.
Installation et paramétrage
L'installation et le paramétrage sont bien documentés. Le minimal est d'inclure le code ci-dessous dans le settings.py
LANGUAGE_CODE = 'en'
USE_I18N = True
_=lambda x:x
LANGUAGES = (
('en', _('English')),
('fr', _('French')),
...
)
MIDDLEWARE_CLASSES = (
'localeurl.middleware.LocaleURLMiddleware', #avant CommonMiddleware
'django.middleware.common.CommonMiddleware',
...
)
INSTALLED_APPS = (
'localeurl', #1er de la liste
...
)
Il existe plusieurs options dont les 2 suivantes qui sont très utiles:
#Pour ne pas prendre en compte la langue dans les urls des media
LOCALE_INDEPENDENT_MEDIA_URL = True
#Expressions régulières pour ignorer d'autres urls
import re
LOCALE_INDEPENDENT_PATHS = (
re.compile('^/data/'),
)
Sitemaps
Qui dit indexation par les moteurs de recherche, dit sitemap. django-localeurl propose une nouvelle classe permettant de générer le sitemap pour une langue donnée. On hérite alors de LocaleurlSitemap plutôt que de Sitemap.
from localeurl.sitemaps import LocaleurlSitemap
from myapp.models import MyModel
class MyModelSitemap(LocaleurlSitemap):
def items(self):
return MyModel.objects.all()
...
#urls.py
sitemaps = {'fr': MyModelSitemap('fr'), 'en': MyModelSitemap('en')}
urlpatterns = patterns('',
url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
)
Voila, c'est tout pour la présentation de cette application bien pratique, que j'ai utilisée dernièrement sur un projet. Le code parait bien stabilisé et elle est assez facile à intégrer à un projet existant. Elle est devenue une de mes applications favorites.