Skip to main content

Django Message Files

Introduction

When building applications for global audiences, supporting multiple languages is essential. Django's internationalization (i18n) framework provides robust tools for creating multilingual applications. At the heart of Django's translation system are message files - special files that contain all the translatable strings from your application along with their translations.

In this tutorial, we'll explore how Django message files work, how to create them, and how to use them effectively to internationalize your Django application.

Understanding Message Files

Message files are structured text files that store translated strings for your application. Django uses the GNU gettext format for these files, which involves several file types:

  • .po files (Portable Object) - Human-readable files containing original strings and their translations
  • .mo files (Machine Object) - Compiled binary versions of .po files for production use
  • .pot files (Portable Object Template) - Template files used as a starting point for translations

These files are organized by language code (such as fr, es, ja) within your project structure.

Setting Up Internationalization

Before working with message files, make sure your Django project is configured for internationalization:

  1. Ensure these settings are properly configured in your settings.py:
python
INSTALLED_APPS = [
# ...other apps
'django.contrib.i18n',
]

MIDDLEWARE = [
# ...other middleware
'django.middleware.locale.LocaleMiddleware',
]

USE_I18N = True
USE_L10N = True

LANGUAGE_CODE = 'en-us' # Default language

LANGUAGES = [
('en', 'English'),
('es', 'Spanish'),
('fr', 'French'),
# Add more languages as needed
]

LOCALE_PATHS = [
os.path.join(BASE_DIR, 'locale'),
]
  1. Create a locale directory at your project root:
bash
mkdir -p locale

Marking Strings for Translation

Before generating message files, you need to mark which strings should be translated:

In Python Files

python
from django.utils.translation import gettext as _
from django.utils.translation import ngettext
from django.utils.translation import gettext_lazy as _

# Simple translation
welcome_message = _("Welcome to our website!")

# Translation with variables
def greet(name):
return _("Hello, %(name)s!") % {'name': name}

# Plural forms
def display_results(count):
return ngettext(
"%(count)d result found.",
"%(count)d results found.",
count
) % {'count': count}

In Template Files

html
{% load i18n %}

<!-- Simple translation -->
<h1>{% translate "Welcome to our website" %}</h1>

<!-- Translation with variables -->
<p>{% blocktranslate with username=user.username %}Hello, {{ username }}!{% endblocktranslate %}</p>

<!-- Plural forms -->
{% blocktranslate count counter=list|length %}
There is {{ counter }} item.
{% plural %}
There are {{ counter }} items.
{% endblocktranslate %}

Creating Message Files

After marking strings for translation, you need to extract them into message files:

Step 1: Extract Messages

Run the makemessages command to extract all marked strings into .po files:

bash
python manage.py makemessages -l es  # Create/update Spanish translations
python manage.py makemessages -l fr # Create/update French translations

This command scans your code for translation strings and creates/updates .po files in your locale directory.

Step 2: Examine the Generated .po Files

Let's look at an example .po file (locale/es/LC_MESSAGES/django.po):

#: myapp/views.py:23
msgid "Welcome to our website!"
msgstr "¡Bienvenido a nuestro sitio web!"

#: myapp/views.py:27
#, python-format
msgid "Hello, %(name)s!"
msgstr "¡Hola, %(name)s!"

#: myapp/templates/results.html:12
#, python-format
msgid "%(count)d result found."
msgid_plural "%(count)d results found."
msgstr[0] "%(count)d resultado encontrado."
msgstr[1] "%(count)d resultados encontrados."

Each entry in the .po file contains:

  • A reference to where the string appears in your code
  • The original string (msgid)
  • The translation (msgstr)
  • For plural forms, separate msgstr[n] entries for each plural form

Step 3: Translate Strings

Edit the .po files to provide translations for each string. You can do this manually with a text editor, or use specialized PO file editors like Poedit.

Step 4: Compile Message Files

After translating, compile the .po files into binary .mo files:

bash
python manage.py compilemessages

Django uses these compiled .mo files in production for better performance.

Message Files Structure

The message files are organized in a specific directory structure:

locale/
en/
LC_MESSAGES/
django.po
django.mo
es/
LC_MESSAGES/
django.po
django.mo
fr/
LC_MESSAGES/
django.po
django.mo

