Pygame Tutorial: Localizing Video Game UI and Text

Please wait while you are redirected...or Click Here if you do not want to wait.
Pygame Tutorial on Localizing Video Game UI and Text

Writing video games for a global user base? Learn how to get ahead of the game in localization with Pygame and Babel.

  1. Phrase Blog
  2. Developers
  3. Pygame Tutorial: Localizing Video Game UI and Text

Pygame is a strong set of Python modules for writing video games. Nevertheless, localizing the user interface (UI) and text in Pygame can often be a time-consuming process: You need to load the font beforehand, render it with the desired text, and draw it on your screen. On top of that, you need to handle all mouse and keypress events when building the menu interface. In this tutorial, you will learn how to localize the UI and text of both the menu and in-game interface seamlessly. Let’s start by installing the necessary modules.


Our setup for localizing with Pygame and Babel

For a smooth start, it is highly recommended to create a new virtual environment and activate it before the installation process.


Run the following command to install Pygame:pip install pygame


Let us install another complementing package called pygame-menu. It is extremely useful for creating a menu for your game:pip install pygame-menu


Last but not least, Babel is the right choice for localizing Python applications. Run the following command to install it:pip install babel

Babel provides a command-line interface for working with message catalogs.

🗒 Note » Check out the following tutorials if you want to learn more about using Babel:

Message catalogs

Let us break down the basic workflow for localizing with message catalogs:

  • Mark strings for translation in the Python scripts.
  • Extract translations into a base POT file.
  • Create a new message catalog for each supported language.
  • Translate the content in each message catalog.
  • Compile the translation files into MO files.

🔗 Resource » Here’s how to use Google Translate in your Python app to automatically translate the content in your message catalogs.

Marking translation strings

First and foremost, you need to mark all the text that will need to get translated. You can do so by wrapping the text in a call to the gettext.gettext() function. Alternatively, you can use the preferred alias form _() as well. Have a look at the following example:# print exampleprint(_(‘app_title’))# set the title for your applicationpygame.display.set_caption(_(‘window_title’))

app_title and window_title represent the unique identifier for the translation string.

Babel provides the following useful commands for generating message catalogs for your game:

  • extract—extract messages from source files and generate a POT file
  • init—create new message catalogs from a POT file
  • update—update existing message catalogs in a POT file
  • compile—compile message catalogs to MO files

Extracting translations into a base POT file

Once you have marked all translation strings, you can use the extract command to create a POT file. The generated file contains all the strings that are meant for translation. You have to pass in the following arguments:

  • a directory or files path
  • an output path

To test the command, create a new folder called locale in your working directory. Then, run the following command (replace mygame.py with the filename of your Python file):pybabel extract mygame.py -o locale/messages.pot

You should see the following output:extracting messages from mygame.pywriting PO template file to locale/messages.pot

Inside the locale folder, you can find the newly generated messages.pot file. It serves as the base translation file for your project. You will use it to generate the corresponding translation file for each of the locales that your game supports.locale > messages.pot…#: test.py:110msgid “widget_font”msgstr “”#: test.py:116msgid “app_title”msgstr “”#: test.py:120msgid “name”msgstr “”…

Each translation string contains two important items, msgid and msgstr:

  • msgid—represents the unique identifier for the translation
  • msgstr—represents the translation text

You can leave the msgstr as an empty string in the base file.

Creating new message catalogs from a POT file

The next step is to initialize the corresponding locales that will be supported by your game. Run the init command with the following arguments:

  • locale (-l)
  • input file (-i)
  • output directory (-d)

pybabel init -l en_US -i locale/messages.pot -d locale

Your console should display the following output:creating catalog locale\en_US\LC_MESSAGES\messages.po based on locale/messages.pot

You need to repeat the command for the other locales as well. For example:# Germanpybabel init -l de_DE -i locale/messages.pot -d locale# Simplified Chinesepybabel init -l zh_CN -i locale/messages.pot -d locale

Each command will create a new folder based on the locale you have specified. There will be a LC_MESSAGES folder with a messages.po file inside each locale. Open the messages.po file and fill in the corresponding translation texts as follows:locale > en_US > LC_MESSAGES > messages.po…#: test.py:110msgid “widget_font”msgstr “arial”#: test.py:116msgid “app_title”msgstr “Welcome”#: test.py:120msgid “name”msgstr “Name”…

