Usageï
First, be sure to have a valid settings to use multiple languages with parler. ex:
Basicsï
Settingsï
# settings.py
INSTALLED_APPS = [
...
"wagtail_parler",
"parler",
]
LANGUAGE_CODE = "fr"
PARLER_LANGUAGES = {
None: ({"code": "fr", }, {"code": "en", },),
"default": {
"fallbacks": [LANGUAGE_CODE],
"hide_untranslated": False,
},
}
Create a TranslatableModelï
Letâs start with a model TranslatableModel Food.
Nothing special, itâs a normal django-parler TranslatableModel with its translations
# models.py
from django.db import models
from django.utils.translation import gettext_lazy as _
from parler.models import TranslatableModel
from parler.models import TranslatedFields
class BaseFood(TranslatableModel):
yum_rating = models.PositiveSmallIntegerField(
verbose_name=_("Score de miam"), blank=False, null=False
)
vegetarian = models.BooleanField(
verbose_name=_("Vegetarian"),
blank=False,
null=False,
default=False,
)
vegan = models.BooleanField(
verbose_name=_("Vegan"),
blank=False,
null=False,
default=False,
)
translations = TranslatedFields(
name=models.CharField(_("Nom"), max_length=255, blank=False, null=False),
summary=models.TextField(_("Résumé"), blank=False, null=False),
content=models.TextField(_("Contenu"), blank=False, null=False),
)
class Meta:
verbose_name = _("Nourriture")
verbose_name_plural = _("Nourritures")
def __str__(self) -> str:
return self.safe_translation_getter("name", any_language=True)
Create a default Admin interfaceï
Know, letâs create the wagtail SnippetViewSet (or wagtail ModelAdmin) to manage this Model.
# wagtailhooks.py
from wagtail.snippets.views.snippets import SnippetViewSet
from wagtail.snippets.models import register_snippet
from .models import Food
class FoodAdmin(SnippetViewSet):
model = Food
register_snippet(FoodAdmin)
# Or for ModelAdmin:
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from .models import Food
class FoodAdmin(ModelAdmin):
model = Food
modeladmin_register(FoodAdmin)
At this stage, if you go the admin interface to create a new Nourriture, youâll only get standard fields displayed, in the order where they are added in your models (rating, vege, vegan):

Add auto tabs for languages via ParlerSnippetAdminMixin (or ParlerModelAdminMixin)ï
Tranlatable fields are not yet displayed. To add tabs for translations, you just
had to extends ParlerSnippetAdminMixin (or ParlerModelAdminMixin for ModelAdmin):
# wagtailhooks.py
from wagtail.snippets.views.snippets import SnippetViewSet
from wagtail.snippets.models import register_snippet
from wagtail_parler.handlers import ParlerSnippetAdminMixin
from .models import Food
class FoodAdmin(ParlerSnippetAdminMixin, SnippetViewSet):
model = Food
register_snippet(FoodAdmin)
# or for an usage with wagtail-modeladmin:
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from wagtail_parler.handlers import ParlerModelAdminMixin
from .models import Food
class FoodAdmin(ParlerModelAdminMixin, ModelAdmin):
model = Food
modeladmin_register(FoodAdmin)
This time, youâll see 3 tabs (if you set 2 languages). First one will contain fields wich are shared between all translations (rating, vege, vegan). Then a tab for each configured language will be added to translate the language dependant fields (name, summary, content).
Untranslated data tabï

Language tabï

Use Model.panelsï
You can use panels attribute of your Model to customise the order of the fields or group them
together for exemple:
# models.py
...
class BaseFood(TranslatableModel):
...
panels = [
FieldPanel("yum_rating"),
ObjectList(
heading=_("Régime"), children=[FieldPanel("vegetarian"), FieldPanel("vegan")]
)
]
...
Use edit_handler from Model or ModelAdminï
wagtail-parler supports all ways to define your ModelAdminâs edit_handlers.
You can customize non-translated fields as you want.
# models.py
...
class BaseFood(TranslatableModel):
...
panels = [
FieldPanel("yum_rating"),
ObjectList(
heading=_("Régime"), children=[FieldPanel("vegetarian"), FieldPanel("vegan")]
)
]
...
Customize handlers of the translated fields with TranslationsListï
Of course, wagtail-parler let you customize what is in language tabs.
To do this, youâll define a TranslationsList inside your edit_handler.
It will be used as a template to generate all languages tabs.
# wagtail_hooks.py
from wagtail.admin.panels import FieldPanel
from wagtail.admin.panels import ObjectList
from wagtail.contrib.modeladmin.options import ModelAdmin
from wagtail.contrib.modeladmin.options import modeladmin_register
from wagtail_parler.handlers import ParlerModelAdminMixin
from wagtail_parler.handlers import TranslationsList
from .models import Food
class FoodAdmin(ParlerModelAdminMixin, ModelAdmin):
model = Food
edit_handler = ObjectList(
children=[
FieldPanel("yum_rating"),
# translations tabs will be inserted here, between "yum_rating" and "Régime".
TranslationsList(
children=[
# Each translation tab will use those handlers
FieldPanel("name"),
ObjectList(
heading="HTML content",
children=[
FieldPanel("summary"),
FieldPanel("content"),
]
),
],
),
ObjectList(
heading=_("Régime"), children=[FieldPanel("vegetarian"), FieldPanel("vegan")]
),
],
)
modeladmin_register(FoodAdmin)
Now, translations tabs will be set between tabs âScore de miamâ (yum_rating) and âRĂ©gimeâ (specific objectlist grouping vegetarian and vegan fields).
Furthermore, we also organized content of each language tab by grouping fields summary and
content below a âHTML Contentâ group.

Personalize language tab lablesï
Those settings are designed to personlize all language tabs for all your ParlerModelAdminMixin:
WAGTAIL_PARLER_DEFAULT_TAB_HEADINGWAGTAIL_PARLER_DEFAULT_TAB_HEADING_UNSTRANLATEDWAGTAIL_PARLER_DEFAULT_TAB_HEADING_TRANLATED
But if you want a really specific tab label for one ParlerModelAdminMixin, you can set the label to use in the TranslationsList heading:
# wagtail_hooks.py
...
class FoodAdmin(ParlerModelAdminMixin, ModelAdmin):
model = Food
edit_handler = ObjectList(
children=[
FieldPanel("yum_rating"),
# translations tabs will be inserted here, between "yum_rating" and "Régime".
TranslationsList(
heading="Specific %(locale)s tab",
children=[
]
)
...
]
)
%(locale)s will always be available as var replacement.

All others keys inside each language confiuration dict of PARLER_LANGUAGES (PARLER_LANGUAGES[None][<lang_offset>]) will also be available, so do not hesitate to add your customs inside it.
# settings.py
PARLER_LANGUAGES = {
None: (
{
"code": "fr",
"translated_label": _("Français"), # custom add
"untranslated_label": "Français", # custom add
"utf8_flag": "đ«đ·", # custom add
},
...
)
...
}
...
TranslationsList(
heading="%(code)s: %(locale)s - %(untranslated_label)s %(utf8_flag)s",
...
)
...
