Skip to content
Browse files

Adding wagtail-modeltranslation to get at least some sort of localiza…

…tion. (#1566)

* Added localization through wagtail-modeltranslation + custom UX scripts + documentation on what was added and how to undo things when wagtail eventually comes with built-in localization
  • Loading branch information...
Pomax committed Aug 21, 2018
1 parent b9def72 commit fc55d4356326ab0f3570172ae61e412d4f0410fe
@@ -24,11 +24,12 @@ Pillow = "*"
python-slugify = "*"
requests = "*"
social-auth-app-django = "*"
wagtail = "*"
wagtail = "==2.1.2"
wagtail-factories = {ref = "1ead51cadaad3b4530ba2197ccf45d2dca87dbdf", git = ""}
wagtail-inventory = "*"
wagtail-metadata = "*"
whitenoise = "==4.0b4"
wagtail-modeltranslation = "*"

django-debug-toolbar = "*"

Some generated files are not rendered by default. Learn more.

@@ -1,2 +1,2 @@
release: cd network-api && python ./ migrate --no-input && python ./ block_inventory
release: cd network-api && python ./ migrate --no-input && python ./ block_inventory && python ./ sync_page_translation_fields && python ./ update_translation_fields
web: cd network-api && gunicorn networkapi.wsgi:application
@@ -0,0 +1,47 @@
# (Temporary) localised content in Wagtail

This document exists primarily because the current localisation approach is at best a patch to ensure there is _some_ localisation possible of Wagtail content in lieu of an official localisation solution that would ideally take the form of:

- An API for retrieving strings tied to specific pages using a unique page id,
- a mechanism to send those strings to Pontoon, or similar translation/localisation community
- a mechanism to receive translations from Pontoon (or similar service)
- an API for instructing wagtail which localised strings correspond to which original strings, applicable to which locale.

While Wagtail forms a plan of attack around tackling this, we need a localisation solution, and so we have implemented the following:

- `wagtail-modeltranslation` is used to enable content localisation based on a `LANGUAGE_CODE` default language identifier, and `LANGUAGES` list of tuples (of the form `('code', 'full name of locale')`).
- a custom javascript library that improves usability by allowing users of the CMS to hide/reveal localisable fields based on the language they belong to.

The modeltranslation library adds localised URLs to Wagtail, interjecting the locale code in the URL betewen the host domain and the content path, such that a url like:

with "en" as locale, becomes:

This functionality is enabled in the main `` file, where any (subset of) routes that need this kind of infix locale must be wrapped by the Django `i18npatterns` call.

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

Additionally, there is now a `` file in the `wagtailpages` app that describes which models should have which fields automatically added to the set of localizable fields. This is, quite wisely, tracked in separate tables, so that removing wagtail-modeltranslations does not lead to a complete loss of all pages ever made, or massive migrations beyond "drop the translation wrapper tables".

The [wagtail-modeltranslation]( covers all the steps taken to enable this on our end in, so please give that a read-over as well.

There is an open PR over on which was opened to put our usability script in the modeltranslation package itself, however that PR has not landed at the time of writing this document, and so in order to land the PR that this document is in, the updated `` that was necessary to get access to some of the settings over in the client environment has been manually added to a special app called `wagtail-l10n-customization`.

This special app has the following dir structure and content:

|- static
| |-css
| | `-language_toggles.css : styling for the UX improvements
| `-js
| `-language_toggles.js : UX improvements for working with localisable fields
`- : hooks into wagtail that load the UX improver in /cms

In order to ensure that things deploy smoothly, both the `Procfile` and the `` have been updated with instructions specific to wagtail-modeltranslation use, so in the interest of knowing where to look when it comes time to take this back out, all the relevant changes happened by landing the following PR:
@@ -13,6 +13,7 @@
import os
import environ
import dj_database_url
from django.utils.translation import gettext_lazy as _

app = environ.Path(__file__) - 1
root = app - 1
@@ -170,7 +171,13 @@

# wagtail-specific app
# wagtail localisation app

# wagtail-specific app prefixed so that it can be localised
@@ -187,6 +194,7 @@

'django.middleware.locale.LocaleMiddleware', # should be after SessionMiddleware and before CommonMiddleware
@@ -307,7 +315,16 @@
# Internationalization

('en', _('English')),
('de', _('German')),
('pt', _('Portuguese')),
('es', _('Spanish')),
('fr', _('French')),
('pl', _('Polish')),

USE_I18N = True
USE_L10N = True
@@ -13,13 +13,13 @@
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
from django.conf.urls import url, include
from django.contrib import admin
from django.conf import settings
from django.conf.urls import url, include
from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static
from django.contrib import admin
from django.views.generic.base import RedirectView

from wagtail.admin import urls as wagtailadmin_urls
from wagtail.documents import urls as wagtaildocs_urls
from wagtail.core import urls as wagtail_urls
@@ -61,19 +61,22 @@
url(r'^help/', review_app_help_view, name='Review app help'),

# Wagtail CMS routes


url(r'^cms/', include(wagtailadmin_urls)),
url(r'^documents/', include(wagtaildocs_urls)),
url('^sitemap\.xml$', sitemap) if settings.DEBUG else None,
url(r'', include(wagtail_urls)),

# Anything that needs to respect the localised
# url format with /<language_code>/ infixed needs
# to be wrapped by django's i18n_patterns feature:
urlpatterns += i18n_patterns(
url(r'', include(wagtail_urls)),

if settings.USE_S3 is not True:
urlpatterns += static(
@@ -0,0 +1,51 @@
form .l10n-hidden {
display: none;

div.locale-picker {
display: block;
text-align: right;
margin-top: 1.5em;
margin-bottom: 0.5em;

form > div.locale-picker {
/* calc, to highlight what we're doing here:
wagtail's header has a 2em margin-bottom,
and so we correct that for the 1.5em
margin-bottom used in the normal definition
for the div.locale-picker, above */;
margin-top: calc(1.5em - 2em);
margin-bottom: 1em;

div.locale-picker h2 {
display: inline;

div.locale-picker ul.locales {
display: inline-block;
margin: auto 1em;

div.locale-picker ul.locales li.locale {
display: inline;
margin: auto 0.5em;

div.locale-picker ul.locales li.locale button.locale-toggle {
background-color: rgba(57, 151, 150, 0.3);
text-transform: uppercase;
outline: none;
border: none;
font-weight: 700;
font-size: 1.2em;
text-decoration: none;
color: #fff;
padding: .25em;
max-height: 2em;

div.locale-picker ul.locales li.locale button.locale-toggle.showing-locale {
background-color: #43b1b0;

0 comments on commit fc55d43

Please sign in to comment.
You can’t perform that action at this time.