diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index a7fdfcb..fb076b6 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -107,6 +107,8 @@ function hook_entity_view_mode_info_alter(&$view_modes) {
  *     - access callback: As in hook_menu(). 'user_access' will be assumed if
  *       no value is provided.
  *     - access arguments: As in hook_menu().
+ *   - translatable: (optional) A boolean value specifying whether this bundle
+ *     has translation support enabled. Defaults to FALSE.
  *
  * @see entity_get_bundles()
  * @see hook_entity_bundle_info_alter()
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index c0d2617..cd8a619 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -10,6 +10,8 @@
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Config\ConfigNameException;
 use Drupal\Core\Config\Context\ContextInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+use Symfony\Component\EventDispatcher\EventDispatcher;
 
 /**
  * Defines the default configuration object.
@@ -320,7 +322,12 @@ public function set($key, $value) {
       $this->load();
     }
     // Type-cast value into a string.
-    $value = $this->castValue($value);
+    if ($value instanceof TypedDataInterface) {
+      $value = $value->getString();
+    }
+    else {
+      $value = $this->castValue($value);
+    }
 
     // The dot/period is a reserved character; it may appear between keys, but
     // not within keys.
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityNGBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityNGBase.php
new file mode 100644
index 0000000..ea41a5a
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityNGBase.php
@@ -0,0 +1,137 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\Entity\ConfigEntityNGBase.
+ */
+
+namespace Drupal\Core\Config\Entity;
+
+use Drupal\Core\Entity\EntityNG;
+use Drupal\Core\TypedData\ContextAwareInterface;
+
+/**
+ * Defines a base configuration entity class.
+ */
+abstract class ConfigEntityNGBase extends EntityNG implements ConfigEntityInterface {
+
+  /**
+   * The original ID of the configuration entity.
+   *
+   * The ID of a configuration entity is a unique string (machine name). When a
+   * configuration entity is updated and its machine name is renamed, the
+   * original ID needs to be known.
+   *
+   * @var string
+   */
+  protected $originalID;
+
+  /**
+   * The enabled/disabled status of the configuration entity.
+   *
+   * @var bool
+   */
+  public $status = TRUE;
+
+  /**
+   * Implements ConfigEntityInterface::getOriginalID().
+   */
+  public function getOriginalID() {
+    return $this->originalID;
+  }
+
+  /**
+   * Implements ConfigEntityInterface::setOriginalID().
+   */
+  public function setOriginalID($id) {
+    $this->originalID = $id;
+  }
+
+  /**
+   * Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::enable().
+   */
+  public function enable() {
+    $this->status = TRUE;
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::disable().
+   */
+  public function disable() {
+    $this->status = FALSE;
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::setStatus().
+   */
+  public function setStatus($status) {
+    $this->status = (bool) $status;
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::status().
+   */
+  public function status() {
+    return !empty($this->status->value);
+  }
+
+  /**
+   * Overrides Entity::isNew().
+   *
+   * EntityInterface::enforceIsNew() is only supported for newly created
+   * configuration entities but has no effect after saving, since each
+   * configuration entity is unique.
+   */
+  final public function isNew() {
+    // Configuration entity IDs are strings, and '0' is a valid ID.
+    return !empty($this->enforceIsNew) || $this->id() === NULL || $this->id() === '';
+  }
+
+  /**
+   * Overrides Entity::createDuplicate().
+   */
+  public function createDuplicate() {
+    $duplicate = parent::createDuplicate();
+    // Prevent the new duplicate from being misinterpreted as a rename.
+    $duplicate->setOriginalID(NULL);
+    return $duplicate;
+  }
+
+  /**
+   * Helper callback for uasort() to sort configuration entities by weight and label.
+   */
+  public static function sort($a, $b) {
+    $a_weight = isset($a->weight) ? $a->weight : 0;
+    $b_weight = isset($b->weight) ? $b->weight : 0;
+    if ($a_weight == $b_weight) {
+      $a_label = $a->label();
+      $b_label = $b->label();
+      return strnatcasecmp($a_label, $b_label);
+    }
+    return ($a_weight < $b_weight) ? -1 : 1;
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\Entity::getExportProperties().
+   */
+  public function getExportProperties() {
+    // Configuration objects do not have a schema. Extract all key names from
+    // class properties.
+    $class_info = new \ReflectionClass($this);
+    $properties = array();
+    foreach ($class_info->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
+      $name = $property->getName();
+      if ($this->getPropertyDefinition($name)) {
+        $properties[$name] = $this->get($name);
+      }
+      else {
+        $properties[$name] = $this->{$name};
+      }
+    }
+    return $properties;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index 92e4be3..8ca433e 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -134,7 +134,7 @@ public function load(array $ids = NULL) {
       // Remove any invalid ids from the array.
       $passed_ids = array_intersect_key($passed_ids, $entities);
       foreach ($entities as $entity) {
-        $passed_ids[$entity->{$this->idKey}] = $entity;
+        $passed_ids[$entity->id()] = $entity;
       }
       $entities = $passed_ids;
     }
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageControllerNG.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageControllerNG.php
new file mode 100644
index 0000000..0f50e37
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageControllerNG.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\Entity\ConfigStorageControllerNG.
+ */
+
+namespace Drupal\Core\Config\Entity;
+
+use Drupal\Component\Uuid\Uuid;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityMalformedException;
+use Drupal\Core\Config\Config;
+
+/**
+ * Defines the storage controller class for configuration entities.
+ */
+class ConfigStorageControllerNG extends ConfigStorageController {
+
+  /**
+   * Overrides ConfigStorageController::buildQuery().
+   */
+  protected function buildQuery($ids, $revision_id = FALSE) {
+    $prefix = $this->getConfigPrefix();
+
+    // Load all of the configuration entities.
+    if ($ids === NULL) {
+      $names = drupal_container()->get('config.storage')->listAll($prefix);
+      $result = array();
+      foreach ($names as $name) {
+        $config = config($name);
+        $result[$config->get($this->idKey)] = $config->get();
+      }
+      return $result;
+    }
+    else {
+      $result = array();
+      foreach ($ids as $id) {
+        // Add the prefix to the ID to serve as the configuration object name.
+        $config = config($prefix . $id);
+        if (!$config->isNew()) {
+          $result[$id] = $config->get();
+        }
+      }
+      return $result;
+    }
+  }
+
+  /**
+   * Overrides ConfigStorageController::attachLoad().
+   */
+  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
+    // Map the loaded records into entity objects and according fields.
+    $class = $this->entityInfo['class'];
+    $entities = array();
+    foreach ($queried_entities as $id => $record) {
+      $values = array();
+      foreach ($record as $name => $value) {
+        // Skip the item delta and item value levels but let the field assign
+        // the value as suiting. This avoids unnecessary array hierarchies and
+        // saves memory here.
+        $values[$name][LANGUAGE_DEFAULT] = $value;
+      }
+      // Turn the record into an entity class.
+      $entities[$id] = new $class($values, $this->entityType);
+      $original_id = $entities[$id]->id();
+      if ($original_id !== NULL && $original_id !== '') {
+        $entities[$id]->setOriginalID($original_id);
+      }
+    }
+    $queried_entities = $entities;
+
+    parent::attachLoad($queried_entities, $revision_id);
+  }
+
+  /**
+   * Overrides ConfigStorageController::create().
+   */
+  public function create(array $values) {
+    $class = $this->entityInfo['class'];
+
+    $entity = new $class($values, $this->entityType);
+    // Mark this entity as new, so isNew() returns TRUE. This does not check
+    // whether a configuration entity with the same ID (if any) already exists.
+    $entity->enforceIsNew();
+
+
+    // Assign a new UUID if there is none yet.
+    if (!isset($entity->{$this->uuidKey}->value)) {
+      $uuid = new Uuid();
+      $entity->{$this->uuidKey} = $uuid->generate();
+    }
+    if (!isset($entity->status->value)) {
+      $entity->status = TRUE;
+    }
+
+    // Set all other given values.
+    foreach ($values as $name => $value) {
+      $entity->$name = $value;
+    }
+
+    $original_id = $entity->id();
+    if ($original_id !== NULL && $original_id !== '') {
+      $entity->setOriginalID($original_id);
+    }
+
+    // Modules might need to add or change the data initially held by the new
+    // entity object, for instance to fill-in default values.
+    $this->invokeHook('create', $entity);
+
+    // Default status to enabled.
+    if (!empty($this->statusKey) && !isset($entity->{$this->statusKey})) {
+      $entity->{$this->statusKey} = TRUE;
+    }
+
+    return $entity;
+  }
+
+  /**
+   * Overrides ConfigStorageController::postSave().
+   */
+  protected function postSave(EntityInterface $entity, $update) {
+    // Delete the original configuration entity, in case the entity ID was
+    // renamed.
+    if ($update && !empty($entity->original) && $entity->{$this->idKey}->value !== $entity->original->{$this->idKey}->value) {
+      // @todo This should just delete the original config object without going
+      //   through the API, no?
+      $entity->original->delete();
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityStorageControllerInterface::getFieldDefinitions().
+   */
+  public function getFieldDefinitions(array $constraints) {
+    // @todo: Add caching for $this->entityFieldInfo.
+    if (!isset($this->entityFieldInfo)) {
+      $this->entityFieldInfo = array(
+        'definitions' => $this->baseFieldDefinitions(),
+        // Contains definitions of optional (per-bundle) fields.
+        'optional' => array(),
+        // An array keyed by bundle name containing the optional fields added by
+        // the bundle.
+        'bundle map' => array(),
+      );
+
+      // Invoke hooks.
+      $result = module_invoke_all($this->entityType . '_property_info');
+      $this->entityFieldInfo = NestedArray::mergeDeep($this->entityFieldInfo, $result);
+      $result = module_invoke_all('entity_field_info', $this->entityType);
+      $this->entityFieldInfo = NestedArray::mergeDeep($this->entityFieldInfo, $result);
+
+      $hooks = array('entity_field_info', $this->entityType . '_property_info');
+      drupal_alter($hooks, $this->entityFieldInfo, $this->entityType);
+
+      // Enforce fields to be multiple by default.
+      foreach ($this->entityFieldInfo['definitions'] as &$definition) {
+        $definition['list'] = TRUE;
+      }
+      foreach ($this->entityFieldInfo['optional'] as &$definition) {
+        $definition['list'] = TRUE;
+      }
+    }
+
+    $bundle = !empty($constraints['Bundle']) ? $constraints['Bundle'] : FALSE;
+
+    // Add in per-bundle fields.
+    if (!isset($this->fieldDefinitions[$bundle])) {
+      $this->fieldDefinitions[$bundle] = $this->entityFieldInfo['definitions'];
+
+      if ($bundle && isset($this->entityFieldInfo['bundle map'][$bundle])) {
+        $this->fieldDefinitions[$bundle] += array_intersect_key($this->entityFieldInfo['optional'], array_flip($this->entityFieldInfo['bundle map'][$bundle]));
+      }
+    }
+    return $this->fieldDefinitions[$bundle];
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::baseFieldDefinitions().
+   */
+  public function baseFieldDefinitions() {
+    return array();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index fe00ef3..785c81c 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -440,4 +440,14 @@ public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
     // As entities are always the root of the tree of typed data, we do not need
     // to set any parent or name.
   }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityInterface::isTranslatable().
+   */
+  public function isTranslatable() {
+    // @todo Inject the entity manager and retrieve bundle info from it.
+    $bundles = entity_get_bundles($this->entityType);
+    return !empty($bundles[$this->bundle()]['translatable']);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
index a23f7e4..08be87c 100644
--- a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
+++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
@@ -460,4 +460,12 @@ public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
   public function getExportProperties() {
     $this->decorated->getExportProperties();
   }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function isTranslatable() {
+    return $this->decorated->isTranslatable();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 34038e9..b900490 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -219,4 +219,13 @@ public function getBCEntity();
    * @see \Drupal\Core\Entity\EntityInterface::getBCEntity()
    */
   public function getOriginalEntity();
+
+  /**
+   * Returns the translation support status.
+   *
+   * @return bool
+   *   TRUE if the entity bundle has translation support enabled.
+   */
+  public function isTranslatable();
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index a0d7c03..be46ac9 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -75,11 +75,8 @@
  * - static_cache: (optional) Boolean indicating whether entities should be
  *   statically cached during a page request. Used by
  *   Drupal\Core\Entity\DatabaseStorageController. Defaults to TRUE.
- * - translation: (optional) An associative array of modules registered as
- *   field translation handlers. Array keys are the module names, and array
- *   values can be any data structure the module uses to provide field
- *   translation. If the value is empty, the module will not be used as a
- *   translation handler.
+ * - translatable: (optional) Boolean indicating whether entities of this type
+ *   have mutlilingual support. Defaults to FALSE.
  * - entity_keys: An array describing how the Field API can extract certain
  *   information from objects of this entity type. Elements:
  *   - id: The name of the property that contains the primary ID of the
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index 70ac362..e954254 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -336,7 +336,7 @@ public function getTranslationLanguages($include_default = TRUE) {
         if (!$field->isEmpty()) {
           $translations[$langcode] = TRUE;
         }
-        if (isset($this->values[$name])) {
+        if (isset($this->values[$name]) && is_array($this->values[$name])) {
           foreach ($this->values[$name] as $langcode => $values) {
             // If a value is there but the field object is empty, it has been
             // unset, so we need to skip the field also.
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
index 62edc8d..9abdbb0 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
@@ -116,7 +116,7 @@ public function getString() {
     foreach ($this->getProperties() as $property) {
       $strings[] = $property->getString();
     }
-    return implode(', ', array_filter($strings));
+    return implode(', ', $strings);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
index 744e780..f066b72 100644
--- a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
@@ -52,4 +52,5 @@ public function getTranslationLanguages($include_default = TRUE);
    *   A typed data object for the translated data.
    */
   public function getTranslation($langcode, $strict = TRUE);
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
index d6ad4be..33d169e 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -34,6 +34,10 @@ public function __construct(array $definition) {
     $this->definition = $definition;
   }
 
+  public function __toString() {
+    return $this->getString();
+  }
+
   /**
    * Implements \Drupal\Core\TypedData\TypedDataInterface::getType().
    */
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
index c84a761..d0ea6ee 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
@@ -31,6 +31,7 @@
  *   revision_table = "custom_block_revision",
  *   menu_base_path = "block/%custom_block",
  *   fieldable = TRUE,
+ *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
  *     "revision" = "revision_id",
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
index d80a844..f6e9091 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
@@ -29,6 +29,7 @@
  *   base_table = "comment",
  *   uri_callback = "comment_uri",
  *   fieldable = TRUE,
+ *   translatable = TRUE,
  *   static_cache = FALSE,
  *   entity_keys = {
  *     "id" = "cid",
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
index f851cbc..250a3ca 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
@@ -76,7 +76,7 @@ function testList() {
     $actual_operations = $controller->getOperations($entity);
     // Sort the operations to normalize link order.
     uasort($actual_operations, 'drupal_sort_weight');
-    $this->assertIdentical($expected_operations, $actual_operations);
+    $this->assertIdentical($expected_operations, $actual_operations, 'The operations are built correctly.');
 
     // Test buildHeader() method.
     $expected_items = array(
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusTest.php
index 5519de5..a9db180 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusTest.php
@@ -19,7 +19,7 @@ class ConfigEntityStatusTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('config_test');
+  public static $modules = array('config_test', 'system');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusUITest.php
index 642fe2e..9f729ea 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusUITest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityStatusUITest.php
@@ -20,7 +20,7 @@ class ConfigEntityStatusUITest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('config_test');
+  public static $modules = array('config_test', 'system');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php
index e93b79f..bdfbbc1 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php
@@ -36,11 +36,11 @@ public static function getInfo() {
   function testCRUD() {
     // Verify default properties on a newly created empty entity.
     $empty = entity_create('config_test', array());
-    $this->assertIdentical($empty->id, NULL);
-    $this->assertTrue($empty->uuid);
-    $this->assertIdentical($empty->label, NULL);
-    $this->assertIdentical($empty->style, NULL);
-    $this->assertIdentical($empty->langcode, LANGUAGE_NOT_SPECIFIED);
+    $this->assertIdentical($empty->id->value, NULL);
+    $this->assertTrue($empty->uuid->value);
+    $this->assertIdentical($empty->label->value, NULL);
+    $this->assertIdentical($empty->style->value, NULL);
+    $this->assertIdentical($empty->langcode->value, LANGUAGE_NOT_SPECIFIED);
 
     // Verify ConfigEntity properties/methods on the newly created empty entity.
     $this->assertIdentical($empty->isNew(), TRUE);
@@ -50,11 +50,11 @@ function testCRUD() {
     $this->assertTrue($empty->uuid());
     $this->assertIdentical($empty->label(), NULL);
 
-    $this->assertIdentical($empty->get('id'), NULL);
-    $this->assertTrue($empty->get('uuid'));
-    $this->assertIdentical($empty->get('label'), NULL);
-    $this->assertIdentical($empty->get('style'), NULL);
-    $this->assertIdentical($empty->get('langcode'), LANGUAGE_NOT_SPECIFIED);
+    $this->assertIdentical($empty->get('id')->value, NULL);
+    $this->assertTrue($empty->get('uuid')->value);
+    $this->assertIdentical($empty->get('label')->value, NULL);
+    $this->assertIdentical($empty->get('style')->value, NULL);
+    $this->assertIdentical($empty->get('langcode')->value, LANGUAGE_NOT_SPECIFIED);
 
     // Verify Entity properties/methods on the newly created empty entity.
     $this->assertIdentical($empty->isNewRevision(), FALSE);
@@ -91,12 +91,12 @@ function testCRUD() {
       'label' => $this->randomString(),
       'style' => $this->randomName(),
     ));
-    $this->assertIdentical($config_test->id, $expected['id']);
-    $this->assertTrue($config_test->uuid);
-    $this->assertNotEqual($config_test->uuid, $empty->uuid);
-    $this->assertIdentical($config_test->label, $expected['label']);
-    $this->assertIdentical($config_test->style, $expected['style']);
-    $this->assertIdentical($config_test->langcode, LANGUAGE_NOT_SPECIFIED);
+    $this->assertIdentical($config_test->id->value, $expected['id']);
+    $this->assertTrue($config_test->uuid->value);
+    $this->assertNotEqual($config_test->uuid->value, $empty->uuid->value);
+    $this->assertIdentical($config_test->label->value, $expected['label']);
+    $this->assertIdentical($config_test->style->value, $expected['style']);
+    $this->assertIdentical($config_test->langcode->value, LANGUAGE_NOT_SPECIFIED);
 
     // Verify methods on the newly created entity.
     $this->assertIdentical($config_test->isNew(), TRUE);
@@ -179,7 +179,7 @@ function testCRUD() {
     // Test config entity prepopulation.
     state()->set('config_test.prepopulate', TRUE);
     $config_test = entity_create('config_test', array('foo' => 'bar'));
-    $this->assertEqual($config_test->get('foo'), 'baz', 'Initial value correctly populated');
+    $this->assertEqual($config_test->foo, 'baz', 'Initial value correctly populated');
   }
 
   /**
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
index 9278269..ece4c59 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
@@ -33,7 +33,6 @@ function setUp() {
     parent::setUp();
 
     $this->installSchema('system', 'config_snapshot');
-
     config_install_default_config('module', 'config_test');
     // Installing config_test's default configuration pollutes the global
     // variable being used for recording hook invocations by this test already,
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
index 4cb9ea3..cedd579 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
@@ -13,6 +13,14 @@
  * Tests installation of configuration objects in installation functionality.
  */
 class ConfigInstallTest extends DrupalUnitTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('system');
+
   public static function getInfo() {
     return array(
       'name' => 'Installation functionality unit tests',
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index d0d59fd..721119e 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -155,7 +155,7 @@ function config_test_cache_flush() {
  */
 function config_test_config_test_create(ConfigTest $config_test) {
   if (state()->get('config_test.prepopulate')) {
-    $config_test->set('foo', 'baz');
+    $config_test->foo = 'baz';
   }
 }
 
@@ -184,3 +184,13 @@ function config_test_entity_disable(ConfigTest $config_test) {
   $config_test->disable()->save();
   return new RedirectResponse(url('admin/structure/config_test', array('absolute' => TRUE)));
 }
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function config_test_entity_info_alter(&$entity_info) {
+  // The 'translatable' entity key is not supposed to change over time. In this
+  // case we can safely do it because we set it once and we do not change it for
+  // all the duration of the test session.
+  $entity_info['config_test']['translatable'] = Drupal::service('state')->get('config_test.translatable');
+}
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php
index 7e54e1c..51d1943 100644
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php
@@ -8,12 +8,12 @@
 namespace Drupal\config_test;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityFormController;
+use Drupal\Core\Entity\EntityFormControllerNG;
 
 /**
  * Form controller for the test config edit forms.
  */
-class ConfigTestFormController extends EntityFormController {
+class ConfigTestFormController extends EntityFormControllerNG {
 
   /**
    * Overrides Drupal\Core\Entity\EntityFormController::form().
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestStorageController.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestStorageController.php
index e1fe4fe..8b21022 100644
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestStorageController.php
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestStorageController.php
@@ -7,13 +7,13 @@
 
 namespace Drupal\config_test;
 
-use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Config\Entity\ConfigStorageControllerNG;
 use Drupal\Core\Config\Config;
 
 /**
  * @todo.
  */
-class ConfigTestStorageController extends ConfigStorageController {
+class ConfigTestStorageController extends ConfigStorageControllerNG {
 
   /**
    * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::importCreate().
@@ -45,4 +45,48 @@ public function importDelete($name, Config $new_config, Config $old_config) {
     return parent::importDelete($name, $new_config, $old_config);
   }
 
+  /**
+   * Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::baseFieldDefinitions().
+   */
+  public function baseFieldDefinitions() {
+    $fields = parent::baseFieldDefinitions();
+    $fields['id'] = array(
+      'label' => t('ID'),
+      'description' => t('The ID of the test entity.'),
+      'type' => 'string_field',
+    );
+    $fields['uuid'] = array(
+      'label' => t('UUID'),
+      'description' => t('The UUID of the test entity.'),
+      'type' => 'string_field',
+    );
+    $fields['langcode'] = array(
+      'label' => t('Language code'),
+      'description' => t('The language code of the test entity.'),
+      'type' => 'language_field',
+    );
+    $fields['label'] = array(
+      'label' => t('Name'),
+      'description' => t('The name of the test entity.'),
+      'type' => 'string_field',
+    );
+    $fields['style'] = array(
+      'label' => t('Style'),
+      'description' => t('The ID of the associated image style.'),
+      'type' => 'entity_reference_field',
+      'settings' => array('target_type' => 'image_style'),
+    );
+    $fields['protected_property'] = array(
+      'label' => t('Protected Property'),
+      'description' => t('A protected property of the test entity.'),
+      'type' => 'string_field',
+    );
+    $fields['status'] = array(
+      'label' => t('Status'),
+      'description' => t('The status of the test entity.'),
+      'type' => 'boolean_field',
+    );
+    return $fields;
+  }
+
 }
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
index 28b22cc..e10dd08 100644
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\config_test\Plugin\Core\Entity;
 
-use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Config\Entity\ConfigEntityNGBase;
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
 
@@ -33,7 +33,7 @@
  *   }
  * )
  */
-class ConfigTest extends ConfigEntityBase {
+class ConfigTest extends ConfigEntityNGBase {
 
   /**
    * The machine name for the configuration entity.
@@ -70,6 +70,17 @@ class ConfigTest extends ConfigEntityBase {
    */
   protected $protected_property;
 
+  protected function init() {
+    parent::init();
+    // We unset all defined properties, so magic getters apply.
+    unset($this->id);
+    unset($this->label);
+    unset($this->uuid);
+    unset($this->style);
+    unset($this->protected_property);
+    unset($this->status);
+  }
+
   /**
    * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::getExportProperties();
    */
diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc
index a9b33ac..8e5ee39 100644
--- a/core/modules/field/field.multilingual.inc
+++ b/core/modules/field/field.multilingual.inc
@@ -29,17 +29,9 @@
  * The available language codes for a particular field are returned by
  * field_available_languages(). Whether a field is translatable is determined by
  * calling field_is_translatable(), which checks the $field['translatable']
- * property returned by field_info_field(), and whether there is at least one
- * translation handler available for the field. A translation handler is a
- * module registering itself via hook_entity_info_alter() to handle field
- * translations.
+ * property returned by field_info_field() and whether the entity type the field
+ * is attached to supports translation.
  *
-
- * By default, _field_invoke() and _field_invoke_multiple() are processing a
- * field in all available languages, unless they are given a language code
- * suggestion. Based on that suggestion, _field_language_suggestion() determines
- * the languages to act on.
-
  * By default, _field_invoke() and _field_invoke_multiple() process a field in
  * all available languages, unless they are given a language code suggestion.
  * Based on that suggestion, _field_language_suggestion() determines the
@@ -226,23 +218,12 @@ function field_is_translatable($entity_type, $field) {
  *   TRUE, if the given handler is allowed to manage field translations. If no
  *   handler is passed, TRUE means there is at least one registered translation
  *   handler.
+ *
+ * @todo Remove this once the migration to the Entity Field API is complete.
  */
 function field_has_translation_handler($entity_type, $handler = NULL) {
-  $entity_info = entity_get_info($entity_type);
-
-  if (isset($handler)) {
-    return !empty($entity_info['translation'][$handler]);
-  }
-  elseif (isset($entity_info['translation'])) {
-    foreach ($entity_info['translation'] as $handler_info) {
-      // The translation handler must use a non-empty data structure.
-      if (!empty($handler_info)) {
-        return TRUE;
-      }
-    }
-  }
-
-  return FALSE;
+  $info = entity_get_info($entity_type);
+  return !empty($info['translatable']);
 }
 
 /**
diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc
index 81c116d..565ed08 100644
--- a/core/modules/field/tests/modules/field_test/field_test.entity.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc
@@ -12,13 +12,8 @@
  * Implements hook_entity_info_alter().
  */
 function field_test_entity_info_alter(&$entity_info) {
-  // Enable/disable field_test as a translation handler.
   foreach (field_test_entity_info_translatable() as $entity_type => $translatable) {
-    $entity_info[$entity_type]['translation']['field_test'] = $translatable;
-  }
-  // Disable the entity type translation handler.
-  foreach ($entity_info as $entity_type => $info) {
-    $entity_info[$entity_type]['translation'][$entity_type] = FALSE;
+    $entity_info[$entity_type]['translatable'] = $translatable;
   }
 }
 
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 81b9715..1c20695 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -210,11 +210,7 @@ function language_theme() {
 function language_entity_supported() {
   $supported = array();
   foreach (entity_get_info() as $entity_type => $info) {
-    // @todo Revisit this once all core entities are migrated to the Entity
-    //   Field API and language support for configuration entities has been
-    //   sorted out.
-    $entity_class = new ReflectionClass($info['class']);
-    if ($info['fieldable'] && !$entity_class->implementsInterface('Drupal\Core\Config\Entity\ConfigEntityInterface')) {
+    if (!empty($info['fieldable']) && !empty($info['translatable'])) {
       $supported[$entity_type] = $entity_type;
     }
   }
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
index 32c61ca..e367425 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
@@ -30,6 +30,7 @@
  *   revision_table = "node_revision",
  *   uri_callback = "node_uri",
  *   fieldable = TRUE,
+ *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "nid",
  *     "revision" = "vid",
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/ConfigEntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/ConfigEntityQueryTest.php
index bfafc9e..eae8928 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/ConfigEntityQueryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/ConfigEntityQueryTest.php
@@ -21,7 +21,7 @@ class ConfigEntityQueryTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  static $modules = array('config_test');
+  static $modules = array('config_test', 'system');
 
   /**
    * Stores the search results for alter comparision.
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
index 31844b6..4de550b 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
@@ -27,6 +27,7 @@
  *   base_table = "entity_test_mul",
  *   data_table = "entity_test_mul_property_data",
  *   fieldable = TRUE,
+ *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
index a12fc58..632ada7 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
@@ -28,6 +28,7 @@
  *   data_table = "entity_test_mulrev_property_data",
  *   revision_table = "entity_test_mulrev_property_revision",
  *   fieldable = TRUE,
+ *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
index bbb4638..affe712 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
@@ -30,6 +30,7 @@
  *   base_table = "taxonomy_term_data",
  *   uri_callback = "taxonomy_term_uri",
  *   fieldable = TRUE,
+ *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "tid",
  *     "bundle" = "vid",
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php
index a42a019..aee3c67 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php
@@ -50,9 +50,7 @@
  * Additionally some more entity info keys can be defined to further customize
  * the translation UI. The entity translation info is an associative array that
  * has to match the following structure. Two nested arrays keyed respectively
- * by the 'translation' key and the 'entity_translation' key: the first one is
- * the key defined by the core entity system, while the second one registers
- * Entity Tanslation as a field translation handler. Elements:
+ * by the 'translation' key and the 'translation_entity' key. Elements:
  * - access callback: The access callback for the translation pages. Defaults to
  *   'entity_translation_translate_access'.
  * - access arguments: The access arguments for the translation pages. By
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php
index 2439c8b..9c51c46 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php
@@ -35,6 +35,14 @@ function setUp() {
   }
 
   /**
+   * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::enableTranslation().
+   */
+  protected function enableTranslation() {
+    $this->container->get('state')->set('config_test.translatable', TRUE);
+    parent::enableTranslation();
+  }
+
+  /**
    * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getNewEntityValues().
    */
   protected function getNewEntityValues($langcode) {
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index bb78d1e..68eb96d 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -72,11 +72,12 @@ function translation_entity_language_types_info_alter(array &$language_types) {
  * Implements hook_entity_info_alter().
  */
 function translation_entity_entity_info_alter(array &$entity_info) {
-  $edit_form_info = array();
-
-  $bundles_info = entity_get_bundles();
   // Provide defaults for translation info.
   foreach ($entity_info as $entity_type => &$info) {
+    if (empty($info['translatable'])) {
+      continue;
+    }
+
     if (!isset($info['translation']['translation_entity'])) {
       $info['translation']['translation_entity'] = array();
     }
@@ -87,39 +88,37 @@ function translation_entity_entity_info_alter(array &$entity_info) {
     // shared accross different entities.
     $info += array('translation_controller_class' => 'Drupal\translation_entity\EntityTranslationController');
 
-    // Check whether translation is enabled at least for one bundle. We cannot
-    // use translation_entity_enabled() here since it would cause infinite
-    // recursion, as it relies on entity info.
-    $enabled = FALSE;
-    $bundles = isset($bundles_info[$entity_type]) ? array_keys($bundles_info[$entity_type]) : array($entity_type);
-    foreach ($bundles as $bundle) {
-      if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
-        $enabled = TRUE;
-        break;
-      }
+    // If no menu base path is provided we default to the usual
+    // "entity_type/%entity_type" pattern.
+    if (!isset($info['menu_base_path'])) {
+      $path = "$entity_type/%$entity_type";
+      $info['menu_base_path'] = $path;
     }
 
-    if ($enabled) {
-      // If no menu base path is provided we default to the usual
-      // "entity_type/%entity_type" pattern.
-      if (!isset($info['menu_base_path'])) {
-        $path = "$entity_type/%$entity_type";
-        $info['menu_base_path'] = $path;
-      }
+    $path = $info['menu_base_path'];
 
-      $path = $info['menu_base_path'];
+    $info += array(
+      'menu_view_path' => $path,
+      'menu_edit_path' => "$path/edit",
+      'menu_path_wildcard' => "%$entity_type",
+    );
 
-      $info += array(
-        'menu_view_path' => $path,
-        'menu_edit_path' => "$path/edit",
-        'menu_path_wildcard' => "%$entity_type",
-      );
+    $entity_position = count(explode('/', $path)) - 1;
+    $info['translation']['translation_entity'] += array(
+      'access_callback' => 'translation_entity_translate_access',
+      'access_arguments' => array($entity_position),
+    );
+  }
+}
 
-      $entity_position = count(explode('/', $path)) - 1;
-      $info['translation']['translation_entity'] += array(
-        'access_callback' => 'translation_entity_translate_access',
-        'access_arguments' => array($entity_position),
-      );
+/**
+ * Implements hook_entity_bundle_info_alter().
+ */
+function translation_entity_entity_bundle_info_alter(&$bundles) {
+  foreach ($bundles as $entity_type => &$info) {
+    foreach ($info as $bundle => &$bundle_info) {
+      $enabled = translation_entity_get_config($entity_type, $bundle, 'enabled');
+      $bundle_info['translatable'] = !empty($enabled);
     }
   }
 }
@@ -279,9 +278,7 @@ function _translation_entity_menu_strip_loaders($path) {
  */
 function translation_entity_translate_access(EntityInterface $entity) {
   $entity_type = $entity->entityType();
-  return empty($entity->language()->locked) &&
-    language_multilingual() &&
-    translation_entity_enabled($entity_type, $entity->bundle()) &&
+  return empty($entity->language()->locked) && language_multilingual() && $entity->isTranslatable() &&
     (user_access('create entity translations') || user_access('update entity translations') || user_access('delete entity translations'));
 }
 
@@ -445,26 +442,26 @@ function translation_entity_set_config($entity_type, $bundle, $setting, $value)
  * @param string $bundle
  *   (optional) The bundle of the entity. If no bundle is provided, all the
  *   available bundles are checked.
- * @param boolean $skip_handler
- *   (optional) Specifies whether the availablity of a field translation handler
- *   should affect the returned value. By default the check is performed.
  *
  * @returns
  *   TRUE if the specified bundle is translatable. If no bundle is provided
  *   returns TRUE if at least one of the entity bundles is translatable.
  */
-function translation_entity_enabled($entity_type, $bundle = NULL, $skip_handler = FALSE) {
+function translation_entity_enabled($entity_type, $bundle = NULL) {
   $enabled = FALSE;
-  $bundles = !empty($bundle) ? array($bundle) : array_keys(entity_get_bundles($entity_type));
+  $info = entity_get_info($entity_type);
 
-  foreach ($bundles as $bundle) {
-    if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
-      $enabled = TRUE;
-      break;
+  if (!empty($info['translatable'])) {
+    $bundles = !empty($bundle) ? array($bundle) : array_keys(entity_get_bundles($entity_type));
+    foreach ($bundles as $bundle) {
+      if (translation_entity_get_config($entity_type, $bundle, 'enabled')) {
+        $enabled = TRUE;
+        break;
+      }
     }
   }
 
-  return $enabled && ($skip_handler || field_has_translation_handler($entity_type, 'translation_entity'));
+  return $enabled;
 }
 
 /**
@@ -602,7 +599,7 @@ function translation_entity_permission() {
  * Implements hook_form_alter().
  */
 function translation_entity_form_alter(array &$form, array &$form_state) {
-  if (($form_controller = translation_entity_form_controller($form_state)) && ($entity = $form_controller->getEntity($form_state)) && !$entity->isNew() && translation_entity_enabled($entity->entityType(), $entity->bundle())) {
+  if (($form_controller = translation_entity_form_controller($form_state)) && ($entity = $form_controller->getEntity($form_state)) && !$entity->isNew() && $entity->isTranslatable()) {
     $controller = translation_entity_controller($entity->entityType());
     $controller->entityFormAlter($form, $form_state, $entity);
 
@@ -643,7 +640,7 @@ function translation_entity_field_language_alter(&$display_language, $context) {
   $entity = $context['entity'];
   $entity_type = $entity->entityType();
 
-  if (isset($entity->translation[$context['langcode']]) && translation_entity_enabled($entity_type, $entity->bundle()) && !translation_entity_view_access($entity, $context['langcode'])) {
+  if (isset($entity->translation[$context['langcode']]) && $entity->isTranslatable() && !translation_entity_view_access($entity, $context['langcode'])) {
     $instances = field_info_instances($entity_type, $entity->bundle());
     // Avoid altering the real entity.
     $entity = clone($entity);
@@ -679,7 +676,7 @@ function translation_entity_entity_load(array $entities, $entity_type) {
 
   if (translation_entity_enabled($entity_type)) {
     foreach ($entities as $entity) {
-      if (translation_entity_enabled($entity_type, $entity->bundle())) {
+      if ($entity->isTranslatable()) {
         $enabled_entities[$entity->id()] = $entity;
       }
     }
@@ -718,7 +715,7 @@ function translation_entity_load_translation_metadata(array $entities, $entity_t
  */
 function translation_entity_entity_insert(EntityInterface $entity) {
   // Only do something if translation support for the given entity is enabled.
-  if (!translation_entity_enabled($entity->entityType(), $entity->bundle())) {
+  if (!$entity->isTranslatable()) {
     return;
   }
 
@@ -758,7 +755,7 @@ function translation_entity_entity_insert(EntityInterface $entity) {
  */
 function translation_entity_entity_delete(EntityInterface $entity) {
   // Only do something if translation support for the given entity is enabled.
-  if (!translation_entity_enabled($entity->entityType(), $entity->bundle())) {
+  if (!$entity->isTranslatable()) {
     return;
   }
 
@@ -773,7 +770,7 @@ function translation_entity_entity_delete(EntityInterface $entity) {
  */
 function translation_entity_entity_update(EntityInterface $entity) {
   // Only do something if translation support for the given entity is enabled.
-  if (!translation_entity_enabled($entity->entityType(), $entity->bundle())) {
+  if (!$entity->isTranslatable()) {
     return;
   }
 
@@ -867,7 +864,7 @@ function translation_entity_field_info_alter(&$info) {
  * Implements hook_field_attach_presave().
  */
 function translation_entity_field_attach_presave(EntityInterface $entity) {
-  if (translation_entity_enabled($entity->entityType(), $entity->bundle())) {
+  if ($entity->isTranslatable()) {
     $attributes = drupal_container()->get('request')->attributes;
     Drupal::service('translation_entity.synchronizer')->synchronizeFields($entity, $attributes->get('working_langcode'), $attributes->get('source_langcode'));
   }
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
index 64afbfc..fb7cbbb 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
@@ -31,6 +31,7 @@
  *   uri_callback = "user_uri",
  *   label_callback = "user_label",
  *   fieldable = TRUE,
+ *   translatable = TRUE,
  *   entity_keys = {
  *     "id" = "uid",
  *     "uuid" = "uuid"
diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
index 8c44bb0..eae95fc 100644
--- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -985,6 +985,13 @@ public function getOriginalEntity() {
   }
 
   /**
+   * Implements \Drupal\Core\Entity\EntityInterface::isTranslatable().
+   */
+  public function isTranslatable() {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
+
+  /**
    * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName().
    */
   public function getName() {
