How to add translations for django website (python, js, templates and models) p. 5
06.02.2025
17.03.2025
19 minutes
178
0
0
0
0
Introduction
You won't believe it, but I've wanted to write this article for a long time. Do you know why? Because this is the first thing I did for my site and the first thing I figured out. In this article, we will translate my search results parsing site. And I will try to cover all the possible problems and situations that can happen to you when you also want to localize your site.
How does it work
When localizing, you need to understand how exactly you want to display a different version of the language. And it is a good idea to display this directly in the URL. There are options:
- Create a separate site with a corresponding domain. Like example.de. It is too expensive and impractical.
- Add a subdomain. Like ru.example.com
- Add a directory. Like example.com/en/
- Or use the URL parameter example.com?loc=en
Each of these options has its pros and cons. And I am not one of those who can talk about it. Everything is described in detail here, in the Google blog. I only tried the option with directories because ... well, I have one server and a limited budget. Therefore, in this article, you will find this particular method of displaying translations.
In any case, translating a site to Django can be divided into 4 conditional groups. These are string translation in Python, Django model translation, string translation in templates, and string translation in JavaScript code.
Translations of strings for a website can be divided into the following stages:
- Mark (highlight) the necessary strings for translation.
- Collect all marked (highlighted) strings.
- Write translations for them
- Compile a special *.mo files
Basic setup
But before we start selecting and translating, we need to tweak our site a little bit. And first of all, we'll connect the corresponding middleware. Its main role is redirects to the corresponding localized pages and insertion of those translation strings that are needed for the current site locale.
In the settings.py file:
You also need to write down the default language code, which is also the current one, and prepare a list of supported localizations (translations) of the site. Also add the global variable USE_I18N. The last point is not required, it is enabled by default, but I just like everything to be explicitly configured. Also in the settings.py file, somewhere at the end, add:
Since this site is, in fact, a regular web application, that is, the content will not be published there, I can afford to write all the necessary translations through Google Translate.
The final touch will be to indicate which paths we want to localize. In this case, it makes no sense to localize all the API or admin paths, and even more so the allauth paths. This is what will remain untouched. That is, we will localize our frontend. These are the about, contacts, home pages, and the email confirmation and password reset pages.
In the Website/urls.py file, add the following lines:
By the way, the i18n_patterns function can only be used in the main urls.py. That's it.
The preliminary setup is complete. Now you can check how it works. Go to my site (when I place this site on the hosting, a link will appear here), and if you do this in parallel with me, then opening a browser at localhost:8000, you should be redirected to localhost:8000/en/.
You can go to any language version of your site if, of course, you added it to LANGUAGES. Of course, there is nothing to look at yet, but be patient until the translations. Everything will be \( ̄︶ ̄*\))
Select lines for translation
We need to specify what, in fact, needs to be translated, and for each part of the site, be it a template, or python code, or javascript code, it will be different.
Translations in python code
In order to mark lines for translation, you need to import the following functions: gettext and gettext_lazy.
Next, you just need to wrap the line you need in gettext or gettext_lazy. I'll note right away that it's not possible to wrap the gettext function around every line. For example, a variable or calculated values. That is:
In this example, django will be able to detect only the first line, "Usual string". It will ignore all the rest.
I also imported a function such as gettext_lazy. What is it so special, and when to use it? In general, the only difference between them is that the second one takes a line from the *.po file only when this line is used on the server. At least that's what is written on the official django website.
This function should be used when you need to translate django models and their attributes and descriptions for the admin panel. But in 90% of cases, gettext will be enough for you. Plus, as I mentioned earlier, to make models international, you will need something more than the built-in django utilities.
At the current moment of development of the site, SearchResultParser, there is not a single line on the backend that would need to be translated. And if your project has such lines, then just wrap them in gettext and move on.
Translations in templates
To translate text in templates, you only need to know three tags:
At the beginning of the template, you will need to load a special module, i18n. Every time you need to translate some text in the template, load the module. Even if those templates inherit from another template that has already loaded i18n.
After the i18n module has been loaded. You have two options. The first is to use a single-line translation . The second is to use a multi-line translation . The difference between them is that between the tags and everything will be translated, including other tags. Stay tuned.
Now let's select all the lines needed for translation in app.html, base.html, about.html and contacts.html. I will show you an example of what the processed template will look like; the Frontend/templates/Frontend/about.html file:
Translations in JS files by Django
Configuring translations of JavaScript code is probably the most difficult and tedious. Although at first glance, there seems to be a tutorial, and nothing out of the ordinary is required. But more on that later. Let me first show you how to configure JS translations via Django.
The first thing you need to do is to include the corresponding paths in Website/urls.py. Note that if you use the i18n_patterns function, you also need to include the JavaScriptCatalog class there.
The JavaScriptCatalog class will generate a mini-js library for us with a catalog of translation strings. Yes, this will reduce the page loading speed a little, so in the future you will need to remember to cache it. Now connect it in the base.html template, and we are ready to translate.
The translation backend setup is ready. Now we need to mark the strings we want to translate. So, we only need the gettext() function. Let me show you an example of one of my functions, onReqeustPasswordReset:
Also in JSX you will need to wrap the gettext function in brackets like these {}. Example from the fetchProfile function:
And that's basically it, we're ready, and we can collect the strings and compile them into *.mo files. Let's see what we got. Here's the original page, in English:

And here is the page translated into Russian. Can you find anything strange?

Yeah, not everything is translated. Even more, django couldn't find my strings to translate. That's why I couldn't write the translation. Why is that? Most likely because of React, it's compiled into one package, and it's used, not the components I wrote.
I searched the Internet to solve this problem and found a guy who had a similar problem. This dude made a corresponding post on Medium about how to make the makemessages command find strings. In short, he had to get into the Django source code and add the --language=python parameter when the djangojs domain was specified. For some reason unknown to me, xgettext finds more strings to translate when the python language flag is specified. ¯\(°_o)/¯
In any case, this method doesn't suit me, it's too unreliable. And breaking Django just to translate a couple of strings... No, I'd rather implement the translation of JS files through React. More on that in another chapter. But for now...
Collect, Translate and Compile Translations
After setting up translations of templates, python and javascript code (using django), you need to collect all the marked strings into *.po files. Having previously created a locale directory in each application that has strings for translation. After this, you need to run the following command:
If you have also translated the lines in JS, then you need to run another command. In this command, we use the -d flag with the value djangojs. This is a mandatory flag if you want this to work.
Please also use the -i or --ignore flag too. Look, I use Node.js and ReactJS. They are all in the node_modules directory and not only them, actually. If you don't set this flag, django will responsibly check every single fucking file in your project, and there will be tens of thousands of them. And it will take time, so I warned you.
As a result of these two commands, we will get the files django.po - from the first command and djangojs.po - as a result of the second command. There will be lines like this:
I just want to make a couple of comments when you start doing translations. Don't touch anything inside msgid, that's the first thing. And second, when translating multi-line elements (those that start with "\n") after msgid, msgstr should also start with "\n".
In any case, if there are errors during compilation, they are very informative and you will have no problem solving them by yourself.
The last stage of translations is their compilation into *.mo files. To compile translations, regardless of whether they are from a template, python or js, use the following command:
Congratulations! We have finished translating our site, and now it can support many other languages, regardless of what and where these strings are located. But if the quality of the translations of JS files does not suit you and you have encountered the same problem as me, then continue reading the next chapter about translating JS strings using the i18next react module.
Translations in JS files, via i18next framework
Unlike translating JS files via Django, setting up React for this is much more difficult. But finding all the necessary strings is guaranteed. Let's install the necessary packages first:
Let's analyze each package that we installed:
- i18next - a functional core for translations
- react-i18next - a link between React and i18next
- i18next-browser-languagedetector - allows you to determine the current user language in the browser and change it
- i18next-http-backend - is responsible for delivering translations to the end client
- i18next-parser - collects all the lines for translations
Now all this will need to be configured. Let's start with the parser and create a file called i18next-parser.config.js in the Frontend directory, this is where you have the node_modules directory. Then paste the following content.
Compared to the original, I changed it a lot.
- First, I changed the lexer, {lexers:} for JS files.
- Second, I added locales, {locales:}, for English, Russian, Belarusian, German, Spanish and French.
- Third, I changed the path to save the results, {output}. I put all the translation files in the static directory. The question is, why? When I will place this site on the server, all static files should be served by this server. Accordingly, all these files will have to be moved to another location. And Django already has a built-in command for copying all static files to this location - collectstatic. That's right, including our localizations.
- Fourth, I set all the values for the separators to false. I did this because I do not use their translation functions, as demonstrated in their tutorial. I wrap strings and sentences in the appropriate function for translation instead of inventing some special keys for each case.
The next thing we need to do is to create a file, i18n.js next to index.js. And insert the following content.
In this file we configure the i18n module. We define how we will deliver static files to the end user (Backend module) and how we will detect the user's language (LanguageDetector module). In the second case, it is important to specify that 'path' is first, because this is how we specify the language change event.
Now, import this component:
At the very top, because we will need to use it in other modules
Now, using the EmailVerify.js component as an example, I will show how to mark the lines that need to be translated. First of all, we import the translation function:
Next, in the component function, we use the hook:
And now, to wrap the necessary string for translation:
As you may have noticed, I am sending the t function further as an argument to the onConfirmEmail handler. I did this because translations are needed not only in react components but also in regular handlers and functions. I couldn't find another way to globally define a translation function, so in each function that needs translation, I define an additional parameter - t.
Full example:
Now mark all your lines that need translation. Of course, I won't dump everything here, but you will find the already translated version of the site in this archive.
Let's collect all the lines I highlighted in translation files. This is why we installed the i18n-parser module. And to make it easy for us to continue developing our site, we will write a separate script for performing translations in package.json.
You only need to specify where and what to search using a special pattern. ** - means search in all folders and subfolders. *.js - means search for any files with the .js extension
Let's run the script, and it will find all the lines for translations for us and create all the previously specified localization files.
You will find all translation files in the path static/locales/LOCALE/translation.json. And here is an example of a generated localization file for the Russian language:
Next, let's look at the console output and what the page looks like when translated into German.

