Drupal 8 – How to translate the Config API

Auteur(s) de l'article

As you probably already know, I live in Switzerland and - beside chocolate & cheese - this country has one another singularity: 4 official national languages. As result, more than 90% of our web creations need to be localized in at least 2 languages.
This peculiarity makes us experts in the creation of multilingual websites.
When creating applications using Drupal 8, there are many cases when the configuration forms containing data have to be managed by the client. The most concrete case is - for example - all the links of the social networks must be different according to the language.
I will explain here the whole process to enable configuration translation with a ConfigFormBase form. I will go through the creation of the structure and the schema generation as well as the activation of the translation system.

Prerequisites

Every solution to every problem is simple. It's the distance between the two where the mystery lies.

Derek Landy

Skulduggery Pleasant

If you do not know the Drupal 8 Config API system yet, check it out Drupal 8 - Differences between Configuration API & State API. You may also find more help directly on the official documentation.
This article is for experienced Drupal 8 developers.

Assets

Give me matter and I will build a world out of it.

Immanuel Kant

Before explaining how it all works, you can download all the sample files below.
This article example is testable via the downloadable test module here.
To help you understand this article, 2 translatable configuration forms will be created in our example:
  1. The first one is a simple form demonstrating the translation system of several types of fields:
    • Text - translatable
    • Select - untranslatable
    • Checkbox - untranslatable
    • Checkboxes - untranslatable
    • Radios - untranslatable
    • Textarea - translatable
    • CKeditor - translatable
  1. The second is a complex form, demonstrating the translation system via mapping and structure in sequence:
    • Nested fields with multiple children - translatable
    • Entity reference field - untranslatable
    • Textarea with preprocess in sequence (then stored as array) - translatable
As you can notice, several fields are "untranslatable". It's not a deliberate choice, but the Core Drupal requires this, and that's normal! Fields that are not translatable are fields called "key" -> "value". It is therefore normal to be unable to translate keys since they have their own Business Logic. As for the values, they are translatable via the standard translation function exposed by the Core.

Setup default values

We have 2 files because we have 2 different configurations.
  • SettingsForm -> my_module.settings.yml
  • SettingsAdvancedForm -> my_module.settings_advanced.yml
These structure files are always found in the config/install folder of the module. They correspond to the initialization of the configuration, ie the default values. These files are not mandatory by default in Drupal 8, but are required once we want to make our configuration translatable.
These files must be named in a very specific way, depending on the name of the corresponding configuration. You can find this name in the array of the getEditableConfigNames method of your configuration forms.
Let's see what the definition of the structure of our 2 forms looks like:
SettingsForm
SettingsAdvancedForm
Did you notice both content files correspond to what we save in database?! All the fields saved in the database must be there. In addition, the structure of your configuration is also represented in this file.
SettingsForm
title: ''                # simple text field
select: ''               # select field
checkbox: FALSE          # boolean checkbox
checkboxes: { }          # array of checkboxes
radios: ''               # the value selected from radios
message: ''              # content of textarea
ckeditor:
  value: ''              # ckeditor content
  format: 'basic_html'   # ckeditor format
SettingsAdvancedForm
site:                   # array containing our data
  title: ''             # simple text field (nested)
  content:
    value: ''           # ckeditor content
    format: basic_html  # ckeditor format
mails: { }              # Values, formated as array, transformed from the textarea
entity_reference: ''    # entity reference field

Setup the schema

The schema is a single file, regardless of how many configurations your module depends on. This file contains the tree structure corresponding to the previous step, but this time we don't add the default value to each key, but its type and a label.
CheatSheet des formats et types utilisables dans un schema.yml Drupal 8.
Cheatsheet formats and types usable in a Drupal schema.yml 8.
Thanks to the schema configuration, we can declare which key is translatable and which is its Label in the interface of translation for the configuration!
Are you wondering why we need to provide a complete schema when we do not use all the keys for translation? Well the schema is not used only for translations ! For example, when you edit a configuration, the forms will use the schema to "type-cast" the data, which ensures that a field defined as a boolean in your schema is a boolean and not a string when it's manipulated. In addition, this file is used for the automatic generation of the translation form, hence the need to add a Label to each key.
The schema must be created directly in the config/schema folder and must be named following the module name:my_module.schema.yml.
Here is what the schema file of our 2 forms looks like:
In short, the schema system, just like the structure file, is optional as long as you do not need to translate your configuration.

Activate the translation

Once you have created your structure file and schema file, you just have to declare to the translation system which forms expose the translations.
You just need to create the my_module.config_translation.yml file.
This file contains all the configuration exposed by the translation form. Each form must define a title, the path to access the form and finally all the configuration keys that it exposes.
You can now access the forms from the menu: Configuration > Regional and language > Configuration translation.

Add a translation tab

Now that your form is translatable, it is sometimes disconcerting to access it throught the "Configuration translation" page, hidden behind 3 levels of links. It would be much more convenient to access this page from a "Local task", exactly like the other pages and settings that Core Drupal 8 offers.
Local task de traduction pour le formulaire Core - Basic Site.
Local task - Basic Site Translation.
By default, the translation system will generate a "Local task" - or a tab - for the translations of your form, but you may not see it, because to see this action you need to have at least 1 "Local task". I admit it's a little far-fetched, but here's a workaround.
  1. Create a new "Local task" in your module using the my_module.links.task.yml file at the root of your module.
  2. Add an entry for each configuration form exposing translations.

Sources

For the most curious, here are some sources of additional information that inspired the creation of this article.
Gábor Hojtsy (29 october 2015). Configuration translation development. Retrieved from hojtsy.hu
Alex Tkachev (16 décembre 2014). Drupal 8 configuration management. Retrieved from amazeelabs.com