Drupal 8 multilingual tidbits 15: configuration translation basics

Up to date as of October 29th, 2015.

After a long 8 months break in the article series, we are back to talk about configuration translation basics. Why the long break? Well, both the configuration and content system was in heavy development with changes and I did not want to get you content that would be quickly outdated. In the meantime Alex Pott also posted a great set of articles titled Principles of Configuration Management Part 1 and Part 2 which serves as great introductions. We’ll cover configuration translation first because that is more baked.

The Drupal 8 configuration system is a boon for language

As I wrote in the previous article in the series, configuration is now encompassing lots of settings that were variables or used custom settings storage in Drupal 7. The biggest value for non-English and multilingual sites in Drupal 8 of the configuration changes is that now a common system is used to manage your site name, email text settings through to views, field settings, entity form displays, etc. We can introduce language and translation support in a way that modules will need to plan with. It is not just an optional contributed add-on but a core feature.

How we know about the language of your configuration?

The Drupal 8 configuration system uses YAML files to transfer settings. (Active configuration is actually stored in the database). These are really simple text based files that have an internal nested-tree data format to them. On the Drupal side, they are just a simple way to store a nested array of data. For language support, the granularity of these files is of importance because we store/support language on the file level. For example, your site settings by default is the following:

uuid: ''
name: ''
mail: ''
slogan: ''
  403: ''
  404: ''
  front: /user/login
admin_compact_mode: false
weight_select_max: 100
langcode: en
default_langcode: en

This file is located at core/modules/system/config/install/system.site.yml and as the file location indicates only used at installation time to set up the default site settings. The installer itself fills in the missing pieces, such as default mail address, the site name and uuid and copies this over to the active configuration storage in the database. This file is not used anymore after that and is not changed at its original location.

The langcode key is a “reserved key” we use on the file level to identify the language of the file itself. The “en” code in the file above means that all textual content in this file is considered English for the system. The default_langcode key is unique to this file. That is how we identify the default language of the site. When you install Drupal, it sets both of these language codes to the language used in the installer.

The site default language may be changed later, while there is no widget in core to change the language of this configuration file in particular. In the fifth article of my series I covered language assignment across Drupal. When you edit a view, its language can be assigned in the main metadata popup. You can assign language to menus, contact categories, etc. using a simple language selector on them, however site configuration like the site name or account settings do not have this widget. A contributed module can provide this setting.

The top level langcode key sets up the boundaries of language assignment granularity for Drupal 8 configuration. A view is stored in one file, so we can store the language of the view overall and we’ll need to assume all strings in the view are in that language, including display names, pager text, empty result information, etc. All user emails sent by the built-in user module are stored in one file, so all those mail text configuration are supposed to be in the same language.

So that ends up in a mess of configuration in all kinds of languages?

Sort of. When Drupal and its modules are installed, all of their configuration items are created in English at first. If you did install in a foreign language though, English is not even a configured language on the site, and built-in configuration is rewritten to be in the language you installed with (thanks to the Interface translation module). All the new configuration you create will then also be in your site’s language. If you add back English or add any number of other languages, you will be able to create configuration in those languages too.

While configuration will be monolingual on English sites and other single language sites, if you have multiple languages configured, you can create new configuration in any of those languages. You may want to use French to create a menu that only applies on some French pages of the site for example. The configuration system is fully equipped to deal with this situation of multiple languages in configuration and is able to translate from all source languages to all other target languages. So if that French menu needs to be translated after all, you can just take the French source and translate from that to Spanish.

I made this figure to illustrate how a Spanish site might look like after French is added as well. The default installed configuration would be maintained in Spanish by Interface translation (with translations applied from the community), while French translations for those would also be managed by Interface translation. Additionally any new configuration may be created in both Spanish and French and then translation from there if needed. Of course a full translation is not required at all, but this is a possibility.

The ultimate result is all the configuration may be available in both Spanish and French. The translation is transparent to the rest of the system so when a view is used for example, the right language variant is loaded as needed.

We don’t have copies of the files though for each language

We keep track of configuration language information on each file, which lets us translate them to other languages. The way we translate them is not to duplicate the file but instead to use overrides which are loaded on top of the base file when needed. So our configuration translation system uses the same base configuration files but allows to replace textual elements in the configuration to ones in other languages.

There is nothing stopping contributed modules to make a configuration copy-based translation system available where one menu would be a translation of another menu and both would be high level configuration items for example. But that is not what Drupal core is doing. The configuration translation system in core instead is similarly setup like the new Drupal 8 content translation system that translates parts of the object as opposed to creating copies and relating them to each other.

