How to build a multi-language dashboard with Streamlit

Streamlit + Gettext + Localazy

Rafał Rybnik
Apr 7 · 6 min read

I’ve created a Streamlit app for my recent article about keyword monitoring. It is used to search keywords in Google and is quite useful. As search results and SEO topics, in general, are location-dependent, I thought it would be useful to translate the app interface into several languages.

Live demo: http://keywordicebreaker.herokuapp.com/

In theory, making another language version does not seem difficult. After all, it is enough to translate individual labels and descriptions into another language and by using a dictionary to map these.

In practice, this simple approach is time-consuming and error-prone. It is definitely better not to reinvent the wheel and to use established solutions. One of them is Gettext — a universal set of tools for producing multi-lingual messages.

From this article, you’ll learn how to translate Streamlit application by using Gettext and Localazy.

Project start

https://github.com/fischerbach/streamlit-gettext

A code repository is attached to this article. The individual steps are separated into successive branches. The README.md file contains instructions for getting the dashboard up and running.

The result should be as follows:

After providing the API key:

Perhaps, we could translate messages into other languages and use if statements to change them according to the user’s will, but this approach would be tedious and error-prone. Therefore we will first extract all strings from the code to work on them separately from the business logic.

Gettext

GNU gettext is a universal set of tools for producing multi-lingual messages. It provides a framework to support translated message strings. It supports many programming languages 😉, including Python. The gettext module comes shipped with Python standard library. The best thing about gettext is that it will help us seamlessly extract text messages into separate files.

We will prepare English, Polish and German language versions. First, we need to prepare the directory structure.

mkdir -p locales/{de,pl}/LC_MESSAGES

Then, we should extract the messages from the code.

/Library/Frameworks/Python.framework/Versions/3.8/share/doc/python3.8/examples/Tools/i18n/pygettext.py -d base -o locales/base.pot dashboard.py

To find the pygettext.py file, you can use the command: locate pygettext.py .

That will generate in the locales folder a base.pot file with strings taken from the dashboard.py file.

Unfortunately, generated base.pot does not contain any strings. To fix this, we need to modify dashboard.py by marking the messages for translation.

After generating the base.pot again, strings appear in it.

branch: step1

First translations

Now let’s prepare the first translations. Copy and rename the base.pot into each language folder:

cp locales/base.pot locales/de/LC_MESSAGES/base.po
cp locales/base.pot locales/pl/LC_MESSAGES/base.po

Let’s modify the individual language files:

To use translation in our program, we need to generate the MO files. MO files are binary data files that are parsed by the Python gettext module and used in the program.

msgfmt -o locales/de/LC_MESSAGES/base.mo locales/de/LC_MESSAGES/base
msgfmt -o locales/pl/LC_MESSAGES/base.mo locales/pl/LC_MESSAGES/base

Now we can modify the dashboard.py file to display reports in different languages.

At the beginning of the file add:

From now on, the user can select a language from a dropdown. The interface will refresh every label that is processed by _() function:

Two labels in the app are. translated, it’s time for the rest. The procedure is the same. Every time a string with a message appears in the source code, surround it with the function _(‘This is a string’). Once this is done, generate the POT file from the beginning, copy it to the locales of each language, translate it and generate the binary files.

branch: step2

Translation management

So in the next iteration of our solution, we will add functions to the dashboard that generate POT and MO files. You have all the changes here:

Localazy

Localazy is an awesome piece of software that makes the usually awful translation experience bearable and even almost enjoyable. It supports many frameworks and file formats and provides CLI tools for build automation. My favourite features are the possibility of cooperative translation and automagic management of changes in translated files.

So let’s integrate our report with Localazy. First, create a Localazy account and install Localazy CLI. Then, create a new application.

Then, select POT files from available file formats.

You will see a template configuration file localazy.json. Copy it to the project main folder.

Remember to modify the locales folder path. Go to your app on Localazy and add some new languages.

Now you can generate the PO files again and load them into Localazy:

localazy upload

After a while, you will see a list of phrases to translate in each language of your application.

And the cherry on the top, a machine translation comes with each phrase.

Once all the translations have been accepted or created, you can download them into your application and re-generate binary MO files.

Unfortunately, for the changes to be loaded into the Streamlit app, the instance must be reset. If I can resolve this inconvenience, I will update the article.

Final effect:

branch: step3

f-strings problem

The project uses f-strings quite extensively. Unfortunately, we cannot use them as arguments of _() function, gettext will return an error. I describe how to solve this issue in the previous article:

https://netlabe.com/how-to-create-automatic-data-report-in-multiple-languages-2a53b6417d42

Takeaways

As you can see, the duo of Gettext and Localazy is a flexible solution to localization problems. Each addresses different sources of workload and they complement each other wonderfully.

The best thing about the combination of Gettext and Localazy is that if we generate new POT files (and thus lose the previously translated parts), Localazy will take care of re-translating them, so as not to repeat the work unnecessarily.

Thank you for reading. I hope you enjoyed reading as much as I enjoyed writing this for you.

If you would like to share feedback or simply say hello, you can connect with me: https://www.linkedin.com/in/rafalrybnik/?locale=en_US

If you enjoyed reading this, you’ll probably enjoy my other articles too:

References

GNU Gettext:
https://www.gnu.org/software/gettext/

Localazy:
https://localazy.com/

Streamlit:
https://streamlit.io/

└── structure.txt

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Sign up for Best Stories

By Dev Genius

The best stories sent monthly to your email. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Rafał Rybnik

Written by

Researcher of online tracking techniques • Writes about online industry, time series forecasting and things in between • rafalrybnik@yahoo.com

Dev Genius

Coding, Tutorials, News, UX, UI and much more related to development

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store