It's possible to define a custom content type by placing necessary files into a custom module's directory structure under config/[install | optional]. One might also want that content type to be translatable and may have even gone through the work to define some of the translations. If those translations are placed in config/[install | optional]/language/[language-code], then they should be pulled into the system when the module is enabled.

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes


vegantriathlete created an issue. See original summary.

Gábor Hojtsy’s picture

Thanks! Just tagging up as a start, will flesh this out a bit later.

vegantriathlete’s picture

Issue summary: View changes
vegantriathlete’s picture

Steps to reproduce:


You must have the Language module enabled, as well as Content Translation. You must also have some language installed.


  1. Create a new content type through the web interface, ensuring that it is set to be translatable (Language Settings have Enable translation checked).
  2. Add fields to the content type.
  3. Choose to translate the content type and translate the name.
  4. Manage the fields and translate them.
  5. Export the site configuration.
  6. Delete the content type.
  7. Create a custom module (including its .info.yml file) that places the necessary files inside of config/install/optional and config/install/optional/language/[language-code]
  8. Enable the custom module

Expected behavior

You see the new content type, including all its fields and the translated values.

Current behavior

You see the new content type, including all its fields. However none of the translations appear. If you go to a Translate area, you just see the installed language with the Add button.

vegantriathlete’s picture

Title: Process translation config files » Process translation config files for custom modules
vegantriathlete’s picture

Issue summary: View changes
vegantriathlete’s picture

I think we've got the same issue with things like tour.tour.some-tour.yml. Each of those YAML files has the language key. It makes sense that translations of tours should go inside of config/[install | optional]/lanuage/[language-code] as well.

Gábor Hojtsy’s picture

Just found #2831126: Translation strings from install profile config language/[langcode]/*.yml files don't appear in locale_target which seems to be a duplicate of this one. We should decide to close that one or this one.

Gábor Hojtsy’s picture

Closed that one. Bringing over this comment from @andypost there:

Steps to reproduce
1) create install profile with some additional language (ru)
2) add language/ru dir with some translated config
3) use drush to install site (default en locale used)
4) config overrides from language/ru are not imported

This because $override is not new in so it deleted

So according to that the config overrides may in fact be imported but then deleted because they don't end up in locales target.

Gábor Hojtsy’s picture

Issue tags: +SprintWeekend2017, +sprint
andypost’s picture

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

ckaotik’s picture

As an ugly workaround, we've been putting optional configuration's translations into config/install/language/{langcode}, but that of course creates other problems.
Update: Configuration translation in config/optional works if instead of a directory structure you preprend the collection to the config name, e.g. confg/optional/language.{langcode}.{configuration name}.

After some digging around, it seems to me this might be located in ConfigInstaller:installDefaultConfig(). Files in the config/install directory get special collection handling (e.g for language.{langcode})

    // Gets profile storages to search for overrides if necessary.
    $profile_storages = $this->getProfileStorages($name);

    // Gather information about all the supported collections.
    $collection_info = $this->configManager->getConfigCollectionInfo();
    foreach ($collection_info->getCollectionNames() as $collection) {
      $config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storages);
      // If we're installing a profile ensure configuration that is overriding
      // is excluded.
      if ($name == $this->drupalGetProfile()) {
        $existing_configuration = $this->getActiveStorages($collection)->listAll();
        $config_to_create = array_diff_key($config_to_create, array_flip($existing_configuration));
      if (!empty($config_to_create)) {
        $this->createConfiguration($collection, $config_to_create);

while optional config gets handled separately using ConfigInstaller::installOptionalConfig()

   if (is_dir($optional_install_path)) {
      // Install any optional config the module provides.
      $storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION);
      $this->installOptionalConfig($storage, '');
    // Install any optional configuration entities whose dependencies can now
    // be met. This searches all the installed modules config/optional
    // directories.
    $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE);
    $this->installOptionalConfig($storage, [$type => $name]);

which then does not check for collections at all: $config_to_create = $storage->readMultiple($list);.

wesleydv’s picture

Cross posting because I originally posted it in the duplicate of this issue

We encountered this issue when developing a multilingual distribution.
We copied to code from .profile file in to overcome it.

We also applied this patch from this issue

However we’ve noticed that field label translations kept loosing their translation after we did a locale-update

The only solution we could find is to also add the strings in a .po file that is imported.

I’m not sure if it is related to this issue, if not we can still create a separate issue.
But my colleagues and I spend way too much time on this, to not document it here ;-).

We plan to opensource this distribution, so I will add a new comment as soon as that is done (normally next week) so everybody can see exactly what we did.

andypost’s picture

Version: 8.3.x-dev » 8.5.x-dev

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

aralnoth’s picture

A possible workaround when you have some override locales in 'install/language/{langcode}' folders is to add an update function in the module that has the files with the following line:

\Drupal::service('language.config_factory_override')->installLanguageOverrides( '{langcode}' );

This fixes the problem because as a commented here, the locale-update command no works. I’m not sure if it is related to this issue but I leave this possible solution here in case it is helpful to someone else.