API перевода Entity
В Drupal 8 язык полей больше не предоставляется в общедоступном API, вместо этого поля присоединяются к объектам с поддержкой языка, от которых они «наследуют» свой язык.
Основными преимуществами здесь являются:
- Нам не нужно беспокоиться о переносимости полей, так как об этом заботится объект сущности внутри.
// Determine the $active_langcode somehow.
$translation = $entity->getTranslation($active_langcode);
$value = $translation->field_foo->value;
- Нам больше не нужно передавать активный язык, фактически мы можем просто передать объект перевода, который реализует EntityInterface и фактически является клоном исходного объекта, просто с другим внутренним языком. Это означает, что во многих случаях полученный код может не знать язык (конечно, если он явно не имеет отношения к языку).
// 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
}
- Теперь у нас есть повторно используемый API согласования на языке entity, который можно использовать для определения перевода сущности, который наиболее подходит для определенного контекста:
// 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;
}
Мы также можем указать необязательный параметр $context, который можно использовать для описания контекста, в котором будет использоваться объект перевода:
// 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;
}
Логика, используемая для определения возвращаемого объекта перевода, может изменяться модулями. См. LanguageManager :getFallbackCandidates() для более подробной информации.
Фактические данные поля распределяются между всеми объектами перевода, и изменение значения непереводимого поля автоматически изменяет его для всех объектов перевода.
$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'
В любой момент можно создать экземпляр объекта перевода из исходного объекта или другого объекта перевода с помощью метода EntityInterface::getTranslation(). Если активный язык явно необходим, его можно получить через EntityInterface::language(). Исходная сущность может быть получена через EntityInterface::getUntranslated().
$entity->langcode->value = 'en';
$translation = $entity->getTranslation('it');
$langcode = $translation->language()->id; // $langcode is 'it';
$untranslated_entity = $translation->getUntranslated();
$langcode = $untranslated_entity->language()->id; // $langcode is 'en';
$identical = $entity === $untranslated_entity; // $identical is TRUE
$entity_langcode = $translation->getUntranslated()->language()->id; // $entity_langcode is 'en'
EntityInterface теперь имеет несколько методов, которые облегчают работу с переводами сущностей. Если фрагмент кода должен действовать для каждого доступного перевода, он может использовать EntityInterface::getTranslationLanguages():
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
$translation = $entity->getTranslation($langcode);
entity_do_stuff($translation);
}
Существуют также способы добавить перевод, удалить его или проверить его наличие:
if (!$entity->hasTranslation('fr')) {
$translation = $entity->addTranslation('fr', array('field_foo' => 'bag'));
}
// 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
Когда переводы entity добавляются в хранилище или удаляются из него, соответственно запускаются следующие ловушки:
- hook_entity_translation_insert()
- hook_entity_translation_delete()
Язык поля все еще можно получить, вызвав соответствующий метод для самого объекта поля:
$langcode = $translation->field_foo->getLangcode();