How are the translations created?

All the originally shipped configuration is considered part of the software and is translated with the community translation system on localize.drupal.org. The system is integrated with the translation download and update system in core, so all community translations for shipped views, user emails, contact categories, etc. are downloaded in the installer and translation updates to them are available and updated on the site.

To translate built-in configuration like user mail settings, you can go to the regular interface translation screen and search for the settings text. That is not the most convenient way to translate all the built-in configuration, but this showcases the integration with configuration translation well.

When translating configuration this way, the translation is written back to the base active configuration (if the base configuration is in the target language you are translating to) or to translation overrides (for all other languages). This is illustrated on the figure above, where Spanish is the base site language, so configuration is translated in-place to Spanish and French is maintained in translation overrides. Both integrate seamlessly with Interface translation.

Drupal core also includes a Configuration translation module which exposes a user interface to translate all the configuration (with overlapping support for shipped configuration with Interface translation). The same account email settings translation is accessible with this module enabled on a very convenient “Translate” tab from account settings.

This lists all available languages to translate to, allowing us to edit the French translation (Spanish is maintained in the active configuration). These screens are a lot more convenient but more spread out compared to translating on the interface translation UI.

Configuration screens across Drupal core have a tab to allow for translate and/or their summary lists have translation operations listed. For example you can also translate views with operations in the views administration listing.

Of course you may not want to let your site translators access to edit views on your site. That may be dangerous to allow. No problem! We also have a listing of all configuration that can be translated under regional settings that can be used to access the translation screens for all configuration. This is very useful if you are not a super-admin on the site and may not have access to original configuration pages. This overview offers ways to access all those translation pages. This is how the overview looks like on an English interface:

Finally it is good to know that configuration translation is governed by a single global permission 'Translate configuration', so people who do not have access to views or content types would still be able to access their translation screens if this permission is granted to them.

All right, that is how the translation process works, but if you are a developer, how do you make your configuration to support translation? We will cover exactly that in the next article.

Issues to work on

  • DONE! Work is ongoing to make the help text for configuration translation be easier to understand, so people can understand it without lengthy articles like this one. Help at https://drupal.org/node/2161801
  • DONE! The storage solution of translation overrides is changing to support translation deployments better. See https://drupal.org/node/2224887 for that discussion.
  • PARTLY DONE: We still need to make localize.drupal.org expose default configuration for translation. That is unfortunately a non-trivial problem, but we need to solve before translators are asked to translate for the release. See and help at https://drupal.org/node/1933988


Bjorn's picture

A little late to the party, but have just started looking into all the changes to multilingual Drupal in v8.

I'm stomped trying to find a place to configure a different front page per language in D8? (or 403/404 pages for that matter)

Under Configuration Translation only the site name and slogan show up and the rest of the settings apparently can't be configured per language. Am I overlooking something?

Gábor Hojtsy's picture

Right, we support translation in the textual sense. We do not support swapping out different values per language where it is not a translation. Eg. for configuring a different front page, we would need to be able to validate that with the menu system, etc. For configuring a different logo for example with translated text, we would need to have a file upload widget with validation, etc. Drupal does not let us have those widgets. Contributed modules can easily solve these problems, because the configuration override system is very capable to swap those values out as needed, it is just not possible with core only.

Bjorn's picture

Thanks for clearing things up, Gábor.

Gerardo's picture

Hi Gabor,

I'm trying to translate the theme settings form admin/appearance/settings, with the config_translation. Can you point me on the right direction? I tried adding a mytheme.config_translation.yml file with no luck.
Not sure if here is the right place to put my question.

Thanks in advance.

Gábor Hojtsy's picture

You first need a config schema for your theme. See test_basetheme.schema.yml and friends. That defines the structure for your config. All the default keys are defined in the theme_settings base type already, so if you have no additional settings, you just do this in themes/mytheme/config/schema/mytheme.schema.yml:

  type: theme_settings
  label: 'My theme settings'

Once you have this, you can tie the config translation to a route with myheme.config_translation.yml as you already tried.

Marcel's picture

Hi Gábor,

first of all, I like to thank you about your great blog which helped us a lot in the last days! :)

At the moment, we are building a new magazine with Drupal 8. During the last days, we were working on the multilingual setup of the configuration. We really want to keep an english default configuration and use translation files to just translate the necessary strings to german.

