Tag Archives: multi-language

Multi-language content spots with django-chunks and friends

Update March 3: django-better-chunks has now been patched, so there is no need to use my project fork anymore.

In my previous post I wrote about how to use flatpages in Django for serving static content pages. The flatpages module only deals with full pages however, so if you would like to include static content on a more fine-grained level, or have multiple content spots per template, you need to look elsewhere.

This is where django-chunks, or one of the many projects forked from it (e.g. django-better-chunks and django-flatblocks), comes to the rescue. django-chunks allows you to for example create a content spot called “home_page_right” in admin, and then include it in your template like this:

<div id="right">
    {% chunk "home_page_right" %}
</div>

The chunk tag also accepts an optional second parameter that specifies a cache timeout in seconds, e.g. 3600 for an hour’s caching.

So far so good, but a theme of some of my previous posts has been multi-language support, and unfortunately django-chunks is lacking this. Luckily, django-better-chunks was created to remedy just that. Unluckily though, while adding language support they also broke the cache support.

That being said, django-better-chunks is the only module I’ve found that does support multiple languages, so I felt it would be the best project for me to build on. To fix the broken caching I’ve created yet another fork of this project: django-better-chunks-devdoodles. Hopefully the patch can be merged into the original project at some point. Here’s how to get django-better-chunks (or my fork) up and running:

Step 1 — download and install
Download django-better-chunks or, perhaps preferably until the caching is fixed, django-better-chunks-devdoodles and install in your Python/Django path.

Step 2 — edit settings.py
As usual, you need to add LocaleMiddleware to your middleware classes if you want Django to automatically choose which language to use:

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.locale.LocaleMiddleware',
)

Then add contrib.sites (there by default), admin, and chunks to your installed applications:

INSTALLED_APPS = (
    ...
    'django.contrib.sites',
    'django.contrib.admin',
    'chunks',
)

contrib.sites is needed since django-better-chunks connect the content chunks to a site through a ForeignKey relationship. Admin is needed to add and edit content spots.

Optionally, if you want to use another caching backend for chunks than the default local-memory cache (locmem://), then you add it here too. To use a local memcached cache on the default port instead, add:

CACHE_BACKEND = 'memcached://127.0.0.1:11211/'

Step 3 — activate admin in urls.py
Uncomment the three lines needed to activate admin in urls.py.

Step 4 — sync database
Create database tables:

python manage.py syncdb

Step 5 — create spots in admin
At this point all that remains is to create the content spots in the Chunks section in admin (/admin/chunks/chunk/) and start using them from your templates. Make sure to load the chunk tag library first using:

{% load chunks %}

Finally, beware that the help text in the admin UI gives as language examples ‘sv-se’ and ‘de-de’, which often won’t work well with Django’s automatic language detection. This is because Django by default almost always resolves to base languages (e.g. ‘en’) and not sublanguages (e.g. ‘en-us’), since most of the languages defined in the LANGUAGES setting in global_settings.py are defined only as base languages.

Static content with django-multilingual flatpages

While working on a multilingual Django project I encountered the need to have pseudo-static pages, e.g. an about page or FAQ page, translated into multiple languages. In my earlier post I wrote about how to use the i18n tag library in templates to handle translations, and although this approach would work for static pages too it would not be a perfect fit.

Django comes bundled with the flatpages application, which rather cleverly hooks into the 404 errors generated by Django when it cannot find a page and maps the requested URL to a database list. If there’s an entry for the requested URL, it shows the page stored in the database for that URL instead of the 404 page.

The bundled flatpages application has no inherent multi-language support, and I was pretty close to adapting it for my needs before Google came to the rescue. Obviously, this had already been done by someone, and it’s distributed as a part of the django-multilingual module, which is a generic module for having translated fields in Django models. Here’s what I did to get it up and running, based on the steps described on the project wiki.

Step 1 — install django-multilingual
Check out the Subversion trunk for the project as described in the wiki, and make the checked out module available for Python somehow. I just copied the multilingual sub-folder to my project folder as if it were my own application.

Step 2 — edit settings.py
Add the list of languages you want to support to settings.py, and mark English as the default language (through its tuple index). The LANGUAGES setting is actually already defined in global_settings.py, but I don’t want to support all those languages so I override it.

LANGUAGES = (
    ('en', 'English'),
    ('sv', 'Swedish'),
)
DEFAULT_LANGUAGE = 1

Add the multilingual context processor to TEMPLATE_CONTEXT_PROCESSORS. This setting is not included by default in your settings.py file, but the first four core processors below are set as default in the global settings (for reference, see here and here):

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.core.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
    'multilingual.context_processors.multilingual',
)

Add the middleware classes in the order listed below to support language detection and for the actual mapping of 404s to flatpages to be triggered. Curiously, the FlatpageFallbackMiddleware is not mentioned in the official installation instructions, but you can deduce that it’s needed by its mentioning in the original flatpage documentation and, of course, the fact that nothing happens without it.

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.locale.LocaleMiddleware',
    'multilingual.middleware.DefaultLanguageMiddleware',
    'multilingual.flatpages.middleware.FlatpageFallbackMiddleware',
)

Add admin and the multilingual apps to INSTALLED_APPS:

INSTALLED_APPS = (
    ...
    'django.contrib.admin',
    'multilingual',
    'multilingual.flatpages',
)

Step 3 — activate admin in urls.py
Uncomment the three lines needed to activate admin in urls.py.

Step 4 — sync database
Create all database tables needed for admin and flatpages:

python manage.py syncdb

Step 5 — create template
Create a ./flatpages/ sub-folder in your template-root directory, and create a default.html template in it. This is the default template used for displaying the flatpages, but it can be overridden in admin (see the next step). Its context is populated by a flatpage variable with two fields: title and content. An example template is available here.

Step 6 — create pages in admin
Go to the flatpages section in your admin application (/admin/flatpages/multilingualflatpage/) and create all the pages and translations you want to serve using the flatpages application.

Step 7 — done!
We’re done! As before, you can test your work by modifying LANGUAGE_CODE in settings.py or changing the preferred-languages setting in your web browser.

Multi-language support in a Django project

The Django documentation on internationalization describes how to add multi-language support to your application. As it took me a few tries to get it right, here’s a rundown of what I did to add it to an existing project.

Step 1 — update settings.py
In settings.py, make sure that USE_I18N is set to True, which is the default. The LANGUAGE_CODE setting controls the default language for the site, so if you only need to support one language you set it here.

Then add the LocaleMiddleware class to your MIDDLEWARE_CLASSES, which for a clean project might look like this:

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.locale.LocaleMiddleware',
)

Note that the order of the middleware classes makes a difference. LocaleMiddleware is needed for Django to select the user-preferred language from data in the request (i.e. cookies or the Accept-Language http header).

Step 2 — add translation keys to templates
Every template that should have translation support must load the i18n tag library using the {% load %} template tag. Once loaded, you can use the {% trans %} tag to mark a string for translation. A minimalistic hello-world template could look like this:

{% load i18n %}
{% trans "Hello" %}

Step 3 — create language files
Once your templates are set up, it’s time to create the language files. This step depends a bit on which operating system you’re running. I use Ubuntu Linux, so that’s what I’ll cover.

You can have language files local to an application as well as global for all your site. To set up language files for e.g. English and Swedish, move to the root of your project (or the root of the application), and run:

mkdir locale
django-admin.py makemessages -l en
django-admin.py makemessages -l sv

Note that you need to create the locale directory manually before running makemessages, otherwise you will get an error message. Also, if you get this error message:

Error: errors happened while running xgettext on __init__.py
/bin/sh: xgettext: not found

… it’s because your Linux distribution is missing the xgettext program. In Ubuntu, it’s provided by the gettext package:

sudo apt-get install gettext

Now that the language files are set up as ./locale/<language>/LC_MESSAGE/django.po, you can edit them and provide translations of the “Hello” key for each locale. When you’re done, they need to be compiled to .mo files before Django can use them:

django-admin.py compilemessages

Step 4 — done!
That’s all! You can try changing the LANGUAGE_CODE setting to switch between languages, or change the preferred-languages setting in your web browser (under Tools->Options->Content->Languages in Firefox), and Django should adapt automatically.