Out of the box, Wagtail assumes all content will be authored in a single language.
This document describes how to configure Wagtail for authoring content in
multiple languages.
```{note}
Wagtail provides the infrastructure for creating and serving content in multiple languages.
There are two options for managing translations across different languages in the admin interface:
[wagtail.contrib.simple_translation](simple_translation) or the more advanced [wagtail-localize](https://github.com/wagtail/wagtail-localize) (third-party package).
```
This document only covers the internationalisation of content managed by Wagtail.
For information on how to translate static content in template files, JavaScript
After wrapping your URL patterns with `i18n_patterns`, your site will now
respond on URL prefixes. But now it won't respond on the root path.
To fix this, we need to detect the user's browser language and redirect them
to the best language prefix. The recommended approach to do this is with
Django's `LocaleMiddleware`:
```python
# my_project/settings.py
MIDDLEWARE = [
# ...
'django.middleware.locale.LocaleMiddleware',
# ...
]
```
#### Custom routing/language detection
You don't strictly have to use `i18n_patterns` or `LocaleMiddleware` for
this and you can write your own logic if you need to.
All Wagtail needs is the language to be activated (using Django's
`django.utils.translation.activate` function) before the
`wagtail.views.serve` view is called.
### Recipes for internationalised sites
#### Language/region selector
Perhaps the most important bit of internationalisation-related UI you can add
to your site is a selector to allow users to switch between different
languages.
If you're not convinced that you need this, have a look at [https://www.w3.org/International/questions/qa-site-conneg#yyyshortcomings](https://www.w3.org/International/questions/qa-site-conneg#yyyshortcomings) for some rationale.
If this is part of a shared base template it may be used in situations where no page object is available, such as 404 error responses, so check that we have a page before proceeding.
For more information, see [get_language_info() in the Django docs](https://docs.djangoproject.com/en/stable/topics/i18n/translation/#django.utils.translation.get_language_info).
The only difference with the `<a>` tag here from the `<a>` tag in the previous example
is how we're getting the page's URL: `{% pageurl page.localized %}`.
All page instances in Wagtail have a `.localized` attribute which fetches the translation
of the page in the current active language. This is why we activated the language previously.
Another difference here is that if the same translated page is shared in two locales, Wagtail
will generate the correct URL for the page based on the current active locale. This is the
key difference between this example and the previous one as the previous one can only get the
URL of the page in its default locale.
#### API filters for headless sites
For headless sites, the Wagtail API supports two extra filters for internationalised sites:
-`?locale=` Filters pages by the given locale
-`?translation_of=` Filters pages to only include translations of the given page ID
For more information, see [](apiv2_i18n_filters).
#### Translatable snippets
You can make a snippet translatable by making it inherit from `wagtail.models.TranslatableMixin`.
For example:
```python
# myapp/models.py
from django.db import models
from wagtail.models import TranslatableMixin
from wagtail.snippets.models import register_snippet
@register_snippet
class Advert(TranslatableMixin, models.Model):
name = models.CharField(max_length=255)
```
The `TranslatableMixin` model adds the `locale` and `translation_key` fields to the model.
##### Making snippets with existing data translatable
For snippets with existing data, it's not possible to just add `TranslatableMixin`,
make a migration, and run it. This is because the `locale` and `translation_key`
fields are both required and `translation_key` needs a unique value for each
instance.
To migrate the existing data properly, we firstly need to use `BootstrapTranslatableMixin`,
which excludes these constraints, then add a data migration to set the two fields, then
switch to `TranslatableMixin`.
This is only needed if there are records in the database. So if the model is empty, you can
go straight to adding `TranslatableMixin` and skip this.
###### Step 1: Add `BootstrapTranslatableMixin` to the model
This will add the two fields without any constraints:
```python
# myapp/models.py
from django.db import models
from wagtail.models import BootstrapTranslatableMixin
from wagtail.snippets.models import register_snippet
@register_snippet
class Advert(BootstrapTranslatableMixin, models.Model):
name = models.CharField(max_length=255)
# if the model has a Meta class, ensure it inherits from
# BootstrapTranslatableMixin.Meta too
class Meta(BootstrapTranslatableMixin.Meta):
verbose_name = 'adverts'
```
Run `python manage.py makemigrations myapp` to generate the schema migration.
###### Step 2: Create a data migration
Create a data migration with the following command:
```sh
python manage.py makemigrations myapp --empty
```
This will generate a new empty migration in the app's `migrations` folder. Edit
that migration and add a `BootstrapTranslatableModel` for each model to bootstrap
in that app:
```python
from django.db import migrations
from wagtail.models import BootstrapTranslatableModel
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_bootstraptranslations'),
]
# Add one operation for each model to bootstrap here
# Note: Only include models that are in the same app!
operations = [
BootstrapTranslatableModel('myapp.Advert'),
]
```
Repeat this for any other apps that contain a model to be bootstrapped.
###### Step 3: Change `BootstrapTranslatableMixin` to `TranslatableMixin`
Now that we have a migration that fills in the required fields, we can swap out
`BootstrapTranslatableMixin` for `TranslatableMixin` that has all the
constraints:
```python
# myapp/models.py
from wagtail.models import TranslatableMixin # Change this line
@register_snippet
class Advert(TranslatableMixin, models.Model): # Change this line
name = models.CharField(max_length=255)
class Meta(TranslatableMixin.Meta): # Change this line, if present
verbose_name = 'adverts'
```
###### Step 4: Run `makemigrations` to generate schema migrations, then migrate!
Run `makemigrations` to generate the schema migration that adds the
constraints into the database, then run `migrate` to run all of the
migrations:
```sh
python manage.py makemigrations myapp
python manage.py migrate
```
When prompted to select a fix for the nullable field 'locale' being changed to
non-nullable, select the option "Ignore for now" (as this has been handled by the
data migration).
### Translation workflow
As mentioned at the beginning, Wagtail does supply `wagtail.contrib.simple_translation`.
The simple_translation module provides a user interface that allows users to copy pages and translatable snippets into another language.
- Copies are created in the source language (not translated)
- Copies of pages are in draft status
Content editors need to translate the content and publish the pages.
To enable add `"wagtail.contrib.simple_translation"` to `INSTALLED_APPS`
and run `python manage.py migrate` to create the `submit_translation` permissions.
In the Wagtail admin, go to settings and give some users or groups the "Can submit translations" permission.
```{note}
Simple Translation is optional. It can be switched out by third-party packages. Like the more advanced [wagtail-localize](https://github.com/wagtail/wagtail-localize).
```
#### Wagtail Localize
As part of the initial work on implementing internationalisation for Wagtail core,
we also created a translation package called `wagtail-localize`. This supports
translating pages within Wagtail, using PO files, machine translation, and external
The Wagtail admin backend has been translated into many different languages. You can find a list of currently available translations on Wagtail's [Transifex page](https://explore.transifex.com/torchbox/wagtail/). (Note: if you're using an old version of Wagtail, this page may not accurately reflect what languages you have available).
If your language isn't listed on that page, you can easily contribute new languages or correct mistakes. Sign up and submit changes to [Transifex](https://explore.transifex.com/torchbox/wagtail/). Translation updates are typically merged into an official release within one month of being submitted.