Download Drupal translations with Composer

Although many articles already exist and new ones are regularly written about what Composer is and how to use it with Drupal, here's a quick reminder.

Point on Composer and comparison with Drush

Composer is a PHP package management tool, it allows you to:

  • download (mainly) PHP libraries with their sets of dependencies,
  • manage updates to these libraries,
  • execute scripts, informed in its composer.json file.

The use of Composer with Drupal has increased / been made mandatory with Drupal 8.

These advantages over Drush are that Composer can:

  • download the dependencies of recursively installed modules whether they be libraries or other Drupal modules,
  • download external libraries (to be placed in /libraries, the equivalent of Drupal 7's sites/all/libraries), as might using .make files with Drush, except that Composer can detect updates for these external libraries.
  • Where Drush Make required the Drupal source folder to be deleted each time the make was run and therefore required the use of symbolic links to the sources of custom modules/themes, to contributed files so as not to lose them, there is now no need to do this with Composer.

On the other hand, Drush Make allowed you to specify which translations to download at runtime. So for each community module/theme, Drush would download the translation that went with it automatically. Although for these translations to be taken into account by Drupal, a script had to be run on these translations in order to rename them according to a naming expected by Drupal (module_name-version.language_code.po) and move them to the Drupal community translations folder.

This downloading of translations is not possible with Composer, and having not found an existing extension for this, I therefore created a Composer extension based on the drupal-composer/drupal-scaffold one in order to allow the translations to be downloaded.

The point of being able to download Drupal translations via composer is to be able to package translations when deploying a new version.

In fact, it sometimes happens that a Drupal site has to be deployed on servers that are not connected to the internet for security reasons or because they are located in an internal network. In this case in order to avoid having to version community translations or having a Drupal site installed to retrieve translations when running build scripts, it is now possible to use this new Composer extension.

Using the Composer extension

https://github.com/FlorentTorregrosa/drupal-l10n

Edit 03/07/2018: The installation of the module has changed with its move to the drupal-composer group. See the article Updated drupal-l10n Composer plugin

Actually, you have to fill in the git repository manually in your composer.json:

"repositories": {
  ...
  "drupal-l10n": {
    "type": "vcs",
    "url": "https://github.com/FlorentTorregrosa/drupal-l10n"
  }
}

Then download the extension via Composer :

 

composer require drupal-composer/drupal-l10n: "dev-master"

Indicate the path where to download the translations and which languages you want :

"extra": {
  ...
  "drupal-l10n": {
    "destination": "translations/contrib",
    "languages": [
      "fr",
      "es"
    ]
  },
  ...
}

If you want to be able to download the translations by running a script, fill in the following script in the composer.json file:

"scripts": {
"drupal-l10n": "DrupalComposer\DrupalL10n\Plugin::download",
...
}

What does the extension do?

Once the extension is installed, each time a library is installed or updated the extension will check :

  • if it is a Drupal community project via filtering on the type filled in Composer:
    • drupal-core
    • drupal-module
    • drupal-theme
    • drupal-profile
  • if this is a development project and the command is run in development mode, the translation will be downloaded,
  • if it is a project with a specific version, development versions do not have a translation.

If the library passes this filtering, its version filled in Composer will be transformed into the Drupal version, for example version 1.0.0-beta1 of the Redis module in Composer for Drupal 8 corresponds to version 8.x-1.0-beta1 on Drupal.org.

Then the translations for this library are downloaded in the various languages required.

Performance note

I wanted to see if using local-only translations would reduce the time to install a Drupal site in a language other than English.

To carry out my tests, I used my Docker development environment (https://github.com/FlorentTorregrosa/docker-drupal-project), in which I measured the execution time of the " drush site-install " command (with the options that go with it).

In order to force local use of the translations, I added the following lines to the settings.php file.

$config['locale.settings']['translation']['path'] = 'translations/contrib';
$config['locale.settings']['translation']['use_source'] = 'local';

I also did the same with the installation profiles for the Drupalcamp base (https://github.com/Drupal-FR/socle-drupalcampfr) and the Drupal France site (https://github.com/Drupal-FR/site-drupalfr/tree/8.x-1.x) in order to see the results with profiles other than the standard installation profile.

Here is a table summarising the results:

LineInstallation profile / ConditionsLanguageExecution time
1Standarden2m 23s
2fr

7m 50s

7m 56s

3Standard / Local translationsen2m 5s
4fr

7m 46s

7m 51s

7m 45s

5Drupalcamp foundationfr13m 43s
6Drupalcamp foundation / Local translationsfr

13m 8s

13m 19s

7Drupalfrfr14m 46s
8Drupalfr / Local translationsfr14m 4s

Line 2: we can see that despite an initial installation, there is no gain on a new installation.

Lines 1 and 3, 2 and 4, 5 and 6, 7 and 8 : we can see that there is a slight gain in using the translations locally, but the time taken to import the translations remains substantial. Between lines 1 and 2, the execution time has more than tripled.

I went and looked in the Drupal 8 core code to see the impact of using local translations only, in the code for the function (core/include/install.core.inc file) that handles importing translations during installation :

/**
 * Imports languages via a batch process during installation.
 *
 * @param $install_state
 * An array of information about the current installation state.
 *
 * @return
 * The batch definition, if there are language files to import.
 */
function install_import_translations(&$install_state) {
  \Drupal::moduleHandler()->loadInclude('locale', 'translation.inc');
  // If there is more than one language or the single one is not English, we
  // should import translations.
  $operations = install_download_additional_translations_operations($install_state);
  $languages = \Drupal::languageManager()->getLanguages();
  if (count($languages) > 1 || !isset($languages['en'])) {
    $operations[] = ['_install_prepare_import', [array_keys($languages), $install_state['server_pattern']]];

    // Set up a batch to import translations for drupal core. Translation import
    // for contrib modules happens in install_import_translations_remaining.
    foreach ($languages as $language) {
      if (locale_translation_use_remote_source()) {
        $operations[] = ['locale_translation_batch_fetch_download', ['drupal', $language->getId()]];
      }
      $operations[] = ['locale_translation_batch_fetch_import', ['drupal', $language->getId(), []];
    }

    module_load_include('fetch.inc', 'locale');
    $batch = [
      operations' => $operations,
      title' => t('Updating translations.'),
      progress_message' => '',
      'error_message' => t('Error importing translation files'),
      finished' => 'locale_translation_batch_fetch_finished', 'file' => drupal_get_path('module', 'locale') . /locale.batch.inc',
    ];
    return $batch;
  }
}

We can see that using local translations removes one operation per language:

if (locale_translation_use_remote_source()) {
$operations[] = ['locale_translation_batch_fetch_download', ['drupal', $language->getId()]];
}

But the other operations nevertheless require Drupal to scan the modules/themes present and the translations available and import them, and it is these operations that take so much time.

We should see if it would be possible to improve performance on these operations.

Conclusion

With this extension, it seems to me that Composer no longer has anything to envy to Drush make :).

I would have liked to see a more significant time saving when importing translations.

I've opened a request on the drupal-composer group on Github (https://github.com/drupal-composer/drupal-scaffold/issues/64) so that the extension is part of the group and thus has more visibility.

Feel free to put a comment on this article (and in the request on Github) to indicate if you find the extension useful.

Edit 03/07/2018: The installation of the module has changed with its move to the drupal-composer group. See article Updated drupal-l10n Composer plugin

Comments

Add new comment