Translation in Django template does not work

I have a wagtail project where wagtail handles the dynamic page translations. In addition to this I have some string literals in my django templates where I use translation. Actually I have a subscription form where two labels are correctly translated but the rest of the texts are not.

Even when I try to check the translation logic in the shell, it is not working.

from django.utils.translation import activate, gettext as _
activate("hu")
print(_("Hello"))

The result is Hello, despite of the fact that it should be “Szia” in Hungarian. The relevant part of the .po file:

#: newsletter/templates/newsletter/emails/confirmation_asking.html:35
msgid "Hello"
msgstr "Szia"

My django template:

{% load i18n wagtailcore_tags wagtailimages_tags %}
<div id="subscription-modal" class="modal {% if form_has_errors %}is-active{% endif %}">
    <div class="modal-background"></div>
    <div class="modal-card">
        <header class="modal-card-head">
            <p class="modal-card-title">{% translate "Join the Inner Circle for Business AI Insights" %}</p>
            <button class="delete" aria-label="close" onclick="closeModal()"></button>
        </header>
        <section class="modal-card-body">
            <p>{% translate "Subscribe to receive weekly insights, use cases, and best practices on AI and business transformation." %}</p>

            <!-- Display form-level errors -->
            {% if form.non_field_errors %}
            <div class="notification is-danger">
                <ul>
                    {% for error in form.non_field_errors %}
                    <li class="has-text-danger">{{ error }}</li>
                    {% endfor %}
                </ul>
            </div>
            {% endif %}

            <!-- Subscription Form -->
            <form method="post" id="subscription-form" action="{% url 'subscribe' %}">
                {% csrf_token %}
                
                <!-- Hidden field to capture the current page URL -->
                <input type="hidden" name="next" value="{{ request.path }}">
                
                <!-- Name Field -->
                <div class="field">
                    <label class="label" for="id_name">{% translate "Name" %}</label>
                    <div class="control">
                        <input class="input" type="text" name="name" id="id_name" placeholder="{% translate 'Write your nickname here' %}" {% if subscription_form.name.value %} value="{{ subscription_form.name.value }}" {% endif %} {% if subscription_form.name.field.required %}required{% endif %}>
                    </div>
                    {% if subscription_form.name.errors %}
                    <p class="help has-text-danger">{{ subscription_form.name.errors }}</p>
                    {% endif %}
                </div>

                <!-- Email Field -->
                <div class="field">
                    <label class="label" for="id_email">{% translate "Email" %}</label>
                    <div class="control">
                        <input class="input" type="email" name="email" id="id_email" placeholder="{% translate 'you@example.com' %}" {% if subscription_form.email.value %} value="{{ subscription_form.email.value }}" {% endif %} {% if subscription_form.email.field.required %}required{% endif %}>
                    </div>
                    {% if subscription_form.email.errors %}
                    <p class="help has-text-danger">{{ subscription_form.email.errors }}</p>
                    {% endif %}
                </div>

                <!-- Topic Selection (Rendered by Django) -->
                <div class="field">
                    <label class="label">{% translate "Topics (You can choose more)" %}</label>
                    <div class="control">
                        {{ subscription_form.topics }}
                    </div>
                </div>
                {% if subscription_form.topics.errors %}
                <p class="help has-text-danger">{{ subscription_form.topics.errors }}</p>
                {% endif %}

                <hr class="is-divider" style="margin: 1.5em 0;">
                <label class="label">{% translate "Legal Statements" %}</label>

                <!-- Privacy Agreement -->
                <div class="field">
                    <div class="control">
                        <label class="checkbox">
                            <input type="checkbox" name="brevo_agreement" id="id_brevo_agreement" {% if subscription_form.brevo_agreement.value %}checked{% endif %}>
                            I agree to <a href="https://www.brevo.com/privacy-policy" target="_blank">{% translate "Privacy Policy" %}</a> {% translate "and" %} <a href="https://www.brevo.com/terms-of-service" target="_blank">{% translate "Terms of Service" %}</a>.
                        </label>
                    </div>
                    {% if subscription_form.privacy_approved.errors %}
                    <p class="help has-text-danger">{{ subscription_form.privacy_approved.errors }}</p>
                    {% endif %}
                </div>

                <!-- Subscription Agreement -->
                <div class="field">
                    <div class="control">
                        <label class="checkbox">
                            <input type="checkbox" name="email_opt_in" id="id_email_opt_in" {% if subscription_form.email_opt_in.value %}checked{% endif %}>
                            {% translate "I agree to receive newsletters and accept the data privacy statement." %}
                        </label>
                    </div>
                    {% if subscription_form.subscription_agreed.errors %}
                    <p class="help has-text-danger">{{ subscription_form.subscription_agreed.errors }}</p>
                    {% endif %}
                </div>

                <!-- Submit Button -->
                <div class="field">
                    <div class="control">
                        <button type="submit" class="button is-primary is-fullwidth">{% translate "Subscribe" %}</button>
                    </div>
                </div>
            </form>
        </section>
    </div>
</div>

Have you performed all the steps as documented at Translation | Django documentation | Django to create your translations and to ensure they are visible in your project?

Please provide the details of what commands you’ve run, and verify that the .mo files exist in the proper directories.

I made the following steps regarding Django template translation:
Relevant settings:

INSTALLED_APPS = [
    "ai_blog2",
    "home",
    "search",
    "blog",
    "newsletter",
    "analytics",
    "wagtailseo", # SEO for Wagtail: https://pypi.org/project/wagtail-seo/
    "wagtail_localize",
    "wagtail_localize.locales", # https://wagtail-localize.org/stable/
    "wagtail.contrib.forms",
    "wagtail.contrib.settings",
    "wagtail.contrib.redirects",
    "wagtail.contrib.table_block",
    "wagtail.embeds",
    "wagtail.sites",
    "wagtail.users",
    "wagtail.snippets",
    "wagtail.documents",
    "wagtail.images",
    "wagtail.search",
    "wagtail.admin",
    "wagtail",
    "modelcluster",
    "taggit",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "wagtailmath", 
    "wagtailmarkdown", 
    "footer",
]

LANGUAGE_CODE = "en"

TIME_ZONE = "Europe/Budapest"
CELERY_TIMEZONE = "Europe/Budapest"

USE_I18N = True
USE_L10N = True

# Add this for wagtail-localize
WAGTAIL_I18N_ENABLED = True
WAGTAIL_CONTENT_LANGUAGES = LANGUAGES = [
    ('en', _("English")),
    ('hu', _("Hungarian"))
]
TIME_ZONE = "Europe/Budapest"
USE_TZ = True

MIDDLEWARE = [
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "django.middleware.security.SecurityMiddleware",
    "wagtail.contrib.redirects.middleware.RedirectMiddleware",
]

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            os.path.join(PROJECT_DIR, "templates"),
        ],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                'django.template.context_processors.i18n',
                "newsletter.context_processors.subscription_form_processor"
            ],
        },
    },
]

Steps I made to initialize the translation:

python manage.py makemessages -l hu
python manage.py compilemessages

Under the project dir the path of my .mo and .po files: locale/hu/LC_MESSAGES. The form related snippet of my po file:

#: newsletter/templates/includes/subscription_form.html:6
msgid "Join the Inner Circle for Business AI Insights"
msgstr "Csatlakozz a konstruktív közösségünkhöz az 'Üzleti AI' információkért"

#: newsletter/templates/includes/subscription_form.html:10
msgid ""
"Subscribe to receive weekly insights, use cases, and best practices on AI "
"and business transformation."
msgstr ""
"Iratkozz fell, hogy hírleveleket kapj a bevált AI üzleti gyakorlatokkal kapcsolatban."

#: newsletter/templates/includes/subscription_form.html:32
msgid "Name"
msgstr "Név"

#: newsletter/templates/includes/subscription_form.html:34
msgid "Write your nickname here"
msgstr "Add meg a beceneved itt"

#: newsletter/templates/includes/subscription_form.html:43
msgid "Email"
msgstr "Email"

#: newsletter/templates/includes/subscription_form.html:45
msgid "you@example.com"
msgstr "te@example.hu"

#: newsletter/templates/includes/subscription_form.html:54
msgid "Topics (You can choose more)"
msgstr "Témák (Többet is választhatsz.)"

#: newsletter/templates/includes/subscription_form.html:64
msgid "Legal Statements"
msgstr "Jogi nyilatkozatok"

#: newsletter/templates/includes/subscription_form.html:71
msgid "Privacy Policy"
msgstr "Adatkezelési szabályzat"

#: newsletter/templates/includes/subscription_form.html:71
msgid "and"
msgstr "és"

#: newsletter/templates/includes/subscription_form.html:71
msgid "Terms of Service"
msgstr "Felhasználási feltételek"

#: newsletter/templates/includes/subscription_form.html:84
msgid "I agree to receive newsletters and accept the data privacy statement."
msgstr "Elfogadom az adatvédelmi nyilatkozatot, és hogy hírleveleket fogok kapni"

#: newsletter/templates/includes/subscription_form.html:95
msgid "Subscribe"
msgstr "Feliratkozás"

What is strange that for example “Name” is correctly translated to “Név” the Hungarian equivalent, but none of the other elements are.

Here is my included django template snippet:

{% load i18n wagtailcore_tags wagtailimages_tags %}
<div id="subscription-modal" class="modal {% if form_has_errors %}is-active{% endif %}">
    <div class="modal-background"></div>
    <div class="modal-card">
        <header class="modal-card-head">
            <p class="modal-card-title">{% translate "Join the Inner Circle for Business AI Insights" %}</p>
            <button class="delete" aria-label="close" onclick="closeModal()"></button>
        </header>
        <section class="modal-card-body">
            <p>{% translate "Subscribe to receive weekly insights, use cases, and best practices on AI and business transformation." %}</p>

            <!-- Display form-level errors -->
            {% if form.non_field_errors %}
            <div class="notification is-danger">
                <ul>
                    {% for error in form.non_field_errors %}
                    <li class="has-text-danger">{{ error }}</li>
                    {% endfor %}
                </ul>
            </div>
            {% endif %}

            <!-- Subscription Form -->
            <form method="post" id="subscription-form" action="{% url 'subscribe' %}">
                {% csrf_token %}
                
                <!-- Hidden field to capture the current page URL -->
                <input type="hidden" name="next" value="{{ request.path }}">
                
                <!-- Name Field -->
                <div class="field">
                    <label class="label" for="id_name">{% translate "Name" %}</label>
                    <div class="control">
                        <input class="input" type="text" name="name" id="id_name" placeholder="{% translate 'Write your nickname here' %}" {% if subscription_form.name.value %} value="{{ subscription_form.name.value }}" {% endif %} {% if subscription_form.name.field.required %}required{% endif %}>
                    </div>
                    {% if subscription_form.name.errors %}
                    <p class="help has-text-danger">{{ subscription_form.name.errors }}</p>
                    {% endif %}
                </div>

                <!-- Email Field -->
                <div class="field">
                    <label class="label" for="id_email">{% translate "Email" %}</label>
                    <div class="control">
                        <input class="input" type="email" name="email" id="id_email" placeholder="{% translate 'you@example.com' %}" {% if subscription_form.email.value %} value="{{ subscription_form.email.value }}" {% endif %} {% if subscription_form.email.field.required %}required{% endif %}>
                    </div>
                    {% if subscription_form.email.errors %}
                    <p class="help has-text-danger">{{ subscription_form.email.errors }}</p>
                    {% endif %}
                </div>

                <!-- Topic Selection (Rendered by Django) -->
                <div class="field">
                    <label class="label">{% translate "Topics (You can choose more)" %}</label>
                    <div class="control">
                        {{ subscription_form.topics }}
                    </div>
                </div>
                {% if subscription_form.topics.errors %}
                <p class="help has-text-danger">{{ subscription_form.topics.errors }}</p>
                {% endif %}

                <hr class="is-divider" style="margin: 1.5em 0;">
                <label class="label">{% translate "Legal Statements" %}</label>

                <!-- Privacy Agreement -->
                <div class="field">
                    <div class="control">
                        <label class="checkbox">
                            <input type="checkbox" name="brevo_agreement" id="id_brevo_agreement" {% if subscription_form.brevo_agreement.value %}checked{% endif %}>
                            I agree to <a href="https://www.brevo.com/privacy-policy" target="_blank">{% translate "Privacy Policy" %}</a> {% translate "and" %} <a href="https://www.brevo.com/terms-of-service" target="_blank">{% translate "Terms of Service" %}</a>.
                        </label>
                    </div>
                    {% if subscription_form.privacy_approved.errors %}
                    <p class="help has-text-danger">{{ subscription_form.privacy_approved.errors }}</p>
                    {% endif %}
                </div>

                <!-- Subscription Agreement -->
                <div class="field">
                    <div class="control">
                        <label class="checkbox">
                            <input type="checkbox" name="email_opt_in" id="id_email_opt_in" {% if subscription_form.email_opt_in.value %}checked{% endif %}>
                            {% translate "I agree to receive newsletters and accept the data privacy statement." %}
                        </label>
                    </div>
                    {% if subscription_form.subscription_agreed.errors %}
                    <p class="help has-text-danger">{{ subscription_form.subscription_agreed.errors }}</p>
                    {% endif %}
                </div>

                <!-- Submit Button -->
                <div class="field">
                    <div class="control">
                        <button type="submit" class="button is-primary is-fullwidth">{% translate "Subscribe" %}</button>
                    </div>
                </div>
            </form>
        </section>
    </div>
</div>
  1. I don’t see the LOCALE_PATHS setting in your settings file. Django needs to know where to look for translation files. Add this to your settings.py:
LOCALE_PATHS = [
    os.path.join(BASE_DIR, 'locale'),
]
  1. After running makemessages and compilemessages, verify that you have a .mo file in the same directory.

  2. To test if the language switching is working properly, try this in your shell:

from django.utils.translation import activate, get_language, gettext as _
print(get_language())  # Should show current language
activate('hu')
print(get_language())  # Should show 'hu'

Does it mean that convention over configuration does not hold here? I put the locale folder to the default place. And what made it weird that one or two words were correctly translated but the rest not.

See the docs at How Django discovers translations for the locations that Django searches for transalation files, and the order in which they are searched.

The project directory is not one of the places searched by default - Django will be searching through the app directories. (Item #2 in the search order.)

If you want it to search through your project directory, then it does need to be added to LOCALE_PATHS.

My guess would be that you would find those words in one of the other translation files.