For now, everything seems to work as far as expected, except for one issue: After exporting configuration changes with „drush config-export“, newly created files contain langcode=de instead of langcode=en. Is there any way to avoid this? We’re worried that this could cause problems when these files get submitted to our repository without manual changes.

Our settings are: default_langcode=en (in system.site.php) and selected_langcode=de (in language.negotiation.yml), we don’t use any language negotiation methods except the last one, fallback, which is manually set to „de“.

Thanks a lot for your support and keep up the great work!

Marcel from Germany

Gábor Hojtsy's picture

Since your user interface is German, your configuration will get created in German. See ConfigEntityStorage::doCreate(). If you want configuration created in English use the language selector to specify the language (available on views, menus, etc) or use an English admin interface for creating these (for config that does not have a language selector like input formats or user roles).

Philipp's picture

Sorry to bother you with my own nooby module, but I'm just trying to make one simple url translatable and it's driving me mad :D

I don't see a translate-tab and when I try to access admin/config/media/newsletter/translate directly I get "Permission denied" (as admin).


  title: 'My Newsletter'
  base_route_name: my_newsletter.newsletter_settings
    - my_newsletter.newsletter_settings

  type: config_object
  label: 'Newsletter settings'
      type: url
      label: 'Privacy policy page'
      translatable: true

  path: '/admin/config/media/newsletter'
    _title: 'My Newsletter'
    _form: '\Drupal\my_newsletter\Form\NewsletterSettingsForm'
    _permission: 'administer newsletter'

  route_name: my_newsletter.newsletter_settings
  title: Settings
  base_route: my_newsletter.newsletter_settings

Am I missing something?

Gábor Hojtsy's picture

I don't think you are missing anything here. You need a config schema (done) with at least a translatable element (done). You need to map the config key to a base route (done) and a default tab so the tab will be added (done). The page would work even without the base tab. I would keep investigating the error, no obvious mistakes here to me.

Muli's picture

Bit late for the party, but i copied your settings and had problems to

- "Not seeing Translatable Tab:"

The Filename SHOULD BE :

and NOT:

you wrongly added a 's' in 'task'

- "permission denied"

Try to change the key in file my_newsletter.schema.yml (should corresponding with *names* in my_newsletter.config_translation.yml)

It could be that the configuration name have a problem when its identical with the routename in my_newsletter.routing.yml.

Changes ***my_newsletter.configuration_blabla*** with a custom key without *** before and after

  title: 'My Newsletter'
  base_route_name: my_newsletter.newsletter_settings
    - ***my_newsletter.configuration_blabla***

and in file 'my_newsletter.newsletter_settings':

type: config_object
  label: 'Newsletter settings'
      type: url
      label: 'Privacy policy page'
      translatable: true
Philipp's picture

Thanks for your help, Muli and Gábor! Indeed the configuration name should not be identical to the route name. Otherwise you get "permission denied". But that's not the only hidden must-have: you also need an install yml with some default values.

my_url: ''

Without this you get "permission denied" too.

It took me hours to find this out and this is so frustrating :(
@Gábor: Maybe you should complement your really helpful article. And do you think, that these two points – the identical routename issue and the mandatory install yml – are bugs?

Tony Miller's picture

The great thing about interface translation is the .po files are easy to work with. I can export a .po file for each language the site is translated into, customise the file by removing back-end interface strings that end-users will never see and then email the .po files to each of the translation teams asking them to populate all the translations. When I get the files back from the translation teams they can be imported straight in.

Translating configuration needs to be handled quite differently. Firstly all the translation teams need access to the site, and then they need to manually click into each string that needs to be translated. A developer needs to sit with them during this process to guide them through which pieces of config are not end-user facing so can be skipped.

It's great for developers to have the clear separation of translating interface vs configuration vs content and the overall management of translations seems much easier than it was with the mix of different contrib modules needed for D7. But for the translators, it is much harder to translate configuration than interface without any .po export/import options in the configuration translation module.

Gábor Hojtsy's picture

The fact that Drupal core does not support importing .po files for all of configuration translation does not mean a contributed module could not do that. But the storages are pretty different, so that would need to be kept in mind.

Anyhow, there is some overlap between the two, namely for shipped configuration, where the configuration included with projects (like Drupal core) are treated as part of the software also known as "built-in interface" and is therefore translatable on localize.drupal.org and via .po files. That supports translation updates from the community, etc. Only applies to configuration that was already part of the project though as distributed from drupal.org. A contributed module could expand this workflow to other use cases.

Add new comment