As you can see from the console, we made an XMLHttpRequest (or XHR) request twice to get German and English localization. Why twice and not once? That's because we specified in i18n.js, fallbackLng: 'en' . That is, if we fail to get German localization, we will use the default one.
You can also see how the LanguageDetector module worked and noticed the change in the user's language. That's it, the JS code translations are complete.
Translations in django models
Unlike string translations, translating django model fields involves creating additional migrations for the translatable model. How does it work? It's pretty simple: if a field needs to be translated into another language, a copy of the field is created, but with the locale prefix it needs to be translated into. And then create and apply the migration.
When and under what circumstances might this be useful? This is actually a very simple question, to which I will answer this way. You have an article, and you want to write it in 2 languages. WELL, and you need two different titles for one article. This is not possible using such built-in django commands as makemessages and compilemessages.
Well, let's set up model translations. First, install the modeltranslation package:
Connect the installed application in settings.py. It is best to connect it first in the list.
In order for this module to know for which languages it needs to create translated models, you must have the languages specified in LANGUAGES (see the beginning of the article). Next, you will need to create a file named translation.py in the directory of the application whose models you want to translate.
Here is an example of the contents of this file:
In this file, we specify for which model we want to make translations and which fields we want to translate. At the end we simply register the specified translated model.
All that remains is to make a database migration and apply these migrations.
We could finish here, but let me show you the modified admin.py file to display the translated model in the django admin panel.
If we want to support 2 languages, for example, English and Russian, then each translation field will have three options, one original plus two more with prefixes *_en and *_ru. The main thing to remember is not to use the original field but its options with prefixes. That's why I exclude these fields from the admin panel.
Epilogue
In this article, we have discussed all possible cases where you will need to make a translation. Translations in templates or python code are not difficult. Although setting up and creating translations for JS code is also easy, you just need to take into account that it works well with vanilla JS. With everything else, difficulties and inaccuracies may arise.
About model translations. If I were you, I would try to avoid this. But Dim, how can we write posts in several languages? After all, many duplicates of empty pages will be created. For example, an article at /ru/articles/art1 will have a variant /en/articles/art1. And if I wrote an article in Russian at the first address, then there should also be an article at the second one. After all, I will not be able to make another article with an identical URL. And, in fact, it is not necessary, I tell you. We write the same article, but at a new address, for example, /en/articles/art1_but_in_english/, and from the page /en/articles/art1, we set up a 301 redirect to it. Here's an example.
Well, in conclusion, I want to add that translation is not an easy and long matter. Even now I'm sitting and waiting for a miracle to happen, maybe the article will translate itself. In any case, here is the updated version of the site (archive), with all the translations and settings. See you in the next part.
\( ̄︶ ̄*\))
Comments
(0)
Send
It's empty now. Be the first (o゚v゚)ノ