Entity Translation API
In Drupal 8 field language is no longer exposed in the public API, instead fields are attached to language-aware entity objects from which they "inherit" their language.
The major advantages here are:
- We do not need to worry about field translatability, as this is taken care of by the entity object internally.
// Determine the $active_langcode somehow. $translation = $entity->getTranslation($active_langcode); $value = $translation->field_foo->value;
- We no longer need to pass around the active language, in fact we can just pass around the translation object, which implements
EntityInterface
and is actually a clone of the original object, just with a different internal language. This means in many cases the resulting code can be language-unaware (of course if it is not explicitly dealing with language).
// Instantiate the proper translation object just once and pass it around // wherever it is needed. This is typically taken care of by core // subsystems and in many common cases an explicit retrieval of the // translation object is not needed. $langcode = Drupal::languageManager()->getLanguage(Language::TYPE_CONTENT); $translation = $entity->getTranslation($langcode); entity_do_stuff($translation); function entity_do_stuff(EntityInterface $entity) { $value = $entity->field_foo->value; // do stuff }
- We have now a reusable entity language negotiation API, that can be used to determine the entity translation that is most appropriate for a certain context:
// Simplified code to generate a renderable array for an entity. function viewEntity(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { // The EntityRepositoryInterface::getTranslationFromContext() method will // apply entity language negotiation logic to the whole entity object // and will return the proper translation object for the given context. // The $langcode parameter is optional and indicates the language of the // current context. If it is not specified the current content language // is used, which is the desired behavior during the rendering phase. // Note that field values are left alone in the process, so empty values // will just not be displayed. $langcode = NULL; $translation = $this->entityRepository->getTranslationFromContext($entity, $langcode); $build = entity_do_stuff($translation, 'full'); return $build; }
We can also specify an optional
$context
parameter, which can be used to describe the context where the translation object will be used:// Simplified token replacements generation code. function node_tokens($type, $tokens, array $data = array(), array $options = array()) { $replacements = array(); // If no language is specified for this context we just default to the // default entity language. if (!isset($options['langcode'])) { $langcode = Language::LANGCODE_DEFAULT; } // We pass a $context parameter describing the operation being performed. // The default operation is 'entity_view'. $context = array('operation' => 'node_tokens'); $translation = \Drupal::service('entity.repository')->getTranslationFromContext($data['node'], $langcode, $context); $items = $translation->get('body'); // do stuff return $replacements; }
The logic used to determine the translation object to be returned is alterable by modules. See
LanguageManager::getFallbackCandidates()
for more details.
The actual field data is shared among all the translation objects and changing the value of an untranslatable field automatically changes it for all the translation objects.
$entity->langcode->value = 'en';
$translation = $entity->getTranslation('it');
$en_value = $entity->field_foo->value; // $en_value is 'bar'
$it_value = $translation->field_foo->value; // $it_value is 'bella'
$entity->field_untranslatable->value = 'baz';
$translation->field_untranslatable->value = 'zio';
$value = $entity->field_untranslatable->value; // $value is 'zio'
In any moment a translation object can be instantiated from the original object or another translation object through the EntityInterface::getTranslation()
method. If the active language is explicitly needed, it can be retrieved through EntityInterface::language()
. The original entity can be retrieved through EntityInterface::getUntranslated()
.
$entity->langcode->value = 'en';
$translation = $entity->getTranslation('it');
$langcode = $translation->language()->getId(); // $langcode is 'it';
$untranslated_entity = $translation->getUntranslated();
$langcode = $untranslated_entity->language()->getId(); // $langcode is 'en';
$identical = $entity === $untranslated_entity; // $identical is TRUE
$entity_langcode = $translation->getUntranslated()->language()->getId(); // $entity_langcode is 'en'
EntityInterface
has now several methods that make easier to deal with entity translations. If a piece of code needs to act on every available translation, it can exploit EntityInterface::getTranslationLanguages()
:
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
$translation = $entity->getTranslation($langcode);
entity_do_stuff($translation);
}
There are also methods to add a translation, remove it or check for its existence:
if (!$entity->hasTranslation('fr')) {
$translation = $entity->addTranslation('fr', array('field_foo' => 'bag'))->save();
}
// Which is equivalent to the following code, although if an invalid language
// code is specified an exception is thrown.
$translation = $entity->getTranslation('fr');
$translation->field_foo->value = 'bag';
// Accessing a field on a removed translation object causes an exception to
// be thrown.
$translation = $entity->getTranslation('it');
$entity->removeTranslation('it');
$value = $translation->field_foo->value; // throws InvalidArgumentException
When entity translations are added to or removed from the storage the following hooks are fired respectively:
hook_entity_translation_insert()
hook_entity_translation_delete()
Field language can still be retrieved by calling the proper method on the field object itself:
$langcode = $translation->field_foo->getLangcode();
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion