How-to deploy Drupal interface translations

At LimoenGroen we build mostly Dutch websites and sometimes multilingual sites. We therefore need a solid and developer friendly solution to deploy interface translations. Since I wrote the interface translation import functionality for Drupal 8, I am in a good position to use this functionality to the max. And, as firm believers in Open Source, we like to share our solution.

Starting points

  • Interface translations are stored in Git and deployed via our deployment system.
  • All translation are deployed, we want full control.
  • A developer can add and modify translations with Gettext po files.
  • Drush and Drupal core are used as much as possible.
  • Translations made by clients at production, should not be overridden by deployed translations.

The process

We distinguish three streams of interface translations, which are each treated differently:

  1. Community translations for core and contributed modules
  2. Interface translation for custom modules and theme
  3. Custom interface translations that override the other translations

Community translations

Translations of Drupal core and contributed modules provided by the community.

The Locale module, shipped with core, can download community translations from the translation server at Since we want to have full control over which translations gets used and when, we manually download translations from this server on the development environment. From there the translation files get stored in the Git repo. During deployment the translation on staging or production gets updated using the translation files stored in the Git repo.

Translations for custom code

Translations provided by custom modules or theme.

Custom modules and themes can contain new translatable strings. The developer either exports the translatable strings with the Potx module, or manually creates a gettext .po file with the strings and their translation. The translation file is stored in the Git repo for deployment. To inform the Locale module about these translation files, some meta data gets added to the module’s .info.yml file (technical details below).

Custom translations

Translation that override the other translations.

This translation stream should not be confused with the above. Custom translations override the other translations. They are typically created through the admin interface. Custom Translations are flagged in the database as custom. Custom translations are not overridden by the default translation import. In a project, custom translations are particularly useful to override community translations, for example to change message texts.

To create a custom translations file, the developer edits the translations in the interface and exports them with a special drush command (see below). This file is saved in the translations directory and stored in Git. Our deploy script imports this file with another special Drush command.

Custom translations made by the client at the production environment can be detected with the same drush commands. Export the custom translations from production with drush and compare the file with a development export of translations.

Technical details

These details will help you to create the above described interface translation workflow.


Locale module

Configure the Locale module (/admin/config/regional/translate/settings) to:

  • Never check for updates.
  • Only use local translation source.
  • Only overwrite imported translations.

Development environment

The development environment is configured differently from staging and production. It uses the local and remote translation source. We use different settings.php files for each environment, the settings file for development contains the below snippet.

// At DEV: Import translation from both l.d.o and local file system.
$config['locale.settings']['translation']['use_source'] = ‘remote_and_local';

Translation directory

The translations directory is placed outside the webroot and included in the Git repo.
 Set the interface translations directory to: ../translations (at /admin/config/media/file-system)

Custom module translations

Register the translation file by adding the lines below to the file. Note that the po file name does not contain a module version number (e.g.

interface translation project: {module name}
interface translation server pattern: modules/custom/%project/translations/%project.%language.po

The gettext .po file that contains the translations of the ‘example’ module is placed in the path assigned above. For example: modules/custom/example/translations/

Drush 9 commands

At dev: Import interface translations for core, contributed modules and custom modules:

drush locale-check
drush locale-update

At dev: Export custom translations:

drush locale:export nl --types=customized > ../translations/

At stage and prod: Import translation for core, contributed and custom code. And import custom translations:

drush locale-check
drush locale-update
drush locale:import nl ../translations/ --type=customized --override=all


There is still some work to be done. The Drush export command is not yet included in Drush 9. Please help getting this committed.

Exported interface translations have a wrong translation formula (for most languages). This affects both the drush export and export via the interface (/admin/config/regional/translate/export)