Télécharger des traductions Drupal avec Composer

Bien que de nombreux articles existent déjà et que régulièrement de nouveaux en sont écrits sur ce qu'est Composer et comment s'en servir avec Drupal, voici un petit rappel.

Point sur Composer et comparaison avec Drush

Composer est un outil de gestion de paquets PHP, il permet de :

  • télécharger des librairies (principalement) PHP avec leurs lots de dépendances,

  • gérer les mises à jour de ces librairies,

  • exécuter des scripts, renseignés dans son fichier composer.json.

L'utilisation de Composer avec Drupal a augmenté / été rendue obligatoire avec Drupal 8.

Ces avantages sur Drush sont que Composer peut :

  • télécharger les dépendances des modules installés de manière récursives que ce soit des librairies ou d'autres modules Drupal,

  • télécharger des librairies externes (à placer dans /librairies, l'équivalent du sites/all/librairies de Drupal 7), comme pourrait le faire l'utilisation de fichiers .make avec Drush, sauf que Composer peut détecter les mises à jour pour ces librairies externes.

  • Là où Drush Make nécessitait d'effacer le dossier des sources Drupal à chaque exécution du make et donc nécessitait l'utilisation de liens symboliques vers les sources des modules/thèmes custom, vers les fichiers contribués pour ne pas les perdre, il n'y a désormais plus besoin de faire cela avec Composer.

En revanche, Drush Make permettait de préciser les traductions à télécharger lors de son exécution. Ainsi pour chaque module/thème communautaire, Drush téléchargeait la traduction qui allait avec automatiquement. Bien que pour que ces traductions soient prises en compte par Drupal, il fallait exécuter un script sur ces traductions afin de les renommer selon un nommage attendu par Drupal (nom_du_module-version.code_langue.po) et les déplacer dans le dossier des traductions communautaire de Drupal.

Ce téléchargement des traductions n'étant pas possible avec Composer, et n'ayant pas trouvé d'extension existante pour cela, j'ai donc créé une extension Composer en me basant sur celle de drupal-composer/drupal-scaffold afin de permettre le téléchargement des traductions.

L'intérêt de pouvoir télécharger les traductions Drupal via composer est de pouvoir packager les traductions lors du déploiement d'une nouvelle version.

En effet, il arrive de devoir déployer un site Drupal sur des serveurs n'étant pas connectés à internet pour des raisons de sécurité ou car ils situés dans un réseau interne. Dans ce cas afin d'éviter de devoir versionner les traductions communautaires ou d'avoir un site Drupal installé pour récupérer les traductions lors de l'exécution de scripts de build, il est désormais possible d'utiliser cette nouvelle extension Composer.

Utilisation de l'extension Composer

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

Actuellement, il faut renseigner le dépôt git manuellement dans votre fichier composer.json :

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

Puis télécharger l'extension via Composer :

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

Indiquer le chemin où télécharger les traductions et quelles sont les langues souhaitées :

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

Si vous souhaitez pouvoir télécharger les traductions en exécutant un script, renseignez le script suivant dans le fichier composer.json :

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

Que fait l'extension ?

Une fois l'extension installée, à chaque installation ou mise à jour d'une librairie l'extension va vérifier :

  • s'il s'agit d'un projet communautaire Drupal via un filtrage sur le type renseigné dans Composer :

    • drupal-core

    • drupal-module

    • drupal-theme

    • drupal-profile

  • s'il s'agit d'un projet de développement et que la commande est exécutée en mode développement, la traduction sera téléchargée,

  • s'il s'agit d'un projet avec une version précise, les versions de développements n'ont pas de traduction.

Si la librairie passe ce filtrage, sa version renseignée dans Composer va être transformée en version Drupal, par exemple la version 1.0.0-beta1 du module Redis dans Composer pour Drupal 8 correspond à la version 8.x-1.0-beta1 sur Drupal.org.

Puis les traductions pour cette librairie sont téléchargées dans les différentes langues souhaitées.

Note sur les performances

J'ai voulu voir si l'utilisation des traduction en local uniquement permettait de réduire le temps d'installation d'un site Drupal dans une langue autre que l'anglais.

Afin de réaliser mes tests, j'ai utilisé mon environnement de développement sous Docker (https://github.com/FlorentTorregrosa/docker-drupal-project), dans lequel j'ai mesuré le temps d'exécution de la commande « drush site-install » (avec les options qui vont avec).

Afin de forcer l'utilisation en local des traductions, j'ai ajouté les lignes suivantes dans le fichier settings.php.

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

J'ai également procédé de même avec les profils d'installation du socle Drupalcamp (https://github.com/Drupal-FR/socle-drupalcampfr) et du site Drupal France (https://github.com/Drupal-FR/site-drupalfr/tree/8.x-1.x) afin de voir les résultats avec des profiles autre que le profil d'installation standard.

Voici un tableau récapitulatif des résultats :

Ligne Profil d'installation / Conditions Langue Temps d'exécution
1 Standard en 2m 23s
2 fr

7m 50s

7m 56s

3 Standard / Traductions locales en 2m 5s
4 fr

7m 46s

7m 51s

7m 45s

5 Socle Drupalcamp fr 13m 43s
6 Socle Drupalcamp / Traductions locales fr

13m 8s

13m 19s

7 Drupalfr fr 14m 46s
8 Drupalfr / Traductions locales fr 14m 4s

Ligne 2 : on peut voir que malgré une première installation, il n'y a pas de gain lors d'une nouvelle installation.

Lignes 1 et 3, 2 et 4, 5 et 6, 7 et 8 : on peut voir qu'il y a un léger gain à utiliser les traductions en local, mais le temps mis pour l'import des traductions reste conséquent. Entre les lignes 1 et 2, le temps d'exécution a plus que triplé.

Je suis allé voir dans le code du noyau Drupal 8 afin de voir l'impact de l'utilisation des traductions locales uniquement, dans le code de la fonction (fichier core/include/install.core.inc) qui gère l'import des traductions lors de l'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;
  }
}

On peut voir qu'utiliser les traductions locales permet de retirer une opération par langue :

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

Mais les autres opérations nécessitent néanmoins que Drupal scanne les modules/thèmes présents et les traductions disponibles et qu'il les importent et ce sont ces opérations qui prennent autant de temps.

Il faudrait voir s'il serait possible d'améliorer les performances sur ces opérations.

Conclusion

Avec cette extension, il me semble que Composer n'a plus rien à envier à Drush make :).

J'aurai bien aimé qu'il y ait un gain de temps plus significatif au niveau de l'import des traductions.

J'ai ouvert une demande sur le groupe drupal-composer sur Github (https://github.com/drupal-composer/drupal-scaffold/issues/64) afin que l'extension fasse partie du groupe et ait ainsi plus de visibilité.

N'hésitez pas à mettre un commentaire sur cette article (et dans la demande sur Github) afin d'indiquer si vous trouvez l'extension utile.

Commentaires

Un grand merci pour cet article de qualité. Pour ma part je ne m'étais jamais penché sur Composer, et ca permet de démystifier un peu le tout.

Merci Florent pour cet article et cette extension très utile. Sur un projet je suis exactement dans ce use case, un drupal hors ligne.

Une question: ayant ajouté cette extension sur un projet existant, seules les traductions des nouvelles dépendances ou celles mises à jour sont téléchargées. A-t-on la possibilité de récupérer les traductions des autres dépendances existantes?

Merci!

Merci pour ton commentaire Manu.

Oui, tu peux retélécharger les traductions pour les modules/thèmes déjà présents. Avec la commande "composer run-script drupal-l10n", le script "drupal-l10n" ayant été défini dans ton composer.json comme dans l'article.

C'est vraiment génial, ça fonctionne parfaitement bien!

Merci encore :)

Ajouter un commentaire