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
- 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_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
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_patternsfunction in your root URLconf.
- Failing that, it looks for the
LANGUAGE_SESSION_KEYkey 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_NAMEsetting. (The default name is
- Failing that, it looks at the
Accept-LanguageHTTP 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
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.pyas shown below:
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
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.
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.