Advanced Message File Features

Contextual Markers

Sometimes the same string might need different translations depending on context:

python
from django.utils.translation import pgettext

# Different meanings of "May"
month = pgettext("month name", "May")
verb = pgettext("verb", "May")

In the .po file:

msgctxt "month name"
msgid "May"
msgstr "Mayo" # Spanish

msgctxt "verb"
msgid "May"
msgstr "Puede" # Spanish

Lazy Translation

For strings that should only be translated when they're used (common in models and forms):

python
from django.utils.translation import gettext_lazy as _

class Product(models.Model):
name = models.CharField(_("product name"), max_length=100)
description = models.TextField(_("product description"))

Domain-specific Message Files

You can create separate message files for different parts of your application:

bash
python manage.py makemessages -l es -d djangojs  # For JavaScript translations

This creates djangojs.po files for JavaScript translations.

Real-world Example: A Multilingual Blog

Let's see how message files work in a real-world example of a blog application:

Models

python
from django.db import models
from django.utils.translation import gettext_lazy as _

class BlogPost(models.Model):
title = models.CharField(_("title"), max_length=200)
content = models.TextField(_("content"))
publication_date = models.DateTimeField(_("publication date"), auto_now_add=True)

class Meta:
verbose_name = _("blog post")
verbose_name_plural = _("blog posts")

Views

python
from django.shortcuts import render
from django.utils.translation import gettext as _
from .models import BlogPost

def blog_index(request):
posts = BlogPost.objects.all()
count = posts.count()
message = ngettext(
"There is %(count)d post available.",
"There are %(count)d posts available.",
count
) % {'count': count}

return render(request, 'blog/index.html', {
'posts': posts,
'message': message,
})

Template

html
{% load i18n %}

{% block content %}
<h1>{% translate "Blog" %}</h1>

<p>{{ message }}</p>

{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<div class="meta">
{% blocktranslate with date=post.publication_date|date:"SHORT_DATE_FORMAT" %}
Published on {{ date }}
{% endblocktranslate %}
</div>
<div class="content">
{{ post.content }}
</div>
<a href="{% url 'blog_detail' post.id %}">
{% translate "Read more" %}
</a>
</article>
{% empty %}
<p>{% translate "No posts available." %}</p>
{% endfor %}
{% endblock %}

Language Switcher

To allow users to change languages:

html
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.path }}">
<select name="language" onchange="this.form.submit()">
{% for lang_code, lang_name in LANGUAGES %}
<option value="{{ lang_code }}" {% if lang_code == LANGUAGE_CODE %}selected{% endif %}>
{{ lang_name }}
</option>
{% endfor %}
</select>
</form>

Working with Message Files in Development

Updating Translation Files

When you add new translatable strings to your code:

  1. Run makemessages again to update .po files:
bash
python manage.py makemessages -l es -l fr
  1. Translate the new strings in the .po files
  2. Compile the updated translations:
bash
python manage.py compilemessages

Tips for Efficient Translation Management

  1. Use comments for translators: Add translator comments in your code:
python
# Translators: This is a greeting shown on the homepage
welcome_message = _("Welcome to our website!")
  1. Keep strings complete: Don't split sentences into multiple translation strings.

  2. Use format strings for variables: Never concatenate strings for translation.

python
# Bad
_("Hello") + " " + user.name

# Good
_("Hello %(name)s") % {'name': user.name}
  1. Consider translation context: Provide context with pgettext() for ambiguous terms.

Summary

Django message files are the foundation of internationalization in Django applications. They store translations of your application's text in a structured format that Django can use to display content in different languages.

The workflow involves:

  1. Marking translatable strings in your code
  2. Extracting these strings with makemessages
  3. Translating the strings in .po files
  4. Compiling the translations with compilemessages
  5. Using the translations in your application

By properly managing your message files, you can create truly multilingual Django applications that provide a localized experience for users around the world.

Additional Resources

Exercises

  1. Set up internationalization in a new Django project and create message files for two languages.
  2. Create a simple form with translatable labels and error messages.
  3. Implement a language switcher that allows users to change the display language.
  4. Add translations for dynamic content such as blog posts or product descriptions.
  5. Create a custom template tag that handles pluralization for a specific use case in your application.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)