Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll
12/04/2025, by Ivan

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.