diff --git a/core/core.services.yml b/core/core.services.yml index 640440b..26a3e94 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -193,6 +193,9 @@ services: arguments: ['@event_dispatcher', '@service_container', '@controller_resolver'] language_manager: class: Drupal\Core\Language\LanguageManager + language_fallback_manager: + class: Drupal\Core\Language\NoFallbackManager + arguments: ['@language_manager'] string_translator.custom_strings: class: Drupal\Core\StringTranslation\Translator\CustomStrings tags: diff --git a/core/includes/language.inc b/core/includes/language.inc index e63acaa..eef7202 100644 --- a/core/includes/language.inc +++ b/core/includes/language.inc @@ -542,20 +542,14 @@ function language_url_split_prefix($path, $languages) { * * @return * An array of language codes. + * + * @see \Drupal\Core\Language\FallbackManagerInterface::getCandidates() + * + * @deprecated This has been deprectaed in favor of the language fallback + * manager. */ function language_fallback_get_candidates($type = Language::TYPE_CONTENT) { - $fallback_candidates = &drupal_static(__FUNCTION__); - - if (!isset($fallback_candidates)) { - // Get languages ordered by weight, add Language::LANGCODE_NOT_SPECIFIED at the end. - $fallback_candidates = array_keys(language_list()); - $fallback_candidates[] = Language::LANGCODE_NOT_SPECIFIED; - - // Let other modules hook in and add/change candidates. - drupal_alter('language_fallback_candidates', $fallback_candidates); - } - - return $fallback_candidates; + return Drupal::service('language_fallback_manager')->getCandidates(array('data' => $type)); } /** diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php index e2dc1b4..93fe769 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php @@ -7,7 +7,9 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Language\FallbackManagerInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManager; use PDO; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Entity\Query\QueryInterface; @@ -59,13 +61,29 @@ class DatabaseStorageController extends EntityStorageControllerBase { protected $database; /** + * The language manager service. + * + * @var \Drupal\Core\Language\LanguageManager + */ + protected $languageManager; + + /** + * The language fallback manager service. + * + * @var \Drupal\Core\Language\FallbackManagerInterface; + */ + protected $languageFallbackManager; + + /** * {@inheritdoc} */ public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) { return new static( $entity_type, $entity_info, - $container->get('database') + $container->get('database'), + $container->get('language_manager'), + $container->get('language_fallback_manager') ); } @@ -78,11 +96,15 @@ public static function createInstance(ContainerInterface $container, $entity_typ * An array of entity info for the entity type. * @param \Drupal\Core\Database\Connection $database * The database connection to be used. + * @param \Drupal\Core\Language\FallbackManagerInterface + * The language fallback manager. */ - public function __construct($entity_type, array $entity_info, Connection $database) { + public function __construct($entity_type, array $entity_info, Connection $database, LanguageManager $language_manager, FallbackManagerInterface $fallback_manager) { parent::__construct($entity_type, $entity_info); $this->database = $database; + $this->languageManager = $language_manager; + $this->languageFallbackManager = $fallback_manager; // Check if the entity type supports IDs. if (isset($this->entityInfo['entity_keys']['id'])) { @@ -367,6 +389,8 @@ public function create(array $values) { $entity_class::preCreate($this, $values); $entity = new $entity_class($values, $this->entityType); + $entity->setLanguageManager($this->languageManager); + $entity->setLanguageFallbackManager($this->languageFallbackManager); // Assign a new UUID if there is none yet. if ($this->uuidKey && !isset($entity->{$this->uuidKey})) { diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php index 143a2ab..4c79658 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php @@ -7,7 +7,9 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Language\FallbackManagerInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManager; use PDO; use Drupal\Core\Entity\Query\QueryInterface; @@ -53,8 +55,9 @@ class DatabaseStorageControllerNG extends DatabaseStorageController { /** * Overrides DatabaseStorageController::__construct(). */ - public function __construct($entity_type, array $entity_info, Connection $database) { - parent::__construct($entity_type,$entity_info, $database); + public function __construct($entity_type, array $entity_info, Connection $database, LanguageManager $languageManager, FallbackManagerInterface $fallback_manager) { + parent::__construct($entity_type, $entity_info, $database, $languageManager, $fallback_manager); + $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE; $this->entityClass = $this->entityInfo['class']; @@ -109,6 +112,8 @@ public function create(array $values) { $bundle = $values[$this->bundleKey]; } $entity = new $this->entityClass(array(), $this->entityType, $bundle); + $entity->setLanguageManager($this->languageManager); + $entity->setLanguageFallbackManager($this->languageFallbackManager); foreach ($entity as $name => $field) { if (isset($values[$name])) { @@ -248,6 +253,8 @@ protected function mapFromStorageRecords(array $records, $load_revision = FALSE) $bundle = $this->bundleKey ? $record->{$this->bundleKey} : FALSE; // Turn the record into an entity class. $entities[$id] = new $this->entityClass($entities[$id], $this->entityType, $bundle); + $entities[$id]->setLanguageManager($this->languageManager); + $entities[$id]->setLanguageFallbackManager($this->languageFallbackManager); } } $this->attachPropertyData($entities, $load_revision); @@ -319,6 +326,8 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) { $bundle = $this->bundleKey ? $values[$this->bundleKey][Language::LANGCODE_DEFAULT] : FALSE; // Turn the record into an entity class. $entities[$id] = new $this->entityClass($values, $this->entityType, $bundle, array_keys($translations[$id])); + $entities[$id]->setLanguageManager($this->languageManager); + $entities[$id]->setLanguageFallbackManager($this->languageFallbackManager); } } } @@ -548,8 +557,8 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $langcode) { $record = new \stdClass(); foreach (drupal_schema_fields_sql($this->entityInfo['data_table']) as $name) { $info = $schema['fields'][$name]; - $value = isset($definitions[$name]) && isset($translation->$name->value) ? $translation->$name->value : NULL; - $record->$name = drupal_schema_get_field_value($info, $value); + $value = isset($definitions[$name]) && isset($translation->$name->value) ? drupal_schema_get_field_value($info, $translation->$name->value) : NULL; + $record->$name = $value; } $record->langcode = $langcode; $record->default_langcode = intval($default_langcode == $langcode); diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 33a62ce..e9758e5 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -8,7 +8,9 @@ namespace Drupal\Core\Entity; use Drupal\Component\Uuid\Uuid; +use Drupal\Core\Language\FallbackManagerInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManager; use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\TypedData\TypedDataInterface; use IteratorAggregate; @@ -60,6 +62,22 @@ class Entity implements IteratorAggregate, EntityInterface { protected $isDefaultRevision = TRUE; /** + * The language manager to be used to retrieve languages and retrieve the + * current content language. + * + * @var \Drupal\Core\Language\LanguageManager + */ + protected $languageManager; + + /** + * The language fallback manager to be used to determine the current + * translation and field values fallback. + * + * @var \Drupal\Core\Language\FallbackManagerInterface + */ + protected $languageFallbackManager; + + /** * Constructs an Entity object. * * @param array $values @@ -120,6 +138,20 @@ public function setNewRevision($value = TRUE) { } /** + * {@inheritdoc} + */ + public function setLanguageManager(LanguageManager $language_manager) { + $this->languageManager = $language_manager; + } + + /** + * {@inheritdoc} + */ + public function setLanguageFallbackManager(FallbackManagerInterface $fallback_manager) { + $this->languageFallbackManager = $fallback_manager; + } + + /** * Implements \Drupal\Core\Entity\EntityInterface::entityType(). */ public function entityType() { @@ -304,6 +336,17 @@ public function getTranslation($langcode) { } /** + * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslation(). + * + * @return \Drupal\Core\Entity\EntityInterface + */ + public function getCurrentTranslation($langcode = NULL, $context = array()) { + // @todo: Replace by EntityNG implementation once all entity types have been + // converted to use the entity field API. + return $this; + } + + /** * Returns the languages the entity is translated to. * * @todo: Remove once all entity types implement the entity field API. @@ -637,4 +680,13 @@ public function initTranslation($langcode) { // http://drupal.org/node/2004244 } + /** + * {@inheritdoc} + */ + public function getFieldLangcode($name) { + // @todo Config entities do not support entity translation hence we need to + // move the TranslatableInterface implementation to EntityNG. See + // http://drupal.org/node/2004244 + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php index a3ffcb9..b5f6605 100644 --- a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php +++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php @@ -7,7 +7,9 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Language\FallbackManagerInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManager; use IteratorAggregate; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\TypedData\TypedDataInterface; @@ -269,6 +271,20 @@ public function setPropertyValues($values) { } /** + * {@inheritdoc} + */ + public function setLanguageManager(LanguageManager $language_manager) { + $this->decorated->setLanguageManager($languageManager); + } + + /** + * {@inheritdoc} + */ + public function setLanguageFallbackManager(FallbackManagerInterface $fallback_manager) { + $this->decorated->setLanguageFallbackManager($fallback_manager); + } + + /** * Forwards the call to the decorated entity. */ public function getPropertyDefinition($name) { @@ -430,6 +446,13 @@ public function getTranslation($langcode) { } /** + * {@inheritdoc} + */ + public function getCurrentTranslation($langcode = NULL, $context = array()) { + return $this->decorated->getCurrentTranslation($langcode = NULL, $context = array()); + } + + /** * Forwards the call to the decorated entity. */ public function getType() { @@ -626,4 +649,11 @@ public function initTranslation($langcode) { $this->decorated->initTranslation($langcode); } + /** + * {@inheritdoc} + */ + public function getFieldLangcode($name) { + return $this->decorated->getFieldLangcode($name); + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php index 36fb318..65660ea 100644 --- a/core/lib/Drupal/Core/Entity/EntityFormController.php +++ b/core/lib/Drupal/Core/Entity/EntityFormController.php @@ -403,15 +403,7 @@ public function getFormLangcode(array $form_state) { $langcode = $form_state['langcode']; } else { - // If no form langcode was provided we default to the current content - // language and inspect existing translations to find a valid fallback, - // if any. - $translations = $entity->getTranslationLanguages(); - $langcode = language(Language::TYPE_CONTENT)->id; - $fallback = language_multilingual() ? language_fallback_get_candidates() : array(); - while (!empty($langcode) && !isset($translations[$langcode])) { - $langcode = array_shift($fallback); - } + $langcode = $entity->getCurrentTranslation()->language()->id; } // If the site is not multilingual or no translation for the given form diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index e965b8e..64c9f99 100644 --- a/core/lib/Drupal/Core/Entity/EntityInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Language\LanguageManager; use Drupal\Core\TypedData\AccessibleInterface; use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\IdentifiableInterface; @@ -326,4 +327,9 @@ public function isTranslatable(); */ public function initTranslation($langcode); + /** + * TODO + */ + public function getFieldLangcode($name); + } diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php index a6768e8..d3eeaf5 100644 --- a/core/lib/Drupal/Core/Entity/EntityNG.php +++ b/core/lib/Drupal/Core/Entity/EntityNG.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Entity; use Drupal\Core\Language\Language; +use Drupal\Core\Language\LanguageManager; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\TypedDataInterface; use ArrayIterator; @@ -135,6 +136,20 @@ class EntityNG extends Entity { protected $translationInitialize = FALSE; /** + * TODO + * + * @var array + */ + protected $languageFallbackContext; + + /** + * TODO + * + * @var array + */ + protected $languageFallbackMap; + + /** * Overrides Entity::__construct(). */ public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = array()) { @@ -315,13 +330,54 @@ protected function getTranslatedField($property_name, $langcode) { $value = $this->values[$property_name][$default_langcode]; } } - $this->fields[$property_name][$langcode] = \Drupal::typedData()->getPropertyInstance($this, $property_name, $value); + // Apply per-field language fallback if enabled on this object. TODO + if (!isset($value) && isset($this->languageFallbackContext)) { + // Initialize the whole map when the first missing field is requested. + if (!isset($this->languageFallbackMap)) { + $this->initializeLanguageFallbackMap(); + } + // Avoid populating the field array with the requested language since + // it is missing, switch to the fallback language instead. + if (isset($this->languageFallbackMap[$property_name])) { + $langcode = $this->languageFallbackMap[$property_name]; + $value = $this->values[$property_name][$langcode]; + } + } + // We may have changed the requested language to the fallback language, + // hence we need to check again. + if (!isset($this->fields[$property_name][$langcode])) { + $this->fields[$property_name][$langcode] = \Drupal::typedData()->getPropertyInstance($this, $property_name, $value); + } } } return $this->fields[$property_name][$langcode]; } /** + * {@inheritdoc} + */ + public function getFieldLangcode($name) { + $langcode = NULL; + if (isset($this->languageFallbackContext)) { + if (!isset($this->languageFallbackMap)) { + $this->initializeLanguageFallbackMap(); + } + if (isset($this->languageFallbackMap[$name])) { + $langcode = $this->languageFallbackMap[$name]; + } + } + return $langcode; + } + + /** + * TODO + */ + protected function initializeLanguageFallbackMap() { + $this->updateOriginalValues(); + $this->languageFallbackMap = $this->languageFallbackManager->getValuesMap($this->values, $this->activeLangcode, $this->languageFallbackContext); + } + + /** * Implements \Drupal\Core\TypedData\ComplexDataInterface::set(). */ public function set($property_name, $value, $notify = TRUE) { @@ -391,6 +447,7 @@ public function getPropertyValues() { public function setPropertyValues($values) { foreach ($values as $name => $value) { $this->get($name)->setValue($value); + unset($this->languageFallbackMap[$name]); } } @@ -515,6 +572,10 @@ public function getTranslation($langcode) { throw new \InvalidArgumentException(format_string($message, array('@langcode' => $langcode))); } + // TODO + $translation->languageFallbackContext = NULL; + $translation->languageFallbackMap = NULL; + return $translation; } @@ -556,7 +617,47 @@ protected function initializeTranslation($langcode) { $translation->values = &$this->values; $translation->fields = &$this->fields; $translation->translations = &$this->translations; - $translation->translationInitialize = FALSE; + $translation->languageFallbackContext = NULL; + $translation->languageFallbackMap = NULL; + + return $translation; + } + + /** + * {@inheritdoc} + */ + public function getCurrentTranslation($langcode = NULL, $context = array()) { + $translation = $this; + + if (count($this->translations) > 1 && !isset($this->languageFallbackContext)) { + // Determine the requested language. Default to the current content + // language. + if (!isset($langcode)) { + $langcode = $this->languageManager->getLanguage(Language::TYPE_CONTENT)->id; + } + + if (!empty($langcode)) { + // TODO + $context['langcode'] = $langcode; + $context['data'] = $this; + $context += array('operation' => 'entity_view'); + $candidates = $this->languageFallbackManager->getCandidates($context); + + foreach ($candidates as $candidate) { + if ($this->hasTranslation($candidate)) { + $translation = $this->getTranslation($candidate); + // If this is the original object we assume there is nothing to fall + // back to. + if ($translation->activeLangcode != Language::LANGCODE_DEFAULT) { + // Initialize the language fallback context to mark the object as + // fallback-enabled. + $translation->languageFallbackContext = $context; + } + break; + } + } + } + } return $translation; } @@ -565,6 +666,10 @@ protected function initializeTranslation($langcode) { * {@inheritdoc} */ public function hasTranslation($langcode) { + $default_language = $this->language ?: $this->getDefaultLanguage(); + if ($langcode == $default_language->id) { + $langcode = Language::LANGCODE_DEFAULT; + } return !empty($this->translations[$langcode]['status']); } diff --git a/core/lib/Drupal/Core/Language/FallbackManagerInterface.php b/core/lib/Drupal/Core/Language/FallbackManagerInterface.php new file mode 100644 index 0000000..209dd66 --- /dev/null +++ b/core/lib/Drupal/Core/Language/FallbackManagerInterface.php @@ -0,0 +1,35 @@ +weight) ? $a->weight : 0; $b_weight = isset($b->weight) ? $b->weight : 0; diff --git a/core/lib/Drupal/Core/Language/NoFallbackManager.php b/core/lib/Drupal/Core/Language/NoFallbackManager.php new file mode 100644 index 0000000..fe4f607 --- /dev/null +++ b/core/lib/Drupal/Core/Language/NoFallbackManager.php @@ -0,0 +1,43 @@ +languageManager = $language_manager; + } + + /** + * {@inheritdoc} + */ + public function getCandidates(array $context = array()) { + return array(Language::LANGCODE_NOT_SPECIFIED); + } + + /** + * {@inheritdoc} + */ + public function getValuesMap(array $values, $langcode, array $context = array()) { + return array(); + } + +} diff --git a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php index 0d5332b..aec7807 100644 --- a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php +++ b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php @@ -7,6 +7,9 @@ namespace Drupal\Core\TypedData; +use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Language\FallbackManagerInterface; + /** * Interface for translatable data. */ @@ -21,6 +24,22 @@ public function language(); /** + * Sets the language manager to be used when dealing with languages. + * + * @param \Drupal\Core\Language\LanguageManager + * The language manager. + */ + public function setLanguageManager(LanguageManager $language_manager); + + /** + * Sets the language fallback manager. + * + * @param \Drupal\Core\Language\FallbackManagerInterface + * The language fallback manager. + */ + public function setLanguageFallbackManager(FallbackManagerInterface $fallback_manager); + + /** * Returns the languages the data is translated to. * * @param bool $include_default @@ -49,6 +68,27 @@ public function getTranslationLanguages($include_default = TRUE); */ public function getTranslation($langcode); + /** + * Returns the translation to be used in the current context. + * + * This will check whether a translation for the requested langauge is + * available and if it is not, it will fall back to the most appropriate + * translation based on the available context provided by the language + * manager. + * + * @param string $language + * (optional) The language code of the translation to be retrieved. Defaults + * to the current language. + * @param array $context + * (optional) An associative arra of arbitrary data that can be useful to + * determine the proper fallback chain. + * + * @return \Drupal\Core\TypedData\TypedDataInterface + * A typed data object for the translated data. + * + * @see \Drupal\Core\Language\FallbackManagerInterface + */ + public function getCurrentTranslation($langcode = NULL, $context = array()); /** * Returns the translatable object referring to the original language. diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index a6988cf..9bc0378 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -631,39 +631,16 @@ function content_translation_form_alter(array &$form, array &$form_state) { } /** - * Implements hook_field_language_alter(). + * Implements hook_language_fallback_candidates_OPERATION_alter(). * * Performs language fallback for unaccessible translations. */ -function content_translation_field_language_alter(&$display_language, $context) { - $entity = $context['entity']; - $entity_type = $entity->entityType(); - - if (isset($entity->translation[$context['langcode']]) && $entity->isTranslatable() && !content_translation_view_access($entity, $context['langcode'])) { - $instances = field_info_instances($entity_type, $entity->bundle()); - // Avoid altering the real entity. - $entity = clone($entity); - $entity_langcode = $entity->getUntranslated()->language()->id; - - foreach ($entity->translation as $langcode => $translation) { - if ($langcode == $context['langcode'] || !content_translation_view_access($entity, $langcode)) { - // Unset unaccessible field translations: if the field is untranslatable - // unsetting a language different from Language::LANGCODE_NOT_SPECIFIED has no - // effect. - foreach ($instances as $instance) { - // @todo BC entities have the same value accessibile both with the - // entity language and with Language::LANGCODE_DEFAULT. We need need to unset - // both until we remove the BC layer. - if ($langcode == $entity_langcode) { - unset($entity->{$instance['field_name']}[Language::LANGCODE_DEFAULT]); - } - unset($entity->{$instance['field_name']}[$langcode]); - } - } +function content_translation_language_fallback_candidates_entity_view_alter(&$candidates, $context) { + $entity = $context['data']; + foreach ($entity->getTranslationLanguages() as $langcode => $language) { + if (!content_translation_view_access($entity, $langcode)) { + unset($candidates[$langcode]); } - - // Find the new fallback values. - field_language_fallback($display_language, $entity, $context['langcode']); } } diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index 02fd704..f971b64 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -465,6 +465,8 @@ function hook_field_attach_view_alter(&$output, $context) { } /** + * TODO remove + * * Perform alterations on field_language() values. * * This hook is invoked to alter the array of display language codes for the @@ -477,7 +479,7 @@ function hook_field_attach_view_alter(&$output, $context) { * - entity: The entity with fields to render. * - langcode: The language code $entity has to be displayed in. */ -function hook_field_language_alter(&$display_langcode, $context) { +function _REMOVE_hook_field_language_alter(&$display_langcode, $context) { // Do not apply core language fallback rules if they are disabled or if Locale // is not registered as a translation handler. if (field_language_fallback_enabled() && field_has_translation_handler($context['entity']->entityType())) { diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc index 5ed17ff..13a04f6 100644 --- a/core/modules/field/field.attach.inc +++ b/core/modules/field/field.attach.inc @@ -1036,7 +1036,7 @@ function field_attach_prepare_view($entity_type, array $entities, array $display // Determine the actual language code to display for each field, given the // language codes available in the field data. - $options['langcode'][$id] = field_language($entity, NULL, $langcode); + $options['langcode'][$id] = $entity->getCurrentTranslation($langcode)->language()->id; // Mark this item as prepared. $entity->_field_view_prepared = TRUE; @@ -1085,10 +1085,10 @@ function field_attach_prepare_view($entity_type, array $entities, array $display */ function field_attach_view(EntityInterface $entity, EntityDisplay $display, $langcode = NULL, array $options = array()) { // Ensure we are working with a BC mode entity. - $entity = $entity->getBCEntity(); + $entity = $entity->getCurrentTranslation($langcode)->getBCEntity(); // Determine the actual language code to display for each field, given the // language codes available in the field data. - $options['langcode'] = field_language($entity, NULL, $langcode); + $options['langcode'] = $entity->language()->id; // For each instance, call the view() method on the formatter object handed // by the entity display. diff --git a/core/modules/field/field.install b/core/modules/field/field.install index b425169..2fe0f60 100644 --- a/core/modules/field/field.install +++ b/core/modules/field/field.install @@ -371,11 +371,7 @@ function field_update_8003() { * @ingroup config_upgrade */ function field_update_8004() { - update_variable_set('field_language_fallback', TRUE); - update_variables_to_config('field.settings', array( - 'field_storage_default' => 'default_storage', - 'field_language_fallback' => 'language_fallback', - )); + // The 'field_language_fallback' variable has been removed. } /** diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 92a2c45..ef0e1bf 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -328,6 +328,8 @@ function field_field_widget_info_alter(&$info) { } /** + * // TODO remove + * * Applies language fallback rules to the fields attached to the given entity. * * Core language fallback rules simply check if fields have a field translation @@ -340,34 +342,19 @@ function field_field_widget_info_alter(&$info) { * * @param $field_langcodes * A reference to an array of language codes keyed by field name. - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to be displayed. + * @param array $values + * The values to be displayed. * @param $langcode * The language code $entity has to be displayed in. + * + * + * @see \Drupal\Core\Language\FallbackManagerInterface::getValuesMap() + * + * @deprecated This has been deprectaed in favor of the language fallback + * manager. */ -function field_language_fallback(&$field_langcodes, EntityInterface $entity, $langcode) { - // Lazily init fallback candidates to avoid unnecessary calls. - $fallback_candidates = NULL; - - foreach ($field_langcodes as $field_name => $field_langcode) { - // If the requested language is defined for the current field use it, - // otherwise search for a fallback value among the fallback candidates. - if (isset($entity->{$field_name}[$langcode])) { - $field_langcodes[$field_name] = $langcode; - } - elseif (!empty($entity->{$field_name})) { - if (!isset($fallback_candidates)) { - require_once DRUPAL_ROOT . '/core/includes/language.inc'; - $fallback_candidates = language_fallback_get_candidates(); - } - foreach ($fallback_candidates as $fallback_langcode) { - if (isset($entity->{$field_name}[$fallback_langcode])) { - $field_langcodes[$field_name] = $fallback_langcode; - break; - } - } - } - } +function _REMOVE_field_language_fallback(&$field_langcodes, array $values, $langcode) { + $field_langcodes = Drupal::service('language_fallback_manager')->getValuesMap($values, $langcode); } /** @@ -697,7 +684,7 @@ function field_view_value(EntityInterface $entity, $field_name, $item, $display if ($field = field_info_field($field_name)) { // Determine the langcode that will be used by language fallback. - $langcode = field_language($entity, $field_name, $langcode); + $langcode = $entity->getCurrentTranslation($langcode)->getFieldLangcode($field_name); // Push the item as the single value for the field, and defer to // field_view_field() to build the render array for the whole field. @@ -797,11 +784,12 @@ function field_view_field(EntityInterface $entity, $field_name, $display_options } if ($formatter) { - $display_langcode = field_language($entity, $field_name, $langcode); + $entity = $entity->getCurrentTranslation($langcode); + $display_langcode = $entity->getFieldLangcode($field_name); // Get the items. if ($entity->getNGEntity() instanceof EntityNG) { - $items = $entity->getTranslation($display_langcode)->get($field_name); + $items = $entity->get($field_name); $definition = $entity->getPropertyDefinition($field_name); } else { @@ -851,8 +839,8 @@ function field_view_field(EntityInterface $entity, $field_name, $display_options * An array with available field items keyed by delta. */ function field_get_items(EntityInterface $entity, $field_name, $langcode = NULL) { + $langcode = $entity->getCurrentTranslation($langcode)->getFieldLangcode($field_name); $entity = $entity->getBCEntity(); - $langcode = field_language($entity, $field_name, $langcode); return isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array(); } diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc index ca33a31..03c9eb0 100644 --- a/core/modules/field/field.multilingual.inc +++ b/core/modules/field/field.multilingual.inc @@ -51,6 +51,8 @@ * - Provide a value in a different language as fallback. By default, the * fallback logic is applied separately to each field to ensure that there * is a value for each field to display. + * + * // TODO remove * The field language fallback logic relies on the global language fallback * configuration. Therefore, the displayed field values can be in the * requested language, but may be different if no values for the requested @@ -153,9 +155,14 @@ function field_content_languages() { /** * Checks whether field language fallback is enabled. + * + * @see \Drupal\Core\Language\FallbackManagerInterface + * + * @deprecated Language fallback is always enabled. This concept has been + * deprecated in favor of fallback contexts and manager swappability. */ function field_language_fallback_enabled() { - return language_multilingual() && config('field.settings')->get('language_fallback'); + return TRUE; } /** @@ -224,6 +231,8 @@ function field_valid_language($langcode, $default = TRUE) { } /** + * TODO remove + * * Returns the display language code for the fields attached to the given * entity. * @@ -252,59 +261,19 @@ function field_valid_language($langcode, $default = TRUE) { * @return * A language code if a field name is specified, an array of language codes * keyed by field name otherwise. + * + * @see \Drupal\Core\Language\FallbackManagerInterface::getValuesMap() + * @see \Drupal\Core\Entity\EntityInterface::getFieldLangcode() + * + * @deprecated This has been deprecated in favor of the Entity Field API. */ function field_language(EntityInterface $entity, $field_name = NULL, $langcode = NULL) { - $display_langcodes = &drupal_static(__FUNCTION__, array()); - $id = $entity->id(); - $bundle = $entity->bundle(); - $entity_type = $entity->entityType(); - $langcode = field_valid_language($langcode, FALSE); - if (!isset($display_langcodes[$entity_type][$id][$langcode])) { + if (!isset($field_name)) { $display_langcode = array(); - - // By default, display language is set to one of the locked languages - // if the field translation is not available. It is up to translation - // handlers to implement language fallback rules. - foreach (field_info_instances($entity_type, $bundle) as $instance) { - if (isset($entity->{$instance['field_name']}[$langcode])) { - $display_langcode[$instance['field_name']] = $langcode; - } - else { - // If the field has a value for one of the locked languages, then use - // that language for display. If not, the default one will be - // Language::LANGCODE_NOT_SPECIFIED. - $display_langcode[$instance['field_name']] = Language::LANGCODE_NOT_SPECIFIED; - foreach (language_list(Language::STATE_LOCKED) as $language_locked) { - if (isset($entity->{$instance['field_name']}[$language_locked->id])) { - $display_langcode[$instance['field_name']] = $language_locked->id; - break; - } - } - } + foreach ($entity->getPropertyDefinitions() as $name => $definition) { + $display_langcodes[$name] = field_language($entity, $name, $langcode); } - - if (field_has_translation_handler($entity_type)) { - $context = array( - 'entity' => $entity, - 'langcode' => $langcode, - ); - // Do not apply core language fallback rules if they are disabled or if - // the entity does not have a translation handler registered. - if (field_language_fallback_enabled() && field_has_translation_handler($entity_type)) { - field_language_fallback($display_langcode, $context['entity'], $context['langcode']); - } - drupal_alter('field_language', $display_langcode, $context); - } - - $display_langcodes[$entity_type][$id][$langcode] = $display_langcode; + return $display_langcodes; } - - $display_langcode = $display_langcodes[$entity_type][$id][$langcode]; - - // Single-field mode. - if (isset($field_name)) { - return isset($display_langcode[$field_name]) ? $display_langcode[$field_name] : FALSE; - } - - return $display_langcode; + return $entity->getCurrentTranslation($langcode)->getFieldLangcode($field_name); } diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php index 3604142..7ac781b 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php +++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php @@ -8,6 +8,7 @@ namespace Drupal\field\Plugin\views\field; use Drupal\Core\Language\Language; +use Drupal\Core\Language\FallbackManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\field\Plugin\Type\Formatter\FormatterPluginManager; use Drupal\views\ViewExecutable; @@ -76,6 +77,13 @@ class Field extends FieldPluginBase { protected $formatterPluginManager; /** + * The language fallback manager. + * + * @var \Drupal\Core\Language\FallbackManagerInterface; + */ + protected $languageFallbackManager; + + /** * Constructs a \Drupal\field\Plugin\views\field\Field object. * * @param array $configuration @@ -87,10 +95,11 @@ class Field extends FieldPluginBase { * @param \Drupal\field\Plugin\Type\Formatter\FormatterPluginManager $formatter_plugin_manager * The field formatter plugin manager. */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, FormatterPluginManager $formatter_plugin_manager) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, FormatterPluginManager $formatter_plugin_manager, FallbackManagerInterface $fallback_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->formatterPluginManager = $formatter_plugin_manager; + $this->languageFallbackManager = $fallback_manager; } /** @@ -101,7 +110,8 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('plugin.manager.field.formatter') + $container->get('plugin.manager.field.formatter'), + $container->get('language_fallback_manager') ); } @@ -231,14 +241,7 @@ public function query($use_groupby = FALSE) { array(drupal_container()->get(Language::TYPE_CONTENT)->id, $default_langcode), $this->view->display_handler->options['field_langcode']); $placeholder = $this->placeholder(); - $langcode_fallback_candidates = array($langcode); - if (field_language_fallback_enabled()) { - require_once DRUPAL_ROOT . '/includes/language.inc'; - $langcode_fallback_candidates = array_merge($langcode_fallback_candidates, language_fallback_get_candidates()); - } - else { - $langcode_fallback_candidates[] = Language::LANGCODE_NOT_SPECIFIED; - } + $langcode_fallback_candidates = $this->languageFallbackManager->getCandidates(array('operation' => 'views_query', 'langcode' => $langcode, 'data' => $this)); $this->query->addWhereExpression(0, "$column IN($placeholder) OR $column IS NULL", array($placeholder => $langcode_fallback_candidates)); } } @@ -836,11 +839,13 @@ function field_langcode(EntityInterface $entity) { array(drupal_container()->get(Language::TYPE_CONTENT)->id, $default_langcode), $this->view->display_handler->options['field_language']); - // Give the Field Language API a chance to fallback to a different language - // (or Language::LANGCODE_NOT_SPECIFIED), in case the field has no data for the selected language. - // field_view_field() does this as well, but since the returned language code - // is used before calling it, the fallback needs to happen explicitly. - $langcode = field_language($entity, $this->field_info['field_name'], $langcode); + // Give the Entity Field API a chance to fallback to a different language + // (or Language::LANGCODE_NOT_SPECIFIED), in case the field has no data + // for the selected language. field_view_field() does this as well, but + // since the returned language code is used before calling it, the + // fallback needs to happen explicitly. + $name = $this->field_info['field_name']; + $langcode = $entity->getCurrentTranslation($langcode)->getFieldLangcode($name)->id; return $langcode; } diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php index ebf9722..aab344a 100644 --- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php @@ -235,9 +235,11 @@ function testTranslatableFieldSaveLoad() { } /** + * TODO remove + * * Tests display language logic for translatable fields. */ - function testFieldDisplayLanguage() { + function _REMOVE_testFieldDisplayLanguage() { $field_name = drupal_strtolower($this->randomName() . '_field_name'); $entity_type = 'entity_test'; diff --git a/core/modules/language/lib/Drupal/language/FallbackManager.php b/core/modules/language/lib/Drupal/language/FallbackManager.php new file mode 100644 index 0000000..342f2c2 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/FallbackManager.php @@ -0,0 +1,107 @@ +moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public function getCandidates(array $context = array()) { + // Get languages ordered by weight, add Language::LANGCODE_NOT_SPECIFIED at + // the end. + $candidates = array_keys(language_list()); + $candidates[] = Language::LANGCODE_NOT_SPECIFIED; + $candidates = MapArray::copyValuesToKeys($candidates); + + // The first candidate should always be the requested language if any. + if (!empty($context['langcode'])) { + $langcode = $context['langcode']; + $candidates = array($langcode => $langcode) + $candidates; + } + + // Let other modules hook in and add/change candidates. + $this->alter('language_fallback_candidates', $candidates, $context); + + return $candidates; + } + + /** + * {@inheritdoc} + */ + public function getValuesMap(array $values, $langcode, array $context = array()) { + $map = array(); + + // Lazily init fallback candidates to avoid unnecessary calls. + $candidates = NULL; + foreach ($values as $name => $value) { + // If the requested language is defined for the current value use it, + // otherwise search for a fallback value among the fallback candidates. + if (isset($value[$langcode])) { + $map[$name] = $langcode; + } + elseif (!empty($values[$name])) { + if (!isset($candidates)) { + $context['langcode'] = $langcode; + $candidates = $this->getCandidates($context); + // Remove the requested language as we already test it above. + unset($candidates[$langcode]); + } + foreach ($candidates as $fallback_langcode) { + if (isset($values[$name][$fallback_langcode])) { + $map[$name] = $fallback_langcode; + break; + } + } + } + } + + // TODO + $context = array('values' => $values, 'langcode' => $langcode) + $context; + $this->alter('language_fallback_values', $map, $context); + + return $map; + } + + /** + * TODO + */ + protected function alter($type, &$data, array $context) { + $types = array($type); + if (!empty($context['operation'])) { + $types[] = $type . '_' . $context['operation']; + } + return $this->moduleHandler->alter($types, $data, $context); + } + +} diff --git a/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php new file mode 100644 index 0000000..9e34c57 --- /dev/null +++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php @@ -0,0 +1,36 @@ +getDefinition('language_fallback_manager'); + $definition->setClass('Drupal\language\FallbackManager'); + $definition->addArgument(new Reference('module_handler')); + } + +} diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php index 43d54ce..80ee321 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php @@ -11,6 +11,8 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Database\Connection; +use Drupal\Core\Language\FallbackManagerInterface; +use Drupal\Core\Language\LanguageManager; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Cmf\Component\Routing\RouteProviderInterface; @@ -55,8 +57,8 @@ class MenuLinkStorageController extends DatabaseStorageController implements Men * @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider * The route provider service. */ - public function __construct($entity_type, array $entity_info, Connection $database, RouteProviderInterface $route_provider) { - parent::__construct($entity_type, $entity_info, $database); + public function __construct($entity_type, array $entity_info, Connection $database, LanguageManager $language_manager, FallbackManagerInterface $fallback_manager, RouteProviderInterface $route_provider) { + parent::__construct($entity_type, $entity_info, $database, $language_manager, $fallback_manager); $this->routeProvider = $route_provider; @@ -85,6 +87,8 @@ public static function createInstance(ContainerInterface $container, $entity_typ $entity_type, $entity_info, $container->get('database'), + $container->get('language_manager'), + $container->get('language_fallback_manager'), $container->get('router.route_provider') ); } diff --git a/core/modules/node/node.tokens.inc b/core/modules/node/node.tokens.inc index 241b83b..34cd748 100644 --- a/core/modules/node/node.tokens.inc +++ b/core/modules/node/node.tokens.inc @@ -139,7 +139,7 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr case 'summary': if ($items = field_get_items($node, 'body', $langcode)) { $instance = field_info_instance('node', 'body', $node->type); - $field_langcode = field_language($node, 'body', $langcode); + $field_langcode = $node->getCurrentTranslation($langcode)->language()->id; // If the summary was requested and is not empty, use it. if ($name == 'summary' && !empty($items[0]['summary'])) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php index 40a7e44..7a01f19 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -82,6 +82,7 @@ function setUp() { $language = new Language(array( 'id' => 'l' . $i, 'name' => $this->randomString(), + 'weight' => $i, )); $this->langcodes[$i] = $language->id; language_save($language); @@ -462,4 +463,68 @@ function testEntityTranslationAPI() { $this->assertEqual($translation->get($this->field_name)->value, $this->field_name . '_' . $langcode2, 'Language-aware default values correctly populated.'); } + /** + * Tests language fallback applied to field and entity translations. + */ + function testLanguageFallback() { + $default_langcode = $this->langcodes[0]; + $langcode = $this->langcodes[1]; + $langcode2 = $this->langcodes[2]; + + $values = array(); + for ($i = 0; $i < 3; $i++) { + $values[$this->langcodes[$i]]['name'] = $this->randomName(); + $values[$this->langcodes[$i]]['user_id'] = mt_rand(0, 127); + } + + $entity = $this->entityManager + ->getStorageController('entity_test_mul') + ->create(array('langcode' => $default_langcode) + $values[$default_langcode]); + $entity->save(); + + $entity->addTranslation($langcode, $values[$langcode]); + $entity->save(); + + // Check that retrieveing the current translation works as expected. + $entity = $this->reloadEntity($entity); + $translation = $entity->getCurrentTranslation($langcode2); + $this->assertEqual($translation->language()->id, $default_langcode, 'The current translation language matches the expected one.'); + + // TODO + $languages = language_list(); + $languages[$langcode]->weight = -1; + language_save($languages[$langcode]); + $translation = $entity->getCurrentTranslation($langcode2); + $this->assertEqual($translation->language()->id, $langcode, 'The current translation language matches the expected one.'); + + // TODO + $translation = $entity->getTranslation($langcode); + unset($translation->name); + $translation->save(); + $entity = $this->reloadEntity($entity); + $translation = $entity->getCurrentTranslation($langcode); + $this->assertEqual($translation->name->value, $values[$default_langcode]['name'], 'Field fallback is correctly performed when a field translation missing.'); + + // TODO + $translation2 = $entity->addTranslation($langcode2, $values[$langcode2]); + unset($translation2->name); + $translation2->save(); + $entity = $this->reloadEntity($entity); + $translation2 = $entity->getCurrentTranslation($langcode2); + $this->assertEqual($translation2->name->value, $values[$default_langcode]['name'], 'Field fallback is correctly performed when a field translation missing.'); + + // TODO + $translation = $entity->getTranslation($langcode); + $translation->name->value = $values[$langcode]['name']; + $this->assertEqual($translation2->name->value, $values[$default_langcode]['name'], 'Field fallback is obsolete without retrieving a fresh fallback translation object.'); + $translation2 = $translation2->getUntranslated()->getCurrentTranslation($langcode2); + $this->assertEqual($translation2->name->value, $values[$langcode]['name'], 'Field fallback is correctly performed when a field translation missing.'); + + // TODO + $translation2->save(); + $entity = $this->reloadEntity($entity); + $translation2 = $entity->getTranslation($langcode2); + $this->assertNull($translation2->name->value, 'Saving a fallback translation object does not cause non-existing values to be stored.'); + } + } diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php index 8b4653c..13e5d75 100644 --- a/core/modules/user/lib/Drupal/user/UserStorageController.php +++ b/core/modules/user/lib/Drupal/user/UserStorageController.php @@ -7,6 +7,8 @@ namespace Drupal\user; +use Drupal\Core\Language\FallbackManagerInterface; +use Drupal\Core\Language\LanguageManager; use Drupal\Core\Entity\EntityBCDecorator; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Password\PasswordInterface; @@ -51,8 +53,8 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS * @param \Drupal\user\UserDataInterface $user_data * The user data service. */ - public function __construct($entity_type, $entity_info, Connection $database, PasswordInterface $password, UserDataInterface $user_data) { - parent::__construct($entity_type, $entity_info, $database); + public function __construct($entity_type, $entity_info, Connection $database, LanguageManager $languageManager, FallbackManagerInterface $fallback_manager, PasswordInterface $password, UserDataInterface $user_data) { + parent::__construct($entity_type, $entity_info, $database, $languageManager, $fallback_manager); $this->password = $password; $this->userData = $user_data; @@ -66,6 +68,8 @@ public static function createInstance(ContainerInterface $container, $entity_typ $entity_type, $entity_info, $container->get('database'), + $container->get('language_manager'), + $container->get('language_fallback_manager'), $container->get('password'), $container->get('user.data') ); diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php index e3538e6..93b20e7 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php @@ -11,6 +11,8 @@ use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\views\ViewExecutable; use Drupal\Core\Database\Database; +use Drupal\Core\Language\FallbackManagerInterface; +use Drupal\Core\Language\LanguageManager; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Core\Session\AccountInterface; use Drupal\views\Plugin\views\query\Sql; @@ -905,6 +907,20 @@ public function setNewRevision($value = TRUE) { } /** + * {@inheritdoc} + */ + public function setLanguageManager(LanguageManager $languageManager) { + $this->storage->setLanguageManager($languageManager); + } + + /** + * {@inheritdoc} + */ + public function setLanguageFallbackManager(FallbackManagerInterface $fallback_manager) { + $this->storage->setLanguageFallbackManager($fallback_manager); + } + + /** * Implements \Drupal\Core\Entity\EntityInterface::enforceIsNew(). */ public function enforceIsNew($value = TRUE) { @@ -927,6 +943,14 @@ public function getTranslation($langcode) { } /** + * {@inheritdoc} + */ + public function getCurrentTranslation($langcode = NULL, $context = array()) { + // @todo Revisit this once config entities are converted to NG. + return $this; + } + + /** * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages(). */ public function getTranslationLanguages($include_default = TRUE) { @@ -1243,4 +1267,12 @@ public function mergeDefaultDisplaysOptions() { public function uriRelationships() { return $this->storage->uriRelationships(); } + + /** + * {@inheritdoc} + */ + public function getFieldLangcode($name) { + return $this->storage->getFieldLangcode($name); + } + }