Updating existing message catalogs in a POT file

Subsequently, when there are new translation strings or changes to the existing translations, simply run the extract command and then execute the update command as shown below. Let us say you have added a new translation string called last_updated in your Python file:locale > en_US > LC_MESSAGES > messages.po…text = _(‘last_updated’)f_text = font.render(text, True, (255, 255, 255))…

All you need to do is run the following command to update the translation files:pybabel extract mygame.py -o locale/messages.potpybabel update -i locale/messages.pot -d locale

The messages.po file should now look like this:locale > en_US > LC_MESSAGES > messages.po#: test.py:110msgid “widget_font”msgstr “arial”#: test.py:116msgid “app_title”msgstr “Welcome”#: test.py:120msgid “name”msgstr “Name”…#: mygame.py:89msgid “last_updated”msgstr “”

It retains all old translations and appends new ones to all messages.po files inside each locale folder. Simply fill in the desired translation and save the file. Here is an example for en_US locale:#: mygame.py:89msgid “last_updated”msgstr “Last updated on”

Once you have filled in the translation text for all supported locales, you are ready to execute the compile command.

Compiling message catalogs to MO files

Lastly, let us compile the PO into MO files by running the compile command:pybabel compile -d locale

You should get the following output:compiling catalog locale\de_DE\LC_MESSAGES\messages.po to locale\de_DE\LC_MESSAGES\messages.mocompiling catalog locale\en_US\LC_MESSAGES\messages.po to locale\en_US\LC_MESSAGES\messages.mocompiling catalog locale\zh_CN\LC_MESSAGES\messages.po to locale\zh_CN\LC_MESSAGES\messages.mo

It will generate the machine-readable messages.mo file side by side with the messages.po in each locale folder.

Localizing your game

We are now all set up to move on with the localization of our game with the help of Babel. We will have a detailed look at interpolation, pluralization, loading locales dynamically, using a predefined theme to localize the UI and text of your menu, as well as formatting numbers and dates.


Given the following translation string…game_name = “Tic Tac Toe”text = _(“press_start”) % game_name

…you can easily interpolate the output inside your messages.po file of each locale:msgid “press_start”msgstr “Press start to play %s”

You need to retain the %s placeholder since it represents string interpolation. For integer variables, you need to use %d instead.msgid “score_board”msgstr “Score: %d”


Babel comes with its own pluralization support via the gettext.ngettext() function. It takes in three positional parameters:

  • singular—unique identifier for singular translation
  • plural—unique identifier for plural translation
  • n—plural determiner

You can use it as follows:import gettext…difficulty = 2text = gettext.ngettext(‘game_message’, ‘game_message_plural’, difficulty )…

Based on the value of the difficulty parameter, it will return either the output string from game_message or game_message_plural. You can interpolate it as follows:gettext.ngettext(‘game_message’, ‘game_message_plural’, difficulty) % difficulty

After you run the extract or update command, you can find the corresponding plural formula inside each messages.po file. By default, Babel will generate the plural forms based on the locale you have set during the init command.# example for two plural forms”Plural-Forms: nplurals=2; plural=(n != 1)\n”# example for 6 plural forms”Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : “”n%100>=3 && n%100<=10 ? 3 : n%100>=0 && n%100<=2 ? 4 : 5)\n”

Here is an example of how the translation string will appear in message.po file for two plural forms:#: mygame.py:67#, python-formatmsgid “game_message”msgid_plural “game_message_plural”msgstr[0] “You will gain %d point per coin collected.”msgstr[1] “You will gain %d points per coin collected.”

Some languages have several plural forms. Arabic, for example, has 6 plural forms in total, so before filling in the translations, make sure its messages.po is as follows:#: mygame.py:67#, python-formatmsgid “game_message”msgid_plural “game_message_plural”msgstr[0] “”msgstr[1] “”msgstr[2] “”msgstr[3] “”msgstr[4] “”msgstr[5] “”

Compile now all messages.po into messages.mo files.

Loading locales dynamically

Next, append the following code in your main file to load all locales dynamically via the glob package:mygame.pyimport globimport os…locales = [x.split(‘\\’)[1] for x in glob.glob(‘locale/*’) if os.path.isdir(x)]active_locale = ‘en_US’translations = {}for locale in locales: translations[locale] = gettext.translation(‘messages’, localedir=’locale’, languages=[locale])