Authored by Thimo Kraemer

Creating multilingual Wagtail sites

Wagtail does not support multi-language content out of the box. There are already packages available which address this issue, but most of them dig somewhat deeper into Wagtail or have another approach to deal with translations.

The following solution just adds a field for each language to page models considering specified panels and search fields. In addition it provides a LanguageSwitcherBlock for StreamFields and a TranslatableFormBuilder for Wagtail form pages.

One downside may be, that the URL is the same for all languages (except the prefixed language code) since slugs are left unchanged. QuerySets aren't modified as well.


Be sure that the following configuration variables are set in

# Enable I18N
USE_I18N = True
# Default language
# Languages you want to support
    ('en', _('English')),
    ('de', _('German')),
# Add the LocaleMiddleware
# It should come after SessionMiddleware and before CommonMiddleware

Enable language dependend URL routing in

from django.conf.urls import url, include
from django.conf.urls.i18n import i18n_patterns
from wagtail.core import urls as wagtail_urls

urlpatterns = [
urlpatterns += i18n_patterns(
    url(r'', include(wagtail_urls)),


Translatable fields

Always subclass from TranslatablePage if the page model contains translatable fields:

from django.db import models
from wagtail_i18n import TranslatablePage, TranslatableField
class MyPage(TranslatablePage):
    # Translatable field
    foo = TranslatableField(models.CharField())
    # Normal field
    bar = models.BooleanField(default=False)

After migration you have the fields foo [en], foo [de] and bar in your editor view.

To use translated fields in QuerySets you can use the helper function i18n_attr:

from wagtail_i18n import i18n_attr

qs = MyPage.objects.filter(**{i18n_attr('foo'): 'filterterm'})

Multi-language StreamFields

Of course one solution would be to just duplicate a StreamField with TranslatableField as shown above. But that's not the preferred way if we often have block content that is the same for all languages, for example code blocks or images. Here we can make use of the LanguageSwitcherBlock:

from wagtail.core.models import Page
from wagtail.core.fields import StreamField
from wagtail.admin.edit_handlers import StreamFieldPanel
from wagtail.core.blocks import RichTextBlock
from wagtail.images.blocks import ImageChooserBlock
from wagtail_i18n import LanguageSwitcherBlock

class MyPage(Page):
    body = StreamField([
        ('arbitrary_content', RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('language_switcher', LanguageSwitcherBlock()),
    content_panels = Page.content_panels + [

Finally we need a template my_page.html where we iterate over iter_i18n:

{% extends "base.html" %}
{% load wagtailcore_tags %}

{% for block in page.body.iter_i18n %}
    {% include_block block %}
{% endfor %}

Now we are able to switch the language within the StreamField and only the blocks of the current language are rendered.

Translatable form builder

Just assign the TranslatableFormBuilder class to the form_builder attribute of your form page to enable translations:

from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
from modelcluster.fields import ParentalKey
from wagtail_i18n import TranslatableFormBuilder

class FormField(AbstractFormField):
    page = ParentalKey('FormPage', on_delete=models.CASCADE, related_name='form_fields')

class FormPage(AbstractEmailForm):
    # Assign the TranslatableFormBuilder
    form_builder = TranslatableFormBuilder

Now all fields (title, help text, choices, etc.) can be translated within usual PO files. You can add one to the LOCALE_PATHS in your

Edited 11.1 KB
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment