mirror of
https://github.com/wagtail/wagtail.git
synced 2024-11-30 01:46:24 +01:00
b131b4813e
- added sub-section to language part of general_guidelines.md - fixes #8860
704 lines
23 KiB
Markdown
704 lines
23 KiB
Markdown
# Internationalisation
|
|
|
|
```{contents}
|
|
---
|
|
local:
|
|
depth: 3
|
|
---
|
|
```
|
|
|
|
(multi_language_content)=
|
|
|
|
## Multi-language content
|
|
|
|
### Overview
|
|
|
|
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
|
|
code, etc, refer to the [Django internationalisation docs](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/).
|
|
Or, if you are building a headless site, refer to the docs of the frontend framework you are using.
|
|
|
|
### Wagtail's approach to multi-lingual content
|
|
|
|
This section provides an explanation of Wagtail's internationalisation approach.
|
|
If you're in a hurry, you can skip to [](Configuration).
|
|
|
|
In summary:
|
|
|
|
- Wagtail stores content in a separate page tree for each locale
|
|
- It has a built-in `Locale` model and all pages are linked to a `Locale` with the `locale` foreign key field
|
|
- It records which pages are translations of each other using a shared UUID stored in the `translation_key` field
|
|
- It automatically routes requests through translations of the site's homepage
|
|
- It uses Django's `i18n_patterns` and `LocaleMiddleware` for language detection
|
|
|
|
#### Page structure
|
|
|
|
Wagtail stores content in a separate page tree for each locale.
|
|
|
|
For example, if you have two sites in two locales, then you will see four
|
|
homepages at the top level of the page hierarchy in the explorer.
|
|
|
|
This approach has some advantages for the editor experience as well:
|
|
|
|
- There is no default language for editing, so content can be authored in any
|
|
language and then translated to any other.
|
|
- Translations of a page are separate pages so they can be published at
|
|
different times.
|
|
- Editors can be given permission to edit content in one locale and not others.
|
|
|
|
#### How locales and translations are recorded in the database
|
|
|
|
All pages (and any snippets that have translation enabled) have a `locale` and
|
|
`translation_key` field:
|
|
|
|
- `locale` is a foreign key to the `Locale` model
|
|
- `translation_key` is a UUID that's used to find translations of a piece of content.
|
|
Translations of the same page/snippet share the same value in this field
|
|
|
|
These two fields have a 'unique together' constraint so you can't have more than
|
|
one translation in the same locale.
|
|
|
|
#### Translated homepages
|
|
|
|
When you set up a site in Wagtail, you select the site's homepage in the 'root page'
|
|
field and all requests to that site's root URL will be routed to that page.
|
|
|
|
Multi-lingual sites have a separate homepage for each locale that exist as siblings
|
|
in the page tree. Wagtail finds the other homepages by looking for translations of
|
|
the site's 'root page'.
|
|
|
|
This means that to make a site available in another locale, you just need to
|
|
translate and publish its homepage in that new locale.
|
|
|
|
If Wagtail can't find a homepage that matches the user's language, it will fall back
|
|
to the page that is selected as the 'root page' on the site record, so you can use
|
|
this field to specify the default language of your site.
|
|
|
|
#### Language detection and routing
|
|
|
|
For detecting the user's language and adding a prefix to the URLs
|
|
(`/en/`, `/fr-fr/`, for example), Wagtail is designed to work with Django's
|
|
built-in internationalisation utilities such as `i18n_patterns` and
|
|
`LocaleMiddleware`. This means that Wagtail should work seamlessly with any
|
|
other internationalised Django applications on your site.
|
|
|
|
#### Locales
|
|
|
|
The locales that are enabled on a site are recorded in the `Locale` model in
|
|
`wagtailcore`. This model has just two fields: ID and `language_code` which
|
|
stores the [BCP-47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag)
|
|
that represents this locale.
|
|
|
|
The locale records can be set up with an [optional management UI](enabling_locale_management) or created
|
|
in the shell. The possible values of the `language_code` field are controlled
|
|
by the `WAGTAIL_CONTENT_LANGUAGES` setting.
|
|
|
|
```{note}
|
|
Read this if you've changed ``LANGUAGE_CODE`` before enabling internationalisation
|
|
|
|
On initial migration, Wagtail creates a ``Locale`` record for the language that
|
|
was set in the ``LANGUAGE_CODE`` setting at the time the migration was run. All
|
|
pages will be assigned to this ``Locale`` when Wagtail's internationalisation is disabled.
|
|
|
|
If you have changed the ``LANGUAGE_CODE`` setting since updating to Wagtail 2.11,
|
|
you will need to manually update the record in the ``Locale`` model too before
|
|
enabling internationalisation, as your existing content will be assigned to the old code.
|
|
```
|
|
|
|
(configuration)=
|
|
|
|
### Configuration
|
|
|
|
In this section, we will go through the minimum configuration required to enable
|
|
content to be authored in multiple languages.
|
|
|
|
```{contents}
|
|
---
|
|
local:
|
|
depth: 1
|
|
---
|
|
```
|
|
|
|
(enabling_internationalisation)=
|
|
|
|
#### Enabling internationalisation
|
|
|
|
To enable internationalisation in both Django and Wagtail, set the following
|
|
settings to `True`:
|
|
|
|
```python
|
|
# my_project/settings.py
|
|
|
|
USE_I18N = True
|
|
WAGTAIL_I18N_ENABLED = True
|
|
```
|
|
|
|
In addition, you might also want to enable Django's localisation support. This
|
|
will make dates and numbers display in the user's local format:
|
|
|
|
```python
|
|
# my_project/settings.py
|
|
|
|
USE_L10N = True
|
|
```
|
|
|
|
(configuring_available_languages)=
|
|
|
|
#### Configuring available languages
|
|
|
|
Next we need to configure the available languages. There are two settings
|
|
for this that are each used for different purposes:
|
|
|
|
- `LANGUAGES` - This sets which languages are available on the frontend of the site.
|
|
- `WAGTAIL_CONTENT_LANGUAGES` - This sets which the languages Wagtail content
|
|
can be authored in.
|
|
|
|
You can set both of these settings to the exact same value. For example, to
|
|
enable English, French, and Spanish:
|
|
|
|
```python
|
|
# my_project/settings.py
|
|
|
|
WAGTAIL_CONTENT_LANGUAGES = LANGUAGES = [
|
|
('en', "English"),
|
|
('fr', "French"),
|
|
('es', "Spanish"),
|
|
]
|
|
```
|
|
|
|
```{note}
|
|
Whenever ``WAGTAIL_CONTENT_LANGUAGES`` is changed, the ``Locale`` model needs
|
|
to be updated as well to match.
|
|
|
|
This can either be done with a data migration or with the optional locale
|
|
management UI described in the next section.
|
|
```
|
|
|
|
You can also set these to different values. You might want to do this if you
|
|
want to have some programmatic localisation (like date formatting or currency,
|
|
for example) but use the same Wagtail content in multiple regions:
|
|
|
|
```python
|
|
# my_project/settings.py
|
|
|
|
LANGUAGES = [
|
|
('en-GB', "English (Great Britain)"),
|
|
('en-US', "English (United States)"),
|
|
('en-CA', "English (Canada)"),
|
|
('fr-FR', "French (France)"),
|
|
('fr-CA', "French (Canada)"),
|
|
]
|
|
|
|
WAGTAIL_CONTENT_LANGUAGES = [
|
|
('en-GB', "English"),
|
|
('fr-FR', "French"),
|
|
]
|
|
```
|
|
|
|
When configured like this, the site will be available in all the different
|
|
locales in the first list, but there will only be two language trees in
|
|
Wagtail.
|
|
|
|
All the `en-` locales will use the "English" language tree, and the `fr-`
|
|
locales will use the "French" language tree. The differences between each locale
|
|
in a language would be programmatic. For example: which date/number format to
|
|
use, and what currency to display prices in.
|
|
|
|
(enabling_locale_management)=
|
|
|
|
#### Enabling the locale management UI (optional)
|
|
|
|
An optional locale management app exists to allow a Wagtail administrator to
|
|
set up the locales from the Wagtail admin interface.
|
|
|
|
To enable it, add `wagtail.locales` into `INSTALLED_APPS`:
|
|
|
|
```python
|
|
# my_project/settings.py
|
|
|
|
INSTALLED_APPS = [
|
|
# ...
|
|
'wagtail.locales',
|
|
# ...
|
|
]
|
|
```
|
|
|
|
#### Adding a language prefix to URLs
|
|
|
|
To allow all of the page trees to be served at the same domain, we need
|
|
to add a URL prefix for each language.
|
|
|
|
To implement this, we can use Django's built-in
|
|
[i18n_patterns](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#language-prefix-in-url-patterns)
|
|
function, which adds a language prefix to all of the URL patterns passed into it.
|
|
This activates the language code specified in the URL and Wagtail takes this into
|
|
account when it decides how to route the request.
|
|
|
|
In your project's `urls.py` add Wagtail's core URLs (and any other URLs you
|
|
want to be translated) into an `i18n_patterns` block:
|
|
|
|
```python
|
|
# /my_project/urls.py
|
|
|
|
# ...
|
|
|
|
from django.conf.urls.i18n import i18n_patterns
|
|
|
|
# Non-translatable URLs
|
|
# Note: if you are using the Wagtail API or sitemaps,
|
|
# these should not be added to `i18n_patterns` either
|
|
urlpatterns = [
|
|
path('django-admin/', admin.site.urls),
|
|
|
|
path('admin/', include(wagtailadmin_urls)),
|
|
path('documents/', include(wagtaildocs_urls)),
|
|
]
|
|
|
|
# Translatable URLs
|
|
# These will be available under a language code prefix. For example /en/search/
|
|
urlpatterns += i18n_patterns(
|
|
path('search/', search_views.search, name='search'),
|
|
path("", include(wagtail_urls)),
|
|
)
|
|
```
|
|
|
|
##### Bypass language prefix for the default language
|
|
|
|
If you want your default language to have URLs that resolve normally without a language prefix,
|
|
you can set the `prefix_default_language` parameter of `i18n_patterns` to `False`.
|
|
For example, if you have your languages configured like this:
|
|
|
|
```python
|
|
# myproject/settings.py
|
|
|
|
# ...
|
|
|
|
LANGUAGE_CODE = 'en'
|
|
WAGTAIL_CONTENT_LANGUAGES = LANGUAGES = [
|
|
('en', "English"),
|
|
('fr', "French"),
|
|
]
|
|
|
|
# ...
|
|
```
|
|
|
|
And your `urls.py` configured like this:
|
|
|
|
```python
|
|
# myproject/urls.py
|
|
# ...
|
|
|
|
# These URLs will be available under a language code prefix only for languages that
|
|
# are not set as default in LANGUAGE_CODE.
|
|
|
|
urlpatterns += i18n_patterns(
|
|
path('search/', search_views.search, name='search'),
|
|
path("", include(wagtail_urls)),
|
|
prefix_default_language=False,
|
|
)
|
|
```
|
|
|
|
Your URLs will now be prefixed only for the French version of your website, for example:
|
|
|
|
```
|
|
- /search/
|
|
- /fr/search/
|
|
```
|
|
|
|
#### User language auto-detection
|
|
|
|
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.
|
|
|
|
(basic_example)=
|
|
|
|
##### Basic example
|
|
|
|
Here is a basic example of how to add links between translations of a page.
|
|
|
|
This example, however, will only include languages defined in
|
|
`WAGTAIL_CONTENT_LANGUAGES` and not any extra languages that might be defined
|
|
in `LANGUAGES`. For more information on what both of these settings mean, see
|
|
[Configuring available languages](configuring_available_languages).
|
|
|
|
If both settings are set to the same value, this example should work well for you,
|
|
otherwise skip to the next section that has a more complicated example which takes
|
|
this into account.
|
|
|
|
```html+django
|
|
|
|
{# make sure these are at the top of the file #}
|
|
{% load i18n wagtailcore_tags %}
|
|
|
|
{% if page %}
|
|
{% for translation in page.get_translations.live %}
|
|
{% get_language_info for translation.locale.language_code as lang %}
|
|
<a href="{% pageurl translation %}" rel="alternate" hreflang="{{ language_code }}">
|
|
{{ lang.name_local }}
|
|
</a>
|
|
{% endfor %}
|
|
{% endif %}
|
|
```
|
|
|
|
Let's break this down:
|
|
|
|
```html+django
|
|
{% if page %}
|
|
...
|
|
{% endif %}
|
|
```
|
|
|
|
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.
|
|
|
|
```html+django
|
|
{% for translation in page.get_translations.live %}
|
|
...
|
|
{% endfor %}
|
|
```
|
|
|
|
This `for` block iterates through all published translations of the current page.
|
|
|
|
```html+django
|
|
{% get_language_info for translation.locale.language_code as lang %}
|
|
```
|
|
|
|
This is a Django built-in tag that gets info about the language of the translation.
|
|
For more information, see [get_language_info() in the Django docs](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#django.utils.translation.get_language_info).
|
|
|
|
```html+django
|
|
<a href="{% pageurl translation %}" rel="alternate" hreflang="{{ language_code }}">
|
|
{{ lang.name_local }}
|
|
</a>
|
|
```
|
|
|
|
This adds a link to the translation. We use `{{ lang.name_local }}` to display
|
|
the name of the locale in its own language. We also add `rel` and `hreflang`
|
|
attributes to the `<a>` tag for SEO.
|
|
|
|
##### Handling locales that share content
|
|
|
|
Rather than iterating over pages, this example iterates over all of the configured
|
|
languages and finds the page for each one. This works better than the [Basic example](basic_example)
|
|
above on sites that have extra Django `LANGUAGES` that share the same Wagtail content.
|
|
|
|
For this example to work, you firstly need to add Django's
|
|
[django.template.context_processors.i18n](https://docs.djangoproject.com/en/3.1/ref/templates/api/#django-template-context-processors-i18n)
|
|
context processor to your `TEMPLATES` setting:
|
|
|
|
```python
|
|
# myproject/settings.py
|
|
|
|
TEMPLATES = [
|
|
{
|
|
# ...
|
|
'OPTIONS': {
|
|
'context_processors': [
|
|
# ...
|
|
'django.template.context_processors.i18n',
|
|
],
|
|
},
|
|
},
|
|
]
|
|
```
|
|
|
|
Now for the example itself:
|
|
|
|
```html+Django
|
|
{% for language_code, language_name in LANGUAGES %}
|
|
{% get_language_info for language_code as lang %}
|
|
|
|
{% language language_code %}
|
|
<a href="{% pageurl page.localized %}" rel="alternate" hreflang="{{ language_code }}">
|
|
{{ lang.name_local }}
|
|
</a>
|
|
{% endlanguage %}
|
|
{% endfor %}
|
|
```
|
|
|
|
Let's break this down too:
|
|
|
|
```html+Django
|
|
{% for language_code, language_name in LANGUAGES %}
|
|
...
|
|
{% endfor %}
|
|
```
|
|
|
|
This `for` block iterates through all of the configured languages on the site.
|
|
The `LANGUAGES` variable comes from the `django.template.context_processors.i18n`
|
|
context processor.
|
|
|
|
```html+Django
|
|
{% get_language_info for language_code as lang %}
|
|
```
|
|
|
|
Does exactly the same as the previous example.
|
|
|
|
```html+Django
|
|
{% language language_code %}
|
|
...
|
|
{% endlanguage %}
|
|
```
|
|
|
|
This `language` tag comes from Django's `i18n` tag library. It changes the
|
|
active language for just the code contained within it.
|
|
|
|
```html+Django
|
|
<a href="{% pageurl page.localized %}" rel="alternate" hreflang="{{ language_code }}">
|
|
{{ lang.name_local }}
|
|
</a>
|
|
```
|
|
|
|
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
|
|
integration with translation services.
|
|
|
|
Github: [https://github.com/wagtail/wagtail-localize](https://github.com/wagtail/wagtail-localize)
|
|
|
|
## Alternative internationalisation plugins
|
|
|
|
Before official multi-language support was added into Wagtail, site implementors
|
|
had to use external plugins. These have not been replaced by Wagtail's own
|
|
implementation as they use slightly different approaches, one of them might
|
|
fit your use case better:
|
|
|
|
- [Wagtailtrans](https://github.com/wagtail/wagtailtrans)
|
|
- [wagtail-modeltranslation](https://github.com/infoportugal/wagtail-modeltranslation)
|
|
|
|
For a comparison of these options, see AccordBox's blog post
|
|
[How to support multi-language in Wagtail CMS](https://www.accordbox.com/blog/how-support-multi-language-wagtail-cms/).
|
|
|
|
## Wagtail admin translations
|
|
|
|
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://www.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://www.transifex.com/torchbox/wagtail/). Translation updates are typically merged into an official release within one month of being submitted.
|
|
|
|
## Change Wagtail admin language on a per-user basis
|
|
|
|
Logged-in users can set their preferred language from `/admin/account/`.
|
|
By default, Wagtail provides a list of languages that have a >= 90% translation coverage.
|
|
It is possible to override this list via the [WAGTAILADMIN_PERMITTED_LANGUAGES](WAGTAILADMIN_PERMITTED_LANGUAGES) setting.
|
|
|
|
In case there is zero or one language permitted, the form will be hidden.
|
|
|
|
If there is no language selected by the user, the `LANGUAGE_CODE` will be used.
|
|
|
|
## Changing the primary language of your Wagtail installation
|
|
|
|
The default language of Wagtail is `en-us` (American English). You can change this by tweaking a couple of Django settings:
|
|
|
|
- Make sure [USE_I18N](https://docs.djangoproject.com/en/stable/ref/settings/#use-i18n) is set to `True`
|
|
- Set [LANGUAGE_CODE](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-LANGUAGE_CODE) to your websites' primary language
|
|
|
|
If there is a translation available for your language, the Wagtail admin backend should now be in the language you've chosen.
|