Fixing Invalid Translations for Drupal 8 Content Moderation
I recently migrated content from a Drupal 7 site to a new Drupal 8 install using core’s Migrate, Migrate Drupal and Migrate Drupal UI modules. A few months after the initial migration, I decided to enable core Content Moderation for use with one of my migrated content types. No beuno.
Just saving any content for which I had enabled Content Moderation resulted in this tasteless error:
The website encountered an unexpected error. Please try again later.
Clicking over to Recent log messages at /admin/reports/dblog
revealed the following exception:
Invalid translation language (und) specified.
It was being thrown from two different places when I attempted to save a node.
The details for each in order were:
Drupal\Core\Entity\EntityStorageException: Invalid translation language (und) specified. in Drupal\Core\Entity\Sql\SqlContentEntityStorage->save() (line 770 of /Applications/MAMP/htdocs/project/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).
InvalidArgumentException: Invalid translation language (und) specified. in Drupal\Core\Entity\ContentEntityBase->addTranslation() (line 823 of /Applications/MAMP/htdocs/project/core/lib/Drupal/Core/Entity/ContentEntityBase.php).
Several quick searches didn’t turn up anything exactly applicable, but I did find a pretty useful summary of a similar issue that put me on the right track.
Basically, my content migrated from a Drupal 7 site was set with a legacy langcode of und. If I just load a node and output its language attribute using the Devel module’s dpm() function, you can see what’s amiss:
use Drupal\node\Entity\Node;
$node = Node::load(<My NID>);
dpm($node->language()->getId()); // returns 'und', which was causing a problem
It’s the language specification of und that was preventing me from saving the content with Content Moderation enabled. Changing the language over to the site default, EN in my case, just took a minute with the following code.
use Drupal\node\Entity\Node;
$node = Node::load(<My NID>);
$langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
$node->set('langcode', $langcode);
$node->save();
dpm($node->language()->getId()); // now this returns 'en', which is good!
Now you can see that the language attribute is reflecting an appropriate value, which in my case meant I was able to save the node like normal with Content Moderation enabled. Fixed!
All that remained was doing the same operation for every node in the database. For my project there were only a couple of thousand nodes, so I just used the code below (I ran this at devel/php
but you could do it in a module or wherever is easiest).
use Drupal\node\Entity\Node;
$langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
$result = db_query("SELECT nid FROM node WHERE langcode = 'und' LIMIT 100");
foreach ($result as $record) {
$node = Node::load($record->nid);
$node->set('langcode', $langcode);
$node->save();
}
I limited my query to 100 nodes to avoid a timeout, then just executed the code several times until all my nodes were updated. If you have tons of nodes that need updating, you might consider writing a hook_cron or using batch operations.
Warning: The site I was working on wasn't multilingual. The code above sets every node with a language specification of und to the site default, which may not be what you want for a multilingual site.
That’s it! It seems simpler in retrospect but took me awhile to understand what was going on. I also created an issue on Drupal.org so hopefully this bug will get addressed eventually.