Featured Articles
Article in Mobile App Development, User Manual categories.
Supporting Multiple Languages In Django — Part 1
Fueled's backend development team will teach you how to support multiple languages in Django based projects and be a Multilingual Super Hero!
Imagine! You’ve got this awesome app served by a RESTful API through Django/DRF and suddenly a requirement comes in. The client wants to support multiple languages in Django. They come to you with two requests:
- We want this app and CMS (admin panel) in Chinese
or
- We want this app and CMS (admin panel) in German
And if you’ve got this humongous code-base not written with all the ugettext
and ugettext_lazy
stuff in mind, then Boy! You’re in danger!
In this post, we’ll see how you can support multiple languages in Django based projects and be a Multilingual Super Hero!
The journey to be any hero isn’t easy. So, we’ll also see certain gotchas that would be your shield in supporting internationalization and localization in your projects. Of course, there is the Django documentation, but one might feel lost in all the details that make it quite overwhelming. So, this post is a primer to get you started with supporting localization in your projects.
NOTE: This is by no means a comprehensive tutorial. Although we’ll have a primer for you to explain most of the things that are needed for supporting internationalization and translation in your projects, there are various other things that are provided with Django for more complex cases. For more information, please refer to the Django Translation Docs.
There are two kinds of data in the app that we’ll need to handle: static data (translations for all the fields, error messages etc. that the app already has) and dynamic data (custom data input by the user in the app). We’ll see how to best handle both of these.
Preparing your project for Internationalization and Localization
The first and foremost thing to enable translation is to tell Django what is the list of languages it should support, where does it find the translation for static data and what is the default language it should fall back on (in case there is no translation available for the requested language)
Gotcha #1: The LocaleMiddleware
should always come before CommonMiddleware
and after SessionMiddleware
With the settings in place, we’ve told Django the information that it needs to enable translations.
Retrieving Language Preference from the client’s request
Now, we need to decide how to track the language the client wants to communicate in. There are different ways to get the language preference in an HTTP request.
In order to retrieve the language preference from the client’s request,LocaleMiddleware
tries to determine the user’s language preference by the following algorithm:
- First, it looks for the language prefix in the requested URL. This is only performed when you are using the
i18n_patterns
function in your root URLconf. - Failing that, it looks for the
LANGUAGE_SESSION_KEY
key in the current user’s session. - Failing that, it looks for a cookie. The name of the cookie used is set by the
LANGUAGE_COOKIE_NAME
setting. (The default name isdjango_language
.) - Failing that, it looks at the
Accept-Language
HTTP header. This header is sent by your browser and tells the server which language(s) you prefer, in order by priority. Django tries each language in the header until it finds one with available translations. - Failing that, it uses the global
LANGUAGE_CODE
setting.
For the REST APIs, I found Accept-Language
header a much cleaner way to accomplish the task. For enabling multiple languages in Django admin panel, we’ll prefer i18n_patterns
function and modify our root urls.py
as shown below:
The i18n_patterns
will automatically prepend the currently active language code to all URL patterns defined within i18n_patterns()
. So, all your admin URLs, with the current configuration having zh-cn
and en
activated, will have URLs as:
NOTE: The * above is a regular expression indicating anything after admin. For whatever language you want the admin panel to be accessible, the use of corresponding language code in the URL can help you in accessing that.
With prefix_default_language=False
set, the default language code will not be prepended (en
in this case), and you can access the admin route at just /admin
. Although you can do this to all the URLs, for the API endpoints, we prefer to supply this bit of information in the Accept-Language
header, which seems more cleaner approach for API endpoints.
What does the LocaleMiddleware help with?
LocaleMiddleware
is the secret sauce in the Translation Machinery. It does the following:
For the request part, it parses it and decides what translation object to install in the current thread context.
For the response part, it formats the URL with appropriate activated language if i18n_patterns
are used and set the Content-Language
header in response for the client to know what language is used in the response for parsing as shown.
Now we just need to tell, what to translate.
What to translate when supporting multiple languages in Django?
We need to translate two kinds of data:
- Static data that includes model names, field names of models, error messages, etc. that are static in the application.
- Dynamic data that majorly includes the field value in models that is input by the user.
In the next article we’ll see how we can support translation in both of these cases and expand on various gotcha moments!
For more from our engineering team, check out our iOS ReactNative Guide.