Translate programmatically with Drupal 8

Symbolic vintage flags in a kitchen

Drupal 8, natively multilingual, offers a GUI to be able to translate both the site configuration (field's label, view's title, etc.) and the contents themselves. But we sometimes need to translate programmatically contents  or configurations, particularly in the context of a website factory to generate such a multilingual site.

Discover some examples to help us translate on the fly both configurations and contents. The examples below assume that we have an original content in French and that we wish to associate their English translation.

We will use, in the snippets below, the loadByProperties() method to load an entity based on one of its properties, but we might as well load it from its identifier (if known) with the load() method. Each context has an appropriate solution.

Translate programmatically a node

// Translate a node.
$entitytype_manager = \Drupal::service('entity_type.manager');
$storageNode = $entitytype_manager->getStorage('node');
$random = new Random();

$titles = [
  'A propos' => 'About',
];

foreach ($titles as $title_fr => $title_en) {
  $node = $storageNode->loadByProperties(['title' => $title_fr]);
  $node = reset($node);
  if ($node && !$node->hasTranslation('en')) {
    $entity_array = $node->toArray();
    $translated_fields = [];
    $translated_fields['title'] = $title_en;
    $translated_fields['body'] = [
      'value' => '[EN] ' . $random->paragraphs(3),
      'format' => 'full_html'
    ];

    $translated_entity_array = array_merge($entity_array, $translated_fields);
    $node->addTranslation('en', $translated_entity_array)->save();
  }
}

Translate programmatically a menu link

// Translate a Link.
$entitytype_manager = \Drupal::service('entity_type.manager');
$storageMenuLinkContent = $entitytype_manager->getStorage('menu_link_content');

$links_title = [
  'A propos' => 'About',
];

foreach ($links_title as $link_title_fr => $link_title_en) {
  $link = $storageMenuLinkContent->loadByProperties(['title' => $link_title_fr]);
  $link = reset($link);
  if ($link && !$link->hasTranslation('en')) {
    $link->addTranslation('en', ['title' => $link_title_en])->save();
  }
}

Translate programmatically a block content

// Translate a blockContent.
$entitytype_manager = \Drupal::service('entity_type.manager');
$storageBlockContent = $entitytype_manager->getStorage('block_content');

// Social block.
$social_block = $storageBlockContent->loadByProperties(['info' => 'Suivez nous']);
$social_block = reset($social_block);
if ($social_block && !$social_block->hasTranslation('en')) {
  $entity_array = $social_block->toArray();
  $translated_fields = [];
  $translated_fields['info'] = 'Follow us';
  $translated_entity_array = array_merge($entity_array, $translated_fields);
  $social_block->addTranslation('en', $translated_entity_array)->save();
}

Translate programmatically a taxonomy term

// Translate a term.
$entitytype_manager = \Drupal::service('entity_type.manager');
$storageTerm = $entitytype_manager->getStorage('taxonomy_term');

$keywords = [
  'Recherche' => 'Search',
  'Enquête' => 'Survey',
  'Voiture' => 'Car',
];

foreach ($keywords as $keyword_fr => $keyword_en) {
  $term = $storageTerm->loadByProperties(['name' => $keyword_fr]);
  $term = reset($term);
  if ($term && !$term->hasTranslation('en')) {
    $entity_array = $term->toArray();
    $translated_fields = [];
    $translated_fields['name'] = $keyword_en;
    $translated_fields['description'] = [
      'value' => '[EN] ' . $random->paragraphs(3),
      'format' => 'full_html'
    ];
    $translated_fields['field_keyword_image'] = $entity_array['field_keyword_image'][0];
    $translated_fields['field_keyword_image']['alt'] = $keyword_en;
    $translated_entity_array = array_merge($entity_array, $translated_fields);
    $term->addTranslation('en', $translated_entity_array)->save();
  }
}

Translate configuration

To translate the configuration of a Drupal 8 website, we can provide configuration translated using configuration files .yml. We must place these configuration files in the config / install / language / en to import the English translation. For example, to import the translation of the content type Event created, we can place the node.type.event.yml file in that folder to automatically import the translation of the name and description of the content type.

#File node.type.event.yml
name: Event
description: 'Permit to publish events.'

But we may also need to translate dynamically, if we do not know the different configurations installed in advance. Then we will resort to the method getLanguageConfigOverride() from the languageManager service. Below are two examples using this method.

Translate programmatically a block configuration

// Translate a block configuration.
$blockConfig = \Drupal::languageManager()->getLanguageConfigOverride('en', 'block.block.follow_us');
$blockConfig->set('settings', ['label' => 'Follow us'])->save();

Translate programmatically a system configuration

// Translate a system configuration.
$site_name = \Drupal::config('system.site')->get('name');
$site_name = $site_name . ' [EN]';

$siteConfig = \Drupal::languageManager()->getLanguageConfigOverride('en', 'system.site');
$siteConfig->set('name', $site_name)
  ->set('slogan', 'The site slogan could be very long, be carreful')
  ->save();

These few snippets should allow us to translate programmatically most content entity type or configurations. Going further on translation and multilingual capabilities of Drupal 8, you can check out this excellent presentation Drupal 8's multilingual APIs -- integrate with all the things, which summarizes all the key concepts to implement multilingual Drupal 8 website.

 

Commentaires

Soumis par Tom Bisciglia (non vérifié) le 08/02/2019 à 18:52 - Permalien

Greetings! I was racking my brain over this problem in Drupal 8. Here's how I finally got it done, This code copies title, body, all fields, and the pathauto setting. I'm sure there's room for improvement here as some meta fields probably need to get processed too. But this should get you started!

// This assumes you have a $node variable that contains the node translation you're starting with
$node_trans = $node->addTranslation('en-au'); // sample using Australian English
$node_trans->setTitle($node->getTitle());
$node_trans_fields = $node->getTranslatableFields();
foreach ($node_trans_fields as $name => $field) {
if (substr($name, 0, 6) == 'field_' || in_array($name, ['body', 'path'])) {
$node_trans->set($name, $field->getValue());
}
}
try {
$node->save();
}
catch (\Exception $error) {
$add_status .= 'ERROR saving ';
}

Soumis par Gilbert (non vérifié) le 17/04/2019 à 21:21 - Permalien

Bonjour,

J'ai un site drupal multilingue avec comme option de détection de langue celle du navigateur.

Browser : Language from the browser's language settings.

La traduction d'interface (user interface translation) fonctionne à merveille, mais je souhaite néanmoins maintenir l'anglais comme langue des utilisateurs (people / users) créés à partir d'un navigateur avec français comme langue par défaut.

Comment faire svp ?
Merci !

Gilbert

Soumis par Matthieu SCASET (non vérifié) le 18/05/2019 à 11:32 - Permalien

Here's another useful snippet if you want to translate a FieldConfig instance.

Let's say you programmatically created a new field instance (e.g. a custom field on your User).

<code>
$langcode = 'fr'
$field_config = 'field.field.user.user.field_custom';
$key_to_translate = 'label';
$translated_value = 'Le label en FR';

$language_manager = \Drupal::languageManager();
$language_manager->getLanguageConfigOverride($langcode, $field_config)->set($key_to_translate, $translated_value)->save();
</code>

Soumis par Rachel Baker (non vérifié) le 30/11/2020 à 17:41 - Permalien

What if you have some text that is being passed through a controller?

Ajouter un commentaire