I have a batch in which i'm importing nodes from REST webservices. My application has two languages, FR and EN (default is FR). So, I'm creating the node in FR, then, I loop through all other languages (just EN for now) and try to do as following :

$languages = \Drupal::languageManager()->getLanguages();

foreach ($languages AS $langcode => $language) {
  if ($node->hasTranslation($langcode)) {
    $node_translation = $node->getTranslation($langcode);
  } else {
    $node_translation = $node->addTranslation($langcode, $node->toArray());

  $node_translation->status = 1;
  $node_translation->title = 'title en';
  $node_translation->subtitle = 'subtitle en';

And it crashes with a 500 error, reporting :

 - Drupal\Core\Entity\EntityStorageException : Invalid translation language (und) specified. dans Drupal\Core\Entity\Sql\SqlContentEntityStorage->save() (ligne 805 de /.../web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).
 - InvalidArgumentException : Invalid translation language (und) specified. dans Drupal\Core\Entity\ContentEntityBase->getTranslation() (ligne 783 de /.../web/core/lib/Drupal/Core/Entity/ContentEntityBase.php).

I'm pulling my hair out on this issue for days and can not understand why the error message tells me $langcode equals 'und' whereas a var_dump of it just before this line returns me 'en'.

Edit : I tried to hardcode and remove the foreach. So just after the $node->save() :

$node_translation = $node->addTranslation('en', $node->toArray());
$node_translation->set('title', 'title en');
$node_translation->set('field_sub_title', 'subtitle en');

This time, the error is the following :

InvalidArgumentException : Invalid translation language (en) specified.
dans Drupal\Core\Entity\ContentEntityBase->addTranslation() (ligne 863
de /var/www/drupalvm/drupal/web/core/lib/Drupal/Core/Entity/ContentEntityBase.php).
  • I tried the solutions given in this post, but it won't work drupal.stackexchange.com/questions/203149/… Commented Feb 1, 2018 at 16:47
  • If you run $node->language() before the foreach what does it contain?
    – johndevman
    Commented Feb 5, 2018 at 13:45
  • protected 'name' => string 'French' (length=6) protected 'id' => string 'fr' (length=2) protected 'direction' => string 'ltr' (length=3) protected 'weight' => int 1 protected 'locked' => boolean false Commented Feb 5, 2018 at 16:03

4 Answers 4


After several days, I finally found the solution. Obviously, there is a bug with Drupal (here 8.4.0).

The hasTranslation() method don't work properly. Instead, you should use EntityManager::getTranslationFromContext() (will be deprecated in Drupal 9 though).

You can also write a new method like this one to replace it:

 * Check if a node as a translation for the given language.
 * @param object $node
 *   The node object.
 * @param string $langcode
 *   The langcode.
 * @return bool
 *   True (exists) or false (does not exist).
function has_translation($node, $langcode) {
  $existing_translation = \Drupal::service('entity.repository')->getTranslationFromContext($node, $langcode);
  return ($existing_translation->langcode->value === $langcode) ? TRUE : FALSE;

So, my corrected code is the following:

if (has_translation($node, $langcode)) {
  $node_translation = $node->getTranslation($langcode);
  $node_translation->title = 'title en';
  $node_translation->field_sub_title = 'subtitle en';
  $node_translation->status = NodeInterface::PUBLISHED;
else {
  $node->addTranslation($langcode, $values);


You can try with simple code:

$node = Node::load(1);
if (!$node->hasTranslation('en')) {
    $translation = $node->addTranslation('en', $node->toArray());

You can't translate just because you wrong target. You need to know original language.

  • That's exactly what I'm doing after the foreach opens. Commented Feb 6, 2018 at 8:15
  • See in your code, try $node->save() don't $node_translation->save()
    – MrD
    Commented Feb 6, 2018 at 8:20
  • Oh, sorry, I didn't get it. Does not work by the way, always the "invalid translation language (en)" exception. Commented Feb 6, 2018 at 8:29
  • Just snippet code help you can translate a note. You can add to loop language and translate theme. See more example api.drupal.org/api/drupal/…
    – MrD
    Commented Feb 6, 2018 at 8:46

No code needed, you can use Lingotek Module and trigger by rules module on new node save and or updated, condition: check in what language is uploaded so you can create another node in the second language. be carefull with google, it will penalize you if you index both nodes, to take care of this you can use No index module or something similar. I made a site with all of above and works perfect.


The error in the original post comes from addTranslation() itself:

  if (!isset($this->languages[$langcode]) || $this
    ->hasTranslation($langcode) || $this->languages[$langcode]
    ->isLocked()) {
    throw new \InvalidArgumentException("Invalid translation language ({$langcode}) specified.");
  if ($this->languages[$this->defaultLangcode]
    ->isLocked()) {
    throw new \InvalidArgumentException("The entity cannot be translated since it is language neutral ({$this->defaultLangcode}).");

So your solution, adding the function has_translation(), amounts to doing some of the same checks that addTranslation() will do. An alternative is to catch the exception:

  try {
    $node->addTranslation($langcode, $node->toArray());
  catch (\InvalidArgumentException $e) {
    // The translation already exists.

I mean that catch clause literally: you can leave it out entirely or just put in a comment to reassure your future self that you did not forget something.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.