diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index 986e344..53bb273 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -241,6 +241,37 @@ function hook_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
 }
 
 /**
+ * Acts after storing a new entity translation.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $translation
+ *   The entity object of the translation just stored.
+ */
+function hook_entity_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
+  $variables = array(
+    '@language' => $translation->language()->name,
+    '@label' => $translation->getOriginal()->label(),
+  );
+  watchdog('example', 'The @language translation of @label has just been stored.', $variables);
+}
+
+/**
+ * Acts after deleting an entity translation from the storage.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The original entity object.
+ * @param string $langcode
+ *   The language code identifying the translation just removed.
+ */
+function hook_entity_translation_delete(\Drupal\Core\Entity\EntityInterface $entity, $langcode) {
+  $languages = language_list();
+  $variables = array(
+    '@language' => $languages[$langcode]->name,
+    '@label' => $entity->label(),
+  );
+  watchdog('example', 'The @language translation of @label has just been deleted.', $variables);
+}
+
+/**
  * Act before entity deletion.
  *
  * This hook runs after the entity type-specific predelete hook.
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
index c3a76e2..982f6ec 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -14,7 +14,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\EntityStorageException;
-use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\TranslatableInterface;
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Database\Connection;
 
@@ -306,6 +306,7 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) {
 
       $data = $query->execute();
       $field_definition = $this->getFieldDefinitions(array());
+      $translations = array();
       if ($this->revisionTable) {
         $data_fields = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_table']), drupal_schema_fields_sql($this->entityInfo['base_table'])));
       }
@@ -319,6 +320,7 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) {
         // Field values in default language are stored with
         // Language::LANGCODE_DEFAULT as key.
         $langcode = empty($values['default_langcode']) ? $values['langcode'] : Language::LANGCODE_DEFAULT;
+        $translations[$id][$langcode] = TRUE;
 
         foreach ($field_definition as $name => $definition) {
           // Set only translatable properties, unless we are dealing with a
@@ -334,7 +336,7 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) {
       foreach ($entities as $id => $values) {
         $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);
+        $entities[$id] = new $this->entityClass($values, $this->entityType, $bundle, array_keys($translations[$id]));
       }
     }
   }
@@ -382,6 +384,9 @@ public function save(EntityInterface $entity) {
         $this->resetCache(array($entity->id()));
         $entity->postSave($this, TRUE);
         $this->invokeHook('update', $entity);
+        if ($this->dataTable) {
+          $this->notifyTranslationChanges($entity);
+        }
       }
       else {
         $return = drupal_write_record($this->entityInfo['base_table'], $record);
@@ -426,7 +431,7 @@ public function save(EntityInterface $entity) {
    */
   protected function saveRevision(EntityInterface $entity) {
     $return = $entity->id();
-    $default_langcode = $entity->language()->langcode;
+    $default_langcode = $entity->getOriginal()->language()->langcode;
 
     if (!$entity->isNewRevision()) {
       // Delete to handle removed values.
@@ -436,9 +441,9 @@ protected function saveRevision(EntityInterface $entity) {
         ->execute();
     }
 
-    $languages = $this->dataTable ? $entity->getTranslationLanguages(TRUE) : array($default_langcode => $entity->language());
+    $languages = $this->dataTable ? $entity->getTranslationLanguages() : array($default_langcode => $entity->language());
     foreach ($languages as $langcode => $language) {
-      $translation = $entity->getTranslation($langcode, FALSE);
+      $translation = $entity->getTranslation($langcode);
       $record = $this->mapToRevisionStorageRecord($translation);
       $record->langcode = $langcode;
       $record->default_langcode = $langcode == $default_langcode;
@@ -500,6 +505,28 @@ protected function savePropertyData(EntityInterface $entity) {
   }
 
   /**
+   * Checks translation statuses and invoke the related hooks if needed.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   */
+  function notifyTranslationChanges(EntityInterface $entity) {
+    // Notify modules of translation creation/removal.
+    foreach ($entity->getTranslationLanguages(FALSE, TRUE) as $langcode => $language) {
+      $translation = $entity->getTranslation($langcode);
+
+      switch ($translation->getTranslationStatus()) {
+        case TranslatableInterface::TRANSLATION_CREATED:
+          $this->invokeHook('translation_insert', $translation);
+          break;
+
+        case TranslatableInterface::TRANSLATION_REMOVED:
+          $this->invokeHook('translation_delete', $entity, $langcode);
+          break;
+      }
+    }
+  }
+
+  /**
    * Overrides DatabaseStorageController::invokeHook().
    *
    * Invokes field API attachers with a BC entity.
@@ -547,7 +574,7 @@ protected function mapToStorageRecord(EntityInterface $entity) {
    * @return \stdClass
    *   The record to store.
    */
-  protected function mapToRevisionStorageRecord(ComplexDataInterface $entity) {
+  protected function mapToRevisionStorageRecord(EntityInterface $entity) {
     $record = new \stdClass();
     $definitions = $entity->getPropertyDefinitions();
     foreach (drupal_schema_fields_sql($this->entityInfo['revision_table']) as $name) {
@@ -570,10 +597,10 @@ protected function mapToRevisionStorageRecord(ComplexDataInterface $entity) {
    *   The record to store.
    */
   protected function mapToDataStorageRecord(EntityInterface $entity, $langcode) {
-    $default_langcode = $entity->language()->langcode;
+    $default_langcode = $entity->getOriginal()->language()->langcode;
     // Don't use strict mode, this way there's no need to do checks here, as
     // non-translatable properties are replicated for each language.
-    $translation = $entity->getTranslation($langcode, FALSE);
+    $translation = $entity->getTranslation($langcode);
     $definitions = $translation->getPropertyDefinitions();
     $schema = drupal_get_schema($this->entityInfo['data_table']);
 
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index f8f8eb1..1ad4741 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\TranslatableInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\user\UserInterface;
 use IteratorAggregate;
@@ -294,10 +295,13 @@ public function language() {
 
   /**
    * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslation().
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
    */
   public function getTranslation($langcode, $strict = TRUE) {
     // @todo: Replace by EntityNG implementation once all entity types have been
     // converted to use the entity field API.
+    return $this;
   }
 
   /**
@@ -314,7 +318,7 @@ public function translations() {
   /**
    * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages().
    */
-  public function getTranslationLanguages($include_default = TRUE) {
+  public function getTranslationLanguages($include_default = TRUE, $include_removed = FALSE) {
     // @todo: Replace by EntityNG implementation once all entity types have been
     // converted to use the entity field API.
     $default_language = $this->language();
@@ -579,4 +583,41 @@ public static function postLoad(EntityStorageControllerInterface $storage_contro
   public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
   }
 
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::getOriginal().
+   */
+  public function getOriginal() {
+    return $this->getTranslation(Language::LANGCODE_DEFAULT);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::hasTranslation().
+   */
+  public function hasTranslation($langcode) {
+    $translations = $this->getTranslationLanguages();
+    return isset($translations[$langcode]);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::addTranslation().
+   */
+  public function addTranslation($langcode, array $values = array()) {}
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::removeTranslation().
+   */
+  public function removeTranslation($langcode) {}
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationStatus().
+   */
+  public function getTranslationStatus() {
+    return TranslatableInterface::TRANSLATION_EXISTING;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::initTranslation().
+   */
+  public function initTranslation($langcode) {}
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
index 93bafb4..a538c05 100644
--- a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
+++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
@@ -418,8 +418,8 @@ public function language() {
   /**
    * Forwards the call to the decorated entity.
    */
-  public function getTranslationLanguages($include_default = TRUE) {
-    return $this->decorated->getTranslationLanguages($include_default);
+  public function getTranslationLanguages($include_default = TRUE, $include_removed = FALSE) {
+    return $this->decorated->getTranslationLanguages($include_default, $include_removed);
   }
 
   /**
@@ -527,12 +527,6 @@ public function onChange($property_name) {
     $this->decorated->onChange($property_name);
   }
 
-  /**
-   * Forwards the call to the decorated entity.
-   */
-  public function isTranslatable() {
-    return $this->decorated->isTranslatable();
-  }
 
   /**
    * {@inheritdoc}
@@ -582,4 +576,54 @@ public static function postDelete(EntityStorageControllerInterface $storage_cont
    */
   public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
   }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function isTranslatable() {
+    return $this->decorated->isTranslatable();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getOriginal() {
+    return $this->decorated->getOriginal();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function hasTranslation($langcode) {
+    return $this->decorated->hasTranslation($langcode);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function addTranslation($langcode, array $values = array()) {
+    return $this->decorated->addTranslation($langcode, $values);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function removeTranslation($langcode) {
+    $this->decorated->removeTranslation($langcode);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getTranslationStatus() {
+   return $this->decorated->getTranslationStatus();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function initTranslation($langcode) {
+    $this->decorated->initTranslation($langcode);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php
index 7c5f7ed..9e0f046 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormController.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormController.php
@@ -120,6 +120,13 @@ protected function init(array &$form_state) {
     // Add the controller to the form state so it can be easily accessed by
     // module-provided form handlers there.
     $form_state['controller'] = $this;
+
+    // Ensure we act on the translation object corresponding to the current form
+    // language.
+    $translation = $this->entity->getTranslation($this->getFormLangcode($form_state));
+    $this->entity = $this->entity instanceof EntityBCDecorator ? $translation->getBCEntity() : $translation;
+
+    // Prepare the entity to be presented in the entity form.
     $this->prepareEntity();
 
     // @todo Allow the usage of different form modes by exposing a hook and the
@@ -164,7 +171,7 @@ public function form(array $form, array &$form_state) {
       // new entities.
       $form['langcode'] = array(
         '#type' => 'value',
-        '#value' => !$entity->isNew() ? $entity->langcode : language_default()->langcode,
+        '#value' => !$entity->isNew() ? $entity->getOriginal()->language()->langcode : language_default()->langcode,
       );
     }
     return $form;
@@ -309,7 +316,6 @@ public function delete(array $form, array &$form_state) {
    */
   public function getFormLangcode(array $form_state) {
     $entity = $this->entity;
-    $translations = $entity->getTranslationLanguages();
 
     if (!empty($form_state['langcode'])) {
       $langcode = $form_state['langcode'];
@@ -318,6 +324,7 @@ public function getFormLangcode(array $form_state) {
       // 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)->langcode;
       $fallback = language_multilingual() ? language_fallback_get_candidates() : array();
       while (!empty($langcode) && !isset($translations[$langcode])) {
@@ -327,14 +334,14 @@ public function getFormLangcode(array $form_state) {
 
     // If the site is not multilingual or no translation for the given form
     // language is available, fall back to the entity language.
-    return !empty($langcode) ? $langcode : $entity->language()->langcode;
+    return !empty($langcode) ? $langcode : $entity->getOriginal()->language()->langcode;
   }
 
   /**
    * Implements \Drupal\Core\Entity\EntityFormControllerInterface::isDefaultFormLangcode().
    */
   public function isDefaultFormLangcode(array $form_state) {
-    return $this->getFormLangcode($form_state) == $this->entity->language()->langcode;
+    return $this->getFormLangcode($form_state) == $this->entity->getOriginal()->language()->langcode;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php b/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php
index 0c9dabd..192beff 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php
@@ -84,11 +84,10 @@ public function buildEntity(array $form, array &$form_state) {
     // edited by this form. Values of fields handled by field API are copied
     // by field_attach_extract_form_values() below.
     $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $entity->bundle())) : $form_state['values'];
-    $translation = $entity->getTranslation($this->getFormLangcode($form_state), FALSE);
-    $definitions = $translation->getPropertyDefinitions();
+    $definitions = $entity->getPropertyDefinitions();
     foreach ($values_excluding_fields as $key => $value) {
       if (isset($definitions[$key])) {
-        $translation->$key = $value;
+        $entity->$key = $value;
       }
     }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index de0fb46..b8d8370 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -12,6 +12,7 @@
 use Drupal\Component\Uuid\Uuid;
 use ArrayIterator;
 use InvalidArgumentException;
+use Drupal\Core\TypedData\TranslatableInterface;
 
 /**
  * Implements Entity Field API specific enhancements to the Entity class.
@@ -80,12 +81,43 @@ class EntityNG extends Entity {
    */
   protected $uriPlaceholderReplacements;
 
+
+
+  /**
+   * Language code identifying the entity active language.
+   *
+   * This is the language field accessors will use to determine which field
+   * values manipulate.
+   *
+   * @var string
+   */
+  protected $activeLangcode = Language::LANGCODE_DEFAULT;
+
+  /**
+   * An array of entity translation metadata.
+   *
+   * An associative array keyed by translation language code. Every value is an
+   * array containg the translation status and the translation object, if it has
+   * already been instantiated.
+   *
+   * @var array
+   */
+  protected $translations;
+
+  /**
+   * A flag indicating whether a translation object is being initialized.
+   *
+   * @var boolean
+   */
+  protected $translationInit = FALSE;
+
   /**
    * Overrides Entity::__construct().
    */
-  public function __construct(array $values, $entity_type, $bundle = FALSE) {
+  public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = array()) {
     $this->entityType = $entity_type;
     $this->bundle = $bundle ? $bundle : $this->entityType;
+
     foreach ($values as $key => $value) {
       // If the key matches an existing property set the value to the property
       // to ensure non converted properties have the correct value.
@@ -94,6 +126,20 @@ public function __construct(array $values, $entity_type, $bundle = FALSE) {
       }
       $this->values[$key] = $value;
     }
+
+    // Initialize translations. Ensure we have at least an entry for the entity
+    // original language.
+    $data = array('status' => TranslatableInterface::TRANSLATION_EXISTING);
+    $this->translations = array(Language::LANGCODE_DEFAULT => $data);
+    if ($translations) {
+      $default_langcode = $this->language()->langcode;
+      foreach ($translations as $langcode) {
+        if ($langcode != $default_langcode && $langcode != Language::LANGCODE_DEFAULT) {
+          $this->translations[$langcode] = $data;
+        }
+      }
+    }
+
     $this->init();
   }
 
@@ -115,10 +161,20 @@ protected function init() {
   }
 
   /**
+   * Clear entity translation object cache to ensure we do not have stale references.
+   */
+  protected function clearTranslationCache() {
+    foreach ($this->translations as &$translation) {
+      unset($translation['entity']);
+    }
+  }
+
+  /**
    * Magic __wakeup() implementation.
    */
   public function __wakeup() {
     $this->init();
+    $this->clearTranslationCache();
   }
 
   /**
@@ -201,12 +257,11 @@ protected function uriPlaceholderReplacements() {
    * Implements \Drupal\Core\TypedData\ComplexDataInterface::get().
    */
   public function get($property_name) {
-    // Values in default language are always stored using the
-    // Language::LANGCODE_DEFAULT constant.
-    if (!isset($this->fields[$property_name][Language::LANGCODE_DEFAULT])) {
-      return $this->getTranslatedField($property_name, Language::LANGCODE_DEFAULT);
+    $this->checkTranslationStatus();
+    if (!isset($this->fields[$property_name][$this->activeLangcode])) {
+      return $this->getTranslatedField($property_name, $this->activeLangcode);
     }
-    return $this->fields[$property_name][Language::LANGCODE_DEFAULT];
+    return $this->fields[$property_name][$this->activeLangcode];
   }
 
   /**
@@ -332,9 +387,26 @@ public function isEmpty() {
    * Implements \Drupal\Core\TypedData\TranslatableInterface::language().
    */
   public function language() {
+    if ($this->activeLangcode != Language::LANGCODE_DEFAULT) {
+      $languages = language_list(Language::STATE_ALL);
+      if (isset($languages[$this->activeLangcode])) {
+        return $languages[$this->activeLangcode];
+      }
+    }
+    return $this->getOriginalLanguage();
+  }
+
+  /**
+   * Returns the entity original language.
+   *
+   * @return \Drupal\Core\Language\Language
+   *   The entity language object.
+   */
+  protected function getOriginalLanguage() {
+    $language = NULL;
     // Get the language code if the property exists.
-    if ($this->getPropertyDefinition('langcode')) {
-      $language = $this->get('langcode')->language;
+    if ($this->getPropertyDefinition('langcode') && ($item = $this->get('langcode')) && isset($item->language)) {
+      $language = $item->language;
     }
     if (empty($language)) {
       // Make sure we return a proper language object.
@@ -346,75 +418,166 @@ public function language() {
   /**
    * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslation().
    *
-   * @return \Drupal\Core\Entity\Field\Type\EntityTranslation
+   * @return \Drupal\Core\Entity\EntityInterface
    */
   public function getTranslation($langcode, $strict = TRUE) {
-    // If the default language is Language::LANGCODE_NOT_SPECIFIED, the entity is not
-    // translatable, so we use Language::LANGCODE_DEFAULT.
-    if ($langcode == Language::LANGCODE_DEFAULT || in_array($this->language()->langcode, array(Language::LANGCODE_NOT_SPECIFIED, $langcode))) {
-      // No translation needed, return the entity.
-      return $this;
-    }
-    // Check whether the language code is valid, thus is of an available
-    // language.
+    // Ensure we always use the default language code when dealing with the
+    // original entity language.
+    if ($langcode != Language::LANGCODE_DEFAULT && $langcode == $this->getOriginalLanguage()->langcode) {
+      $langcode = Language::LANGCODE_DEFAULT;
+    }
+
+    // Populate entity translation object cache so it will be available for all
+    // translation objects.
+    if ($langcode == $this->activeLangcode) {
+      $this->translations[$langcode]['entity'] = $this;
+    }
+
+    // If we already have a translation object for the specified language we can
+    // just return it.
+    if (isset($this->translations[$langcode]['entity'])) {
+      return $this->translations[$langcode]['entity'];
+    }
+
+    // If the requested translation is valid, we instantiate a new translation
+    // object being a clone of the current one but with the specified language
+    // as active language. Before cloning we specify we are initializing a
+    // translation object to perform a shallow clone, in fact all the field data
+    // structures need to be shared among the translation objects to ensure all
+    // of them deal with fresh data.
+    if (isset($this->translations[$langcode])) {
+      $this->translationInit = TRUE;
+      $translation = clone $this;
+      unset($translation->bcEntity);
+      $translation->activeLangcode = $langcode;
+      // Ensure that changes to fields, values and translations are propagated
+      // to all the translation objects.
+      // @todo Consider converting these to ArrayObject.
+      $translation->values = &$this->values;
+      $translation->fields = &$this->fields;
+      $translation->translations = &$this->translations;
+      $translation->translationInit = FALSE;
+      $this->translations[$langcode]['entity'] = $translation;
+      $this->translationInit = FALSE;
+      return $translation;
+    }
+
+    // If we were given a valid language and there is no translation for it, we
+    // return a new one.
+    $languages = language_list();
+    if (isset($languages[$langcode])) {
+      return $this->addTranslation($langcode);
+    }
+
+    // TODO Do we want to return $this instead?
+    throw new InvalidArgumentException("Invalid '$langcode' specified.");
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasTranslation($langcode) {
+    return !empty($this->translations[$langcode]['status']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addTranslation($langcode, array $values = array()) {
     $languages = language_list(Language::STATE_ALL);
-    if (!isset($languages[$langcode])) {
-      throw new InvalidArgumentException("Unable to get translation for the invalid language '$langcode'.");
+    if (!isset($languages[$langcode]) || $this->hasTranslation($langcode)) {
+      throw new InvalidArgumentException("Invalid translation language '$langcode'");
+    }
+
+    // Instantiate a new empty entity so default values will be populated in the
+    // specified language.
+    $info = $this->entityInfo();
+    // @todo Use the actual translation language once the BC decorator is gone.
+    $default_values = array($info['entity_keys']['bundle'] => $this->bundle, 'langcode' => Language::LANGCODE_NOT_SPECIFIED);
+    $entity = entity_create($this->entityType(), $default_values);
+    foreach ($entity as $name => $field) {
+      if (!isset($values[$name]) && !$field->isEmpty()) {
+        $values[$name] = $field->value;
+      }
     }
-    $fields = array();
-    foreach ($this->getPropertyDefinitions() as $name => $definition) {
-      // Load only translatable properties in strict mode.
-      if (!empty($definition['translatable']) || !$strict) {
-        $fields[$name] = $this->getTranslatedField($name, $langcode);
+
+    $this->translations[$langcode]['status'] = self::TRANSLATION_CREATED;
+    $translation = $this->getTranslation($langcode);
+    $definitions = $translation->getPropertyDefinitions();
+
+    foreach ($values as $name => $value) {
+      if (isset($definitions[$name]) && !empty($definitions[$name]['translatable'])) {
+        $translation->$name = $value;
       }
     }
-    // @todo: Add a way to get the definition of a translation to the
-    // TranslatableInterface and leverage TypeDataManager::getPropertyInstance
-    // also.
-    $translation_definition = array(
-      'type' => 'entity_translation',
-      'constraints' => array(
-        'entity type' => $this->entityType(),
-        'bundle' => $this->bundle(),
-      ),
-    );
-    $translation = \Drupal::typedData()->create($translation_definition, $fields);
-    $translation->setStrictMode($strict);
-    $translation->setContext('@' . $langcode, $this);
+
     return $translation;
   }
 
   /**
-   * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages().
+   * {@inheritdoc}
    */
-  public function getTranslationLanguages($include_default = TRUE) {
-    $translations = array();
-    $definitions = $this->getPropertyDefinitions();
-    // Build an array with the translation langcodes set as keys. Empty
-    // translations should not be included and must be skipped.
-    foreach ($this->getProperties() as $name => $property) {
-      foreach ($this->fields[$name] as $langcode => $field) {
-        if (!$field->isEmpty()) {
-          $translations[$langcode] = TRUE;
-        }
-        if (isset($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.
-            if ($values && !empty($definitions[$name]['translatable']) && !(isset($this->fields[$name][$langcode]) && $this->fields[$name][$langcode]->isEmpty())) {
-              $translations[$langcode] = TRUE;
-            }
-          }
-        }
+  public function removeTranslation($langcode) {
+    $translation = $this->getTranslation($langcode);
+    foreach ($translation->getPropertyDefinitions() as $name => $definition) {
+      if (!empty($definition['translatable'])) {
+        $translation->$name = array();
       }
     }
-    // We include the default language code instead of the
-    // Language::LANGCODE_DEFAULT constant.
+    $this->translations[$langcode]['status'] = self::TRANSLATION_REMOVED;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTranslationStatus() {
+    return $this->translations[$this->activeLangcode]['status'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initTranslation($langcode) {
+    $this->translations[$langcode]['status'] = self::TRANSLATION_EXISTING;
+  }
+
+  /**
+   * Checks wether the current translation object has a valid status.
+   *
+   * To avoid manipulating stale data, we invalidate a translation object as
+   * soon as the related translation has been removed. Any attempt to access
+   * invalid data causes an exception to be thrown.
+   *
+   * @return boolean
+   *   TRUE if the translation object has a valid status.
+   */
+  protected function checkTranslationStatus() {
+    if ($this->translations[$this->activeLangcode]['status'] == self::TRANSLATION_REMOVED) {
+      // TODO Use a more specific exception.
+      throw new \Exception('The entity object refers to a removed translation and cannot be manipulated.');
+    }
+    return TRUE;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages().
+   */
+  public function getTranslationLanguages($include_default = TRUE, $include_removed = FALSE) {
+    if (!$include_removed) {
+      $translations = array_filter($this->translations, function($translation) { return $translation['status']; });
+    }
+    else {
+      $translations = $this->translations;
+    }
+
+    $translations = array_flip(array_keys($translations));
     unset($translations[Language::LANGCODE_DEFAULT]);
 
     if ($include_default) {
-      $translations[$this->language()->langcode] = TRUE;
+      $langcode = $this->getOriginalLanguage()->langcode;
+      $translations[$langcode] = TRUE;
     }
+
     // Now load language objects based upon translation langcodes.
     return array_intersect_key(language_list(Language::STATE_ALL), $translations);
   }
@@ -464,17 +627,18 @@ public function updateOriginalValues() {
    * For compatibility mode to work this must return a reference.
    */
   public function &__get($name) {
+    $this->checkTranslationStatus();
     // If this is an entity field, handle it accordingly. We first check whether
     // a field object has been already created. If not, we create one.
-    if (isset($this->fields[$name][Language::LANGCODE_DEFAULT])) {
-      return $this->fields[$name][Language::LANGCODE_DEFAULT];
+    if (isset($this->fields[$name][$this->activeLangcode])) {
+      return $this->fields[$name][$this->activeLangcode];
     }
     // Inline getPropertyDefinition() to speed up things.
     if (!isset($this->fieldDefinitions)) {
       $this->getPropertyDefinitions();
     }
     if (isset($this->fieldDefinitions[$name])) {
-      $return = $this->getTranslatedField($name, Language::LANGCODE_DEFAULT);
+      $return = $this->getTranslatedField($name, $this->activeLangcode);
       return $return;
     }
     // Allow the EntityBCDecorator to directly access the values and fields.
@@ -496,17 +660,18 @@ public function &__get($name) {
    * Uses default language always.
    */
   public function __set($name, $value) {
+    $this->checkTranslationStatus();
     // Support setting values via property objects.
     if ($value instanceof TypedDataInterface && !$value instanceof EntityInterface) {
       $value = $value->getValue();
     }
     // If this is an entity field, handle it accordingly. We first check whether
     // a field object has been already created. If not, we create one.
-    if (isset($this->fields[$name][Language::LANGCODE_DEFAULT])) {
-      $this->fields[$name][Language::LANGCODE_DEFAULT]->setValue($value);
+    if (isset($this->fields[$name][$this->activeLangcode])) {
+      $this->fields[$name][$this->activeLangcode]->setValue($value);
     }
     elseif ($this->getPropertyDefinition($name)) {
-      $this->getTranslatedField($name, Language::LANGCODE_DEFAULT)->setValue($value);
+      $this->getTranslatedField($name, $this->activeLangcode)->setValue($value);
     }
     // Else directly read/write plain values. That way, fields not yet converted
     // to the entity field API can always be directly accessed.
@@ -543,6 +708,7 @@ public function __unset($name) {
    * Overrides Entity::createDuplicate().
    */
   public function createDuplicate() {
+    $this->checkTranslationStatus();
     $duplicate = clone $this;
     $entity_info = $this->entityInfo();
     $duplicate->{$entity_info['entity_keys']['id']}->value = NULL;
@@ -565,13 +731,19 @@ public function createDuplicate() {
    * Magic method: Implements a deep clone.
    */
   public function __clone() {
-    $this->bcEntity = NULL;
-
-    foreach ($this->fields as $name => $properties) {
-      foreach ($properties as $langcode => $property) {
-        $this->fields[$name][$langcode] = clone $property;
-        $this->fields[$name][$langcode]->setContext($name, $this);
+    // Avoid deep-cloning when we are initializing a translation object, since
+    // it will represent the same entity, only with a different active language.
+    if (!$this->translationInit) {
+      $this->bcEntity = NULL;
+
+      foreach ($this->fields as $name => $properties) {
+        foreach ($properties as $langcode => $property) {
+          $this->fields[$name][$langcode] = clone $property;
+          $this->fields[$name][$langcode]->setContext($name, $this);
+        }
       }
+
+      $this->clearTranslationCache();
     }
   }
 
@@ -581,6 +753,9 @@ public function __clone() {
   public function label($langcode = NULL) {
     $label = NULL;
     $entity_info = $this->entityInfo();
+    if (!isset($langcode)) {
+      $langcode = $this->activeLangcode;
+    }
     if (isset($entity_info['label_callback']) && function_exists($entity_info['label_callback'])) {
       $label = $entity_info['label_callback']($this->entityType, $this, $langcode);
     }
@@ -597,4 +772,5 @@ public function validate() {
     // @todo: Add the typed data manager as proper dependency.
     return \Drupal::typedData()->getValidator()->validate($this);
   }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
deleted file mode 100644
index 859fa9a..0000000
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
+++ /dev/null
@@ -1,224 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Type\EntityTranslation.
- */
-
-namespace Drupal\Core\Entity\Field\Type;
-
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\TypedData\AccessibleInterface;
-use Drupal\Core\TypedData\ComplexDataInterface;
-use Drupal\Core\TypedData\TypedData;
-use ArrayIterator;
-use Drupal\Core\TypedData\TypedDataInterface;
-use IteratorAggregate;
-use InvalidArgumentException;
-
-/**
- * Allows accessing and updating translated entity fields.
- *
- * Via this object translated entity fields may be read and updated in the same
- * way as untranslatable entity fields on the entity object.
- */
-class EntityTranslation extends TypedData implements IteratorAggregate, AccessibleInterface, ComplexDataInterface {
-
-  /**
-   * The array of translated fields, each being an instance of
-   * \Drupal\Core\Entity\FieldInterface.
-   *
-   * @var array
-   */
-  protected $fields = array();
-
-  /**
-   * Whether the entity translation acts in strict mode.
-   *
-   * @var boolean
-   */
-  protected $strict = TRUE;
-
-  /**
-   * Returns whether the entity translation acts in strict mode.
-   *
-   * @return boolean
-   *   Whether the entity translation acts in strict mode.
-   */
-  public function getStrictMode() {
-    return $this->strict;
-  }
-
-  /**
-   * Sets whether the entity translation acts in strict mode.
-   *
-   * @param boolean $strict
-   *   Whether the entity translation acts in strict mode.
-   *
-   * @see \Drupal\Core\TypedData\TranslatableInterface::getTranslation()
-   */
-  public function setStrictMode($strict = TRUE) {
-    $this->strict = $strict;
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getValue().
-   */
-  public function getValue() {
-    // The plain value of the translation is the array of translated field
-    // objects.
-    return $this->fields;
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::setValue().
-   */
-  public function setValue($values, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-    $this->fields = $values;
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getString().
-   */
-  public function getString() {
-    $strings = array();
-    foreach ($this->getProperties() as $property) {
-      $strings[] = $property->getString();
-    }
-    return implode(', ', array_filter($strings));
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::get().
-   */
-  public function get($property_name) {
-    $definitions = $this->getPropertyDefinitions();
-    if (!isset($definitions[$property_name])) {
-      throw new InvalidArgumentException(format_string('Field @name is unknown or not translatable.', array('@name' => $property_name)));
-    }
-    return $this->fields[$property_name];
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::set().
-   */
-  public function set($property_name, $value, $notify = TRUE) {
-    $this->get($property_name)->setValue($value, FALSE);
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties().
-   */
-  public function getProperties($include_computed = FALSE) {
-    $properties = array();
-    foreach ($this->getPropertyDefinitions() as $name => $definition) {
-      if ($include_computed || empty($definition['computed'])) {
-        $properties[$name] = $this->get($name);
-      }
-    }
-    return $properties;
-  }
-
-  /**
-   * Magic method: Gets a translated field.
-   */
-  public function __get($name) {
-    return $this->get($name);
-  }
-
-  /**
-   * Magic method: Sets a translated field.
-   */
-  public function __set($name, $value) {
-    $this->get($name)->setValue($value);
-  }
-
-  /**
-   * Implements \IteratorAggregate::getIterator().
-   */
-  public function getIterator() {
-    return new ArrayIterator($this->getProperties());
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
-   */
-  public function getPropertyDefinition($name) {
-    $definitions = $this->getPropertyDefinitions();
-    if (isset($definitions[$name])) {
-      return $definitions[$name];
-    }
-    else {
-      return FALSE;
-    }
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-    $definitions = array();
-    foreach ($this->parent->getPropertyDefinitions() as $name => $definition) {
-      if (!empty($definition['translatable']) || !$this->strict) {
-        $definitions[$name] = $definition;
-      }
-    }
-    return $definitions;
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues().
-   */
-  public function getPropertyValues() {
-    return $this->getValue();
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues().
-   */
-  public function setPropertyValues($values) {
-    foreach ($values as $name => $value) {
-      $this->get($name)->setValue($value);
-    }
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
-   */
-  public function isEmpty() {
-    foreach ($this->getProperties() as $property) {
-      if ($property->getValue() !== NULL) {
-        return FALSE;
-      }
-    }
-    return TRUE;
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange().
-   */
-  public function onChange($property_name) {
-    // Notify the parent of changes.
-    if (isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\AccessibleInterface::access().
-   */
-  public function access($operation = 'view', AccountInterface $account = NULL) {
-    // Determine the language code of this translation by cutting of the
-    // leading "@" from the property name to get the langcode.
-    // @todo Add a way to set and get the langcode so that's more obvious what
-    // we're doing here.
-    $langcode = substr($this->getName(), 1);
-    return \Drupal::entityManager()
-      ->getAccessController($this->parent->entityType())
-      ->access($this->parent, $operation, $langcode, $account);
-  }
-}
diff --git a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
index f86068a..63a264b 100644
--- a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php
@@ -13,9 +13,24 @@
 interface TranslatableInterface {
 
   /**
+   * Status code indentifying a removed translation.
+   */
+  const TRANSLATION_REMOVED = 0;
+
+  /**
+   * Status code indentifying an existing translation.
+   */
+  const TRANSLATION_EXISTING = 1;
+
+  /**
+   * Status code indentifying a newly created translation.
+   */
+  const TRANSLATION_CREATED = 2;
+
+  /**
    * Returns the default language.
    *
-   * @return
+   * @return \Drupal\Core\Language\Language
    *   The language object.
    */
   public function language();
@@ -24,12 +39,16 @@ public function language();
    * Returns the languages the data is translated to.
    *
    * @param bool $include_default
-   *   Whether the default language should be included.
+   *   (optional) Whether the default language should be included. Defaults to
+   *   TRUE.
+   * @param bool $include_removed
+   *   Whether languages referring to removed translations should be included.
+   *   Defaults to FALSE.
    *
    * @return
    *   An array of language objects, keyed by language codes.
    */
-  public function getTranslationLanguages($include_default = TRUE);
+  public function getTranslationLanguages($include_default = TRUE, $include_removed = FALSE);
 
   /**
    * Gets a translation of the data.
@@ -53,4 +72,59 @@ public function getTranslationLanguages($include_default = TRUE);
    */
   public function getTranslation($langcode, $strict = TRUE);
 
+
+  /**
+   * Returns the entity object referring to the original language.
+   *
+   * @return \Drupal\Core\TypedData\TranslatableInterface
+   */
+  public function getOriginal();
+
+  /**
+   * Returns TRUE if the entity has a translation for the given language code.
+   *
+   * @param string $langcode
+   *   The language code identifiying the translation.
+   *
+   * @return bool
+  */
+  public function hasTranslation($langcode);
+
+  /**
+   * Adds a new translation to the entity object.
+   *
+   * @param string $langcode
+   *   The language code identifying the translation.
+   * @param array $values
+   *   (optional) An array of initial values to be assigned to the translatable
+   *   field. Defaults to none.
+   *
+   * @return \Drupal\Core\TypedData\TranslatableInterface
+   */
+  public function addTranslation($langcode, array $values = array());
+
+  /**
+   * Removes the translation identified by the given language code.
+   *
+   * @param string $langcode
+   */
+  public function removeTranslation($langcode);
+
+  /**
+   * Returns the current status of the entity translation object.
+   *
+   * @return integer
+   *   A translation status code as defined in TranslatableInterface.
+   */
+  public function getTranslationStatus();
+
+  /**
+   * Marks the translation identified by the given language code as existing.
+   *
+   * @todo Remove this as soon as all translatable entities have a data table.
+   *
+   * @param string $langcode
+   */
+  public function initTranslation($langcode);
+
 }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index e2040d5..c4632ea 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -158,9 +158,10 @@ public function form(array $form, array &$form_state) {
     }
 
     // Add internal comment properties.
+    $original = $comment->getOriginal();
     foreach (array('cid', 'pid', 'nid', 'uid', 'node_type', 'langcode') as $key) {
       $key_name = key($comment->$key->offsetGet(0)->getPropertyDefinitions());
-      $form[$key] = array('#type' => 'value', '#value' => $comment->$key->{$key_name});
+      $form[$key] = array('#type' => 'value', '#value' => $original->$key->{$key_name});
     }
 
     return parent::form($form, $form_state, $comment);
diff --git a/core/modules/node/lib/Drupal/node/NodeTranslationController.php b/core/modules/node/lib/Drupal/node/NodeTranslationController.php
index 4531a8e..6ed7182 100644
--- a/core/modules/node/lib/Drupal/node/NodeTranslationController.php
+++ b/core/modules/node/lib/Drupal/node/NodeTranslationController.php
@@ -8,12 +8,12 @@
 namespace Drupal\node;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\translation_entity\EntityTranslationController;
+use Drupal\translation_entity\EntityTranslationControllerNG;
 
 /**
  * Defines the translation controller class for nodes.
  */
-class NodeTranslationController extends EntityTranslationController {
+class NodeTranslationController extends EntityTranslationControllerNG {
 
   /**
    * Overrides EntityTranslationController::getAccess().
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 6a1f766..66f6116 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2243,11 +2243,6 @@ function system_data_type_info() {
       'description' => t('All kind of entities, e.g. nodes, comments or users.'),
       'class' => '\Drupal\Core\Entity\Field\Type\EntityWrapper',
     ),
-    'entity_translation' => array(
-      'label' => t('Entity translation'),
-      'description' => t('A translation of an entity'),
-      'class' => '\Drupal\Core\Entity\Field\Type\EntityTranslation',
-    ),
     'boolean_field' => array(
       'label' => t('Boolean field item'),
       'description' => t('An entity field containing a boolean value.'),
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php
index 0ad1da0..ea72819 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestFormController.php
@@ -47,7 +47,7 @@ public function form(array $form, array &$form_state) {
     $form['langcode'] = array(
       '#title' => t('Language'),
       '#type' => 'language_select',
-      '#default_value' => $entity->language()->langcode,
+      '#default_value' => $entity->getOriginal()->language()->langcode,
       '#languages' => Language::STATE_ALL,
     );
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php
index 832a0cf..8e98c4f 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermFormController.php
@@ -47,7 +47,7 @@ public function form(array $form, array &$form_state) {
       '#type' => 'language_select',
       '#title' => t('Language'),
       '#languages' => Language::STATE_ALL,
-      '#default_value' => $term->langcode->value,
+      '#default_value' => $term->getOriginal()->language()->langcode,
       '#access' => !is_null($language_configuration['language_show']) && $language_configuration['language_show'],
     );
 
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
index 906248a..973902e 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
@@ -126,7 +126,7 @@ public function getSourceLangcode(array $form_state) {
   public function entityFormAlter(array &$form, array &$form_state, EntityInterface $entity) {
     $form_controller = translation_entity_form_controller($form_state);
     $form_langcode = $form_controller->getFormLangcode($form_state);
-    $entity_langcode = $entity->language()->langcode;
+    $entity_langcode = $entity->getOriginal()->language()->langcode;
     $source_langcode = $this->getSourceLangcode($form_state);
 
     $new_translation = !empty($source_langcode);
@@ -144,7 +144,7 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
     if (isset($languages[$form_langcode]) && ($has_translations || $new_translation)) {
       $title = $this->entityFormTitle($entity);
       // When editing the original values display just the entity label.
-      if ($form_langcode != $entity->language()->langcode) {
+      if ($form_langcode != $entity_langcode) {
         $t_args = array('%language' => $languages[$form_langcode]->name, '%title' => $entity->label());
         $title = empty($source_langcode) ? $title . ' [' . t('%language translation', $t_args) . ']' : t('Create %language translation of %title', $t_args);
       }
@@ -433,10 +433,9 @@ public function entityFormEntityBuild($entity_type, EntityInterface $entity, arr
     }
 
     // Set contextual information that can be reused during the storage phase.
-    // @todo Remove this once we have an EntityLanguageDecorator to deal with
-    //   the active language.
-    $attributes = drupal_container()->get('request')->attributes;
-    $attributes->set('working_langcode', $form_langcode);
+    // @todo Remove this once translation metadata is converted to regular
+    //   fields.
+    $attributes = \Drupal::request()->attributes;
     $attributes->set('source_langcode', $source_langcode);
   }
 
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php
index fd007cd..2243466 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php
@@ -25,10 +25,7 @@ public function getAccess(EntityInterface $entity, $op) {
    * Overrides \Drupal\translation_entity\EntityTranslationControllerInterface::removeTranslation().
    */
   public function removeTranslation(EntityInterface $entity, $langcode) {
-    $translation = $entity->getTranslation($langcode);
-    foreach ($translation->getPropertyDefinitions() as $property_name => $langcode) {
-      $translation->$property_name = array();
-    }
+    $entity->removeTranslation($langcode);
   }
 
 }
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.php b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.php
index 39d1a21..757cdff 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.php
@@ -55,7 +55,7 @@ public function synchronizeFields(EntityInterface $entity, $sync_langcode, $orig
     // If the entity language is being changed there is nothing to synchronize.
     $entity_type = $entity->entityType();
     $entity_unchanged = isset($entity->original) ? $entity->original : $this->entityManager->getStorageController($entity_type)->loadUnchanged($entity->id());
-    if ($entity->language()->langcode != $entity_unchanged->language()->langcode) {
+    if ($entity->getOriginal()->language()->langcode != $entity_unchanged->getOriginal()->language()->langcode) {
       return;
     }
 
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
index 50e8bbd..e18c454 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
@@ -10,7 +10,6 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityNG;
 use Drupal\Core\Language\Language;
-use Drupal\Core\TypedData\ComplexDataInterface;
 
 /**
  * Tests the Entity Translation UI.
@@ -267,7 +266,7 @@ protected function getTranslation(EntityInterface $entity, $langcode) {
   /**
    * Returns the value for the specified property in the given language.
    *
-   * @param \Drupal\Core\TypedData\TranslatableInterface $translation
+   * @param \Drupal\Core\Entity\EntityInterface $translation
    *   The translation object the property value should be retrieved from.
    * @param string $property
    *   The property name.
@@ -277,7 +276,7 @@ protected function getTranslation(EntityInterface $entity, $langcode) {
    * @return
    *   The property value.
    */
-  protected function getValue(ComplexDataInterface $translation, $property, $langcode) {
+  protected function getValue(EntityInterface $translation, $property, $langcode) {
     $key = $property == 'user_id' ? 'target_id' : 'value';
     // @todo remove EntityBCDecorator condition once EntityBCDecorator is gone.
     if (($translation instanceof EntityInterface) && !($translation instanceof EntityNG) && !($translation instanceof EntityBCDecorator)) {
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index 6f6c740..b547b3f 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -279,7 +279,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() && $entity->isTranslatable() &&
+  return empty($entity->getOriginal()->language()->locked) && language_multilingual() && $entity->isTranslatable() &&
     (user_access('create entity translations') || user_access('update entity translations') || user_access('delete entity translations'));
 }
 
@@ -332,7 +332,7 @@ function translation_entity_edit_access(EntityInterface $entity, Language $langu
   $language = !empty($language) ? $language : language(Language::TYPE_CONTENT);
   $translations = $entity->getTranslationLanguages();
   $languages = language_list();
-  return isset($languages[$language->langcode]) && $language->langcode != $entity->language()->langcode && isset($translations[$language->langcode]) && translation_entity_access($entity, 'update');
+  return isset($languages[$language->langcode]) && $language->langcode != $entity->getOriginal()->language()->langcode && isset($translations[$language->langcode]) && translation_entity_access($entity, 'update');
 }
 
 /**
@@ -348,7 +348,7 @@ function translation_entity_delete_access(EntityInterface $entity, Language $lan
   $language = !empty($language) ? $language : language(Language::TYPE_CONTENT);
   $translations = $entity->getTranslationLanguages();
   $languages = language_list();
-  return isset($languages[$language->langcode]) && $language->langcode != $entity->language()->langcode && isset($translations[$language->langcode]) && translation_entity_access($entity, 'delete');
+  return isset($languages[$language->langcode]) && $language->langcode != $entity->getOriginal()->language()->langcode && isset($translations[$language->langcode]) && translation_entity_access($entity, 'delete');
 }
 
 /**
@@ -645,7 +645,7 @@ function translation_entity_field_language_alter(&$display_language, $context) {
     $instances = field_info_instances($entity_type, $entity->bundle());
     // Avoid altering the real entity.
     $entity = clone($entity);
-    $entity_langcode = $entity->language()->langcode;
+    $entity_langcode = $entity->getOriginal()->language()->langcode;
 
     foreach ($entity->translation as $langcode => $translation) {
       if ($langcode == $context['langcode'] || !translation_entity_view_access($entity, $langcode)) {
@@ -705,7 +705,11 @@ function translation_entity_load_translation_metadata(array $entities, $entity_t
     // @todo Declare these as entity (translation?) properties.
     foreach ($record as $field_name => $value) {
       if (!in_array($field_name, $exclude)) {
-        $entity->translation[$record->langcode][$field_name] = $value;
+        $langcode = $record->langcode;
+        $entity->translation[$langcode][$field_name] = $value;
+        if (!$entity->hasTranslation($langcode)) {
+          $entity->initTranslation($langcode);
+        }
       }
     }
   }
@@ -866,8 +870,10 @@ function translation_entity_field_info_alter(&$info) {
  */
 function translation_entity_field_attach_presave(EntityInterface $entity) {
   if ($entity->isTranslatable()) {
-    $attributes = drupal_container()->get('request')->attributes;
-    Drupal::service('translation_entity.synchronizer')->synchronizeFields($entity, $attributes->get('working_langcode'), $attributes->get('source_langcode'));
+    // @todo Avoid using request attributes once translation metadata become
+    //   regular fields.
+    $attributes = Drupal::request()->attributes;
+    Drupal::service('translation_entity.synchronizer')->synchronizeFields($entity, $entity->language()->langcode, $attributes->get('source_langcode'));
   }
 }
 
diff --git a/core/modules/translation_entity/translation_entity.pages.inc b/core/modules/translation_entity/translation_entity.pages.inc
index c7de9e2..d1b6c44 100644
--- a/core/modules/translation_entity/translation_entity.pages.inc
+++ b/core/modules/translation_entity/translation_entity.pages.inc
@@ -19,7 +19,7 @@ function translation_entity_overview(EntityInterface $entity) {
   $controller = translation_entity_controller($entity->entityType());
   $entity_manager = Drupal::entityManager();
   $languages = language_list();
-  $original = $entity->language()->langcode;
+  $original = $entity->getOriginal()->language()->langcode;
   $translations = $entity->getTranslationLanguages();
   $field_ui = module_exists('field_ui') && user_access('administer ' . $entity->entityType() . ' fields');
 
@@ -238,19 +238,13 @@ function translation_entity_edit_page(EntityInterface $entity, Language $languag
  */
 function translation_entity_prepare_translation(EntityInterface $entity, Language $source, Language $target) {
   // @todo Unify field and property handling.
-  $instances = field_info_instances($entity->entityType(), $entity->bundle());
   $entity = $entity->getNGEntity();
   if ($entity instanceof EntityNG) {
     $source_translation = $entity->getTranslation($source->langcode);
-    $target_translation = $entity->getTranslation($target->langcode);
-    foreach ($target_translation->getPropertyDefinitions() as $property_name => $definition) {
-      // @todo The "key" part should not be needed. Remove it as soon as things
-      // do not break.
-      $key = key($entity->{$property_name}[0]->getProperties());
-      $target_translation->$property_name->{$key} = $source_translation->$property_name->{$key};
-    }
+    $entity->addTranslation($target->langcode, $source_translation->getPropertyValues());
   }
   else {
+    $instances = field_info_instances($entity->entityType(), $entity->bundle());
     foreach ($instances as $field_name => $instance) {
       $field = field_info_field($field_name);
       if (!empty($field['translatable'])) {
diff --git a/core/modules/user/lib/Drupal/user/ProfileTranslationController.php b/core/modules/user/lib/Drupal/user/ProfileTranslationController.php
index c1b2414..59a34f9 100644
--- a/core/modules/user/lib/Drupal/user/ProfileTranslationController.php
+++ b/core/modules/user/lib/Drupal/user/ProfileTranslationController.php
@@ -8,12 +8,12 @@
 namespace Drupal\user;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\translation_entity\EntityTranslationController;
+use Drupal\translation_entity\EntityTranslationControllerNG;
 
 /**
  * Defines the translation controller class for terms.
  */
-class ProfileTranslationController extends EntityTranslationController {
+class ProfileTranslationController extends EntityTranslationControllerNG {
 
   /**
    * Overrides EntityTranslationController::entityFormAlter().
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 beaee0a..67f92d1 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -924,14 +924,15 @@ public function getExportProperties() {
    * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslation().
    */
   public function getTranslation($langcode, $strict = TRUE) {
-    return $this->storage->getTranslation($langcode, $strict);
+    // @todo Revisit this once config entities are converted to NG.
+    return $this;
   }
 
   /**
    * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages().
    */
-  public function getTranslationLanguages($include_default = TRUE) {
-    return $this->storage->getTranslationLanguages($include_default);
+  public function getTranslationLanguages($include_default = TRUE, $include_removed = FALSE) {
+    return $this->storage->getTranslationLanguages($include_default, $include_removed);
   }
 
   /**
@@ -1047,6 +1048,48 @@ public function isTranslatable() {
   }
 
   /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::getOriginal().
+   */
+  public function getOriginal() {
+    return $this->storage->getOriginal();
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::hasTranslation().
+   */
+  public function hasTranslation($langcode) {
+    return $this->storage->hasTranslation($langcode);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::addTranslation().
+   */
+  public function addTranslation($langcode, array $values = array()) {
+    return $this->storage->addTranslation($langcode, $values);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::removeTranslation().
+   */
+  public function removeTranslation($langcode) {
+    $this->storage->removeTranslation($langcode);
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationStatus().
+   */
+  public function getTranslationStatus() {
+    return $this->storage->getTranslationStatus();
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TranslatableInterface::getTranslationStatus().
+   */
+  public function initTranslation($langcode) {
+    $this->storage->initTranslation($langcode);
+  }
+
+  /**
    * Implements \Drupal\Core\TypedData\TypedDataInterface::getType().
    */
   public function getType() {
