API Entity Translation
In Drupal 8, field language is no longer exposed via the public API. Instead, fields are attached to language-aware objects from which they "inherit" their language.
The key benefits here are:
- We no longer need to worry about field portability—this is handled internally by the entity object.
// Determine the $active_langcode somehow. $translation = $entity->getTranslation($active_langcode); $value = $translation->field_foo->value;
- We no longer need to pass the active language explicitly—we can just pass the translation object, which implements EntityInterface and is essentially a clone of the original object with a different internal language. In many cases, the code doesn’t need to know the language explicitly.
// 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 now have a reusable entity language negotiation API that can determine the most appropriate translation for a given context:
// Simplified code to generate a renderable array for an entity. function viewEntity(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { // The EntityManagerInterface::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->entityManager->getTranslationFromContext($entity, $langcode); $build = entity_do_stuff($translation, 'full'); return $build; }
We can also specify an optional $context
parameter that describes the context in which the translation 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 which translation object to return can be altered by modules. See LanguageManager::getFallbackCandidates() for more details.
Field data is shared across all translation objects. Changing the value of an untranslated field affects all translations.
$entity->langcode->value = 'en'; $translation = $entity->getTranslation('it'); $en_value = $entity->field_foo->value; // 'bar' $it_value = $translation->field_foo->value; // 'bella' $entity->field_untranslatable->value = 'baz'; $translation->field_untranslatable->value = 'zio'; $value = $entity->field_untranslatable->value; // 'zio'
You can instantiate a translation object from the original or from another translation using EntityInterface::getTranslation()
. If the active language is needed, use EntityInterface::language()
. The original (untranslated) entity can be retrieved via EntityInterface::getUntranslated()
.
$entity->langcode->value = 'en'; $translation = $entity->getTranslation('it'); $langcode = $translation->language()->id; // 'it' $untranslated_entity = $translation->getUntranslated(); $langcode = $untranslated_entity->language()->id; // 'en' $identical = $entity === $untranslated_entity; // TRUE $entity_langcode = $translation->getUntranslated()->language()->id; // 'en'
EntityInterface
now includes several methods to facilitate working with entity translations. To act on every available translation, use EntityInterface::getTranslationLanguages()
:
foreach ($entity->getTranslationLanguages() as $langcode => $language) { $translation = $entity->getTranslation($langcode); entity_do_stuff($translation); }
You can also add, remove, or check for the existence of translations:
if (!$entity->hasTranslation('fr')) { $translation = $entity->addTranslation('fr', array('field_foo' => 'bag')); } // Equivalent to: $translation = $entity->getTranslation('fr'); $translation->field_foo->value = 'bag'; // Accessing a removed translation throws an exception. $translation = $entity->getTranslation('it'); $entity->removeTranslation('it'); $value = $translation->field_foo->value; // throws InvalidArgumentException
When translations are added to or removed from the storage, the following hooks are triggered:
hook_entity_translation_insert()
hook_entity_translation_delete()
You can still retrieve a field’s language directly via its method:
$langcode = $translation->field_foo->getLangcode();
Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.