diff --git a/core/modules/node/lib/Drupal/node/NodeTranslationController.php b/core/modules/node/lib/Drupal/node/NodeTranslationController.php
index da078ac..e6a2523 100644
--- a/core/modules/node/lib/Drupal/node/NodeTranslationController.php
+++ b/core/modules/node/lib/Drupal/node/NodeTranslationController.php
@@ -29,14 +29,20 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
     parent::entityFormAlter($form, $form_state, $entity);
 
     // Move the translation fieldset to a vertical tab.
-    if (isset($form['translation'])) {
-      $form['translation'] += array(
+    if (isset($form['translation_entity'])) {
+      $form['translation_entity'] += array(
         '#group' => 'additional_settings',
         '#weight' => 100,
         '#attributes' => array(
           'class' => array('node-translation-options'),
         ),
       );
+
+      // We do not need to show these values on node forms: they inherit the
+      // basic node property values.
+//       $form['translation_entity']['status']['#access'] = FALSE;
+      $form['translation_entity']['name']['#access'] = FALSE;
+      $form['translation_entity']['created']['#access'] = FALSE;
     }
   }
 
@@ -47,4 +53,18 @@ protected function entityFormTitle(EntityInterface $entity) {
     $type_name = node_get_type_label($entity);
     return t('<em>Edit @type</em> @title', array('@type' => $type_name, '@title' => $entity->label()));
   }
+
+  /**
+   * Overrides EntityTranslationController::entityFormEntityBuild().
+   */
+  public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, array &$form_state) {
+    if (isset($form_state['values']['translation_entity'])) {
+      $translation = &$form_state['values']['translation_entity'];
+//       $translation['status'] = $form_state['values']['status'];
+      $translation['name'] = $form_state['values']['name'];
+      $translation['created'] = $form_state['values']['date'];
+    }
+    parent::entityFormEntityBuild($entity_type, $entity, $form, $form_state);
+  }
+
 }
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 26a2699..1efcb08 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
@@ -64,7 +64,7 @@ public function retranslate(EntityInterface $entity, $langcode = NULL) {
     $updated_langcode = !empty($langcode) ? $langcode : $entity->language()->langcode;
     $translations = $entity->getTranslationLanguages();
     foreach ($translations as $langcode => $language) {
-      $entity->retranslate[$langcode] = $langcode != $updated_langcode;
+      $entity->translation[$langcode]['translate'] = $langcode != $updated_langcode;
     }
   }
 
@@ -213,7 +213,7 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
     // We need to display the translation tab only when there is at least one
     // translation available or a new one is about to be created.
     if ($new_translation || $has_translations) {
-      $form['translation'] = array(
+      $form['translation_entity'] = array(
         '#type' => 'fieldset',
         '#title' => t('Translation'),
         '#collapsible' => TRUE,
@@ -224,9 +224,35 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
         '#multilingual' => TRUE,
       );
 
-      $translate = !$new_translation && $entity->retranslate[$form_langcode];
+      // A new translation is enabled by default.
+      $status = $new_translation || $entity->translation[$form_langcode]['status'];
+      // If there is only one published translation we cannot unpublish it,
+      // since there would be nothing left to display.
+      $enabled = !$status;
+      if (!empty($status)) {
+        // A new translation is not available in the translation metadata, hence
+        // it should count as one more.
+        $published = $new_translation;
+        foreach ($entity->translation as $langcode => $translation) {
+          $published += $translation['status'];
+        }
+        $enabled = $published > 1;
+      }
+      $description = $enabled ?
+        t('An unpublished translation will not be visible without translation permissions.') :
+        t('Only this translation is published. You must publish at least one more translation to unpublish this one.');
+
+      $form['translation_entity']['status'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('This translation is published'),
+        '#default_value' => $status,
+        '#description' => $description,
+        '#disabled' => !$enabled,
+      );
+
+      $translate = !$new_translation && $entity->translation[$form_langcode]['translate'];
       if (!$translate) {
-        $form['translation']['retranslate'] = array(
+        $form['translation_entity']['retranslate'] = array(
           '#type' => 'checkbox',
           '#title' => t('Flag other translations as outdated'),
           '#default_value' => FALSE,
@@ -234,7 +260,7 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
         );
       }
       else {
-        $form['translation']['translate'] = array(
+        $form['translation_entity']['translate'] = array(
           '#type' => 'checkbox',
           '#title' => t('This translation needs to be updated'),
           '#default_value' => $translate,
@@ -242,6 +268,25 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
         );
       }
 
+      $name = $new_translation ? $GLOBALS['user']->name : user_load($entity->translation[$form_langcode]['uid'])->name;
+      $form['translation_entity']['name'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Authored by'),
+        '#maxlength' => 60,
+        '#autocomplete_path' => 'user/autocomplete',
+        '#default_value' => $name,
+        '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
+      );
+
+      $date = $new_translation ? REQUEST_TIME : $entity->translation[$form_langcode]['created'];
+      $form['translation_entity']['created'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Authored on'),
+        '#maxlength' => 25,
+        '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => format_date($date, 'custom', 'Y-m-d H:i:s O'), '%timezone' => format_date($date, 'custom', 'O'))),
+        '#default_value' => $new_translation ? '' : format_date($date, 'custom', 'Y-m-d H:i:s O'),
+      );
+
       if ($language_widget) {
         $form_langcode['#multilingual'] = TRUE;
       }
@@ -252,6 +297,11 @@ public function entityFormAlter(array &$form, array &$form_state, EntityInterfac
     // Process the submitted values before they are stored.
     $form['#entity_builders'][] = array($this, 'entityFormEntityBuild');
 
+    // Handle entity validation.
+    if (isset($form['actions']['submit'])) {
+      $form['actions']['submit']['#validate'][] = array($this, 'entityFormValidate');
+    }
+
     // Handle entity deletion.
     if (isset($form['actions']['delete'])) {
       $form['actions']['delete']['#submit'][] = array($this, 'entityFormDelete');
@@ -348,25 +398,52 @@ protected function addTranslatabilityClue(&$element) {
   public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, array &$form_state) {
     $form_controller = translation_entity_form_controller($form_state);
     $form_langcode = $form_controller->getFormLangcode($form_state);
-    $source_langcode = $this->getSourceLangcode($form_state);
 
-    if ($source_langcode) {
-      // @todo Use the entity setter when all entities support multilingual
-      // properties.
-      $entity->source[$form_langcode] = $source_langcode;
+    if (!isset($entity->translation[$form_langcode])) {
+      $entity->translation[$form_langcode] = array();
     }
+    $values = isset($form_state['values']['translation_entity']) ? $form_state['values']['translation_entity'] : array();
+    $translation = &$entity->translation[$form_langcode];
 
-    // Ensure every key has at least a default value. Subclasses may provide
-    // entity-specific values to alter them.
-    $values = isset($form_state['values']['translation']) ? $form_state['values']['translation'] : array();
-    $entity->retranslate[$form_langcode] = isset($values['translate']) && $values['translate'];
+    // @todo Use the entity setter when all entities support multilingual
+    // properties.
+    $account = user_load_by_name($values['name']);
+    $translation['uid'] = !empty($account) ? $account->uid : 0;
+    $translation['status'] = $values['status'];
+    $translation['created'] = !empty($values['created']) ? strtotime($values['created']) : REQUEST_TIME;
+    $translation['changed'] = REQUEST_TIME;
 
+    $source_langcode = $this->getSourceLangcode($form_state);
+    if ($source_langcode) {
+      $translation['source'] = $source_langcode;
+    }
+
+    $translation['translate'] = isset($values['translate']) && $values['translate'];
     if (!empty($values['retranslate'])) {
       $this->retranslate($entity, $form_langcode);
     }
   }
 
   /**
+   * Form validation handler for EntityTranslationController::entityFormAlter().
+   *
+   * Validates the submitted entity translation metadata.
+   */
+  function entityFormValidate($form, &$form_state) {
+    if (!empty($form_state['values']['translation_entity'])) {
+      $translation = $form_state['values']['translation_entity'];
+      // Validate the "authored by" field.
+      if (!empty($translation['name']) && !($account = user_load_by_name($translation['name']))) {
+        form_set_error('translation_entity][name', t('The translation authoring username %name does not exist.', array('%name' => $translation['name'])));
+      }
+      // Validate the "authored on" field.
+      if (!empty($translation['created']) && strtotime($translation['created']) === FALSE) {
+        form_set_error('translation_entity][created', t('You have to specify a valid translation authoring date.'));
+      }
+    }
+  }
+
+  /**
    * Form submission handler for EntityTranslationController::entityFormAlter().
    *
    * Takes care of the source language change.
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 934ed62..fe22b4f 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
@@ -182,7 +182,7 @@ function testTranslationUI() {
 
     // Add another translation and mark the other ones as outdated.
     $values[$langcode] = $this->getNewEntityValues($langcode);
-    $edit = $this->getEditValues($values, $langcode) + array('translation[retranslate]' => TRUE);
+    $edit = $this->getEditValues($values, $langcode) + array('translation_entity[retranslate]' => TRUE);
     $this->drupalPost($path, $edit, t('Save'));
     $entity = entity_load($this->entityType, $entity->id(), TRUE);
 
@@ -203,16 +203,16 @@ function testTranslationUI() {
       $path = $prefix . $controller->getEditPath($entity);
       $this->drupalGet($path);
       if ($enabled_langcode == $langcode) {
-        $this->assertFieldByXPath('//input[@name="translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
+        $this->assertFieldByXPath('//input[@name="translation_entity[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
       }
       else {
-        $this->assertFieldByXPath('//input[@name="translation[translate]"]', TRUE, 'The translate flag is checked by default.');
-        $edit = array('translation[translate]' => FALSE);
+        $this->assertFieldByXPath('//input[@name="translation_entity[translate]"]', TRUE, 'The translate flag is checked by default.');
+        $edit = array('translation_entity[translate]' => FALSE);
         $this->drupalPost($path, $edit, t('Save'));
         $this->drupalGet($path);
-        $this->assertFieldByXPath('//input[@name="translation[retranslate]"]', FALSE, 'The retranslate flag is now shown.');
+        $this->assertFieldByXPath('//input[@name="translation_entity[retranslate]"]', FALSE, 'The retranslate flag is now shown.');
         $entity = entity_load($this->entityType, $entity->id(), TRUE);
-        $this->assertFalse($entity->retranslate[$enabled_langcode], 'The "outdated" status has been correctly stored.');
+        $this->assertFalse($entity->translation[$enabled_langcode]['translate'], 'The "outdated" status has been correctly stored.');
       }
     }
 
diff --git a/core/modules/translation_entity/translation_entity.install b/core/modules/translation_entity/translation_entity.install
index e66e544..9562d19 100644
--- a/core/modules/translation_entity/translation_entity.install
+++ b/core/modules/translation_entity/translation_entity.install
@@ -46,6 +46,30 @@ function translation_entity_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
+      'uid' => array(
+        'description' => 'The author of this translation.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'status' => array(
+        'description' => 'Boolean indicating whether the translation is visible to non-translators.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'created' => array(
+        'description' => 'The Unix timestamp when the translation was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the translation was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
     ),
     'primary key' => array('entity_type', 'entity_id', 'langcode'),
   );
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index f57773b..7099419 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -234,6 +234,22 @@ function translation_entity_translate_access(EntityInterface $entity) {
 }
 
 /**
+ * Checks whether the given user can view the specified translation.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity whose translation overview should be displayed.
+ * @param $langcode
+ *   The language code of the translation to be displayed.
+ * @param \Drupal\user\Plugin\Core\Entity\User $account
+ *   (optional) The account for which view access should be checked. Defaults to
+ *   the current user.
+ */
+function translation_entity_view_access(EntityInterface $entity, $langcode, $account = NULL) {
+  $entity_type = $entity->entityType();
+  return !empty($entity->translation[$langcode]['status']) || user_access('translate any entity', $account) || user_access("translate $entity_type entities", $account);
+}
+
+/**
  * Access callback for the translation addition page.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
@@ -453,6 +469,35 @@ function translation_entity_form_alter(array &$form, array &$form_state) {
 }
 
 /**
+ * Implements hook_field_language_alter().
+ *
+ * Performs language fallback for unaccessible translations.
+ */
+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'])) {
+    $instances = field_info_instances($entity_type, $entity->bundle());
+    $entity = clone($entity);
+
+    foreach ($entity->translation as $langcode => $translation) {
+      if ($langcode == $context['langcode'] || !translation_entity_view_access($entity, $langcode)) {
+        // Unset unaccessible field translations: if the field is untranslatable
+        // unsetting a language different from LANGUAGE_NOT_SPECIFIED has no
+        // effect.
+        foreach ($instances as $instance) {
+          unset($entity->{$instance['field_name']}[$langcode]);
+        }
+      }
+    }
+
+    // Find the new fallback values.
+    field_language_fallback($display_language, $entity, $context['langcode']);
+  }
+}
+
+/**
  * Implements hook_entity_load().
  */
 function translation_entity_entity_load(array $entities, $entity_type) {
@@ -467,7 +512,7 @@ function translation_entity_entity_load(array $entities, $entity_type) {
   }
 
   if (!empty($enabled_entities)) {
-    translation_entity_load_translation_data($enabled_entities, $entity_type);
+    translation_entity_load_translation_metadata($enabled_entities, $entity_type);
   }
 }
 
@@ -479,20 +524,22 @@ function translation_entity_entity_load(array $entities, $entity_type) {
  * @param string $entity_type
  *   The type of the entities.
  */
-function translation_entity_load_translation_data(array $entities, $entity_type) {
+function translation_entity_load_translation_metadata(array $entities, $entity_type) {
   $result = db_select('translation_entity', 'te')
     ->fields('te', array())
     ->condition('te.entity_type', $entity_type)
     ->condition('te.entity_id', array_keys($entities))
     ->execute();
 
+  $exclude = array('entity_type', 'entity_id', 'langcode');
   foreach ($result as $record) {
     $entity = $entities[$record->entity_id];
     // @todo Declare these as entity (translation?) properties.
-    $entity->source[$record->langcode] = $record->source;
-    // @todo Rename to 'translate' when the column is removed from the node
-    //   schema.
-    $entity->retranslate[$record->langcode] = (boolean) $record->translate;
+    foreach ($record as $field_name => $value) {
+      if (!in_array($field_name, $exclude)) {
+        $entity->translation[$record->langcode][$field_name] = $value;
+      }
+    }
   }
 }
 
@@ -505,16 +552,32 @@ function translation_entity_entity_insert(EntityInterface $entity) {
     return;
   }
 
-  $entity_type = $entity->entityType();
-  $id = $entity->id();
-  $query = db_insert('translation_entity')
-    ->fields(array('entity_type', 'entity_id', 'langcode', 'source', 'translate'));
+  $fields = array('entity_type', 'entity_id', 'langcode', 'source', 'translate', 'uid', 'status', 'created', 'changed');
+  $query = db_insert('translation_entity')->fields($fields);
 
   foreach ($entity->getTranslationLanguages() as $langcode => $language) {
-    // @todo Declare these as entity (translation?) properties.
-    $source = (isset($entity->source[$langcode]) ? $entity->source[$langcode] : NULL) . '';
-    $retranslate = intval(!empty($entity->retranslate[$langcode]));
-    $query->values(array($entity_type, $id, $langcode, $source, $retranslate));
+    $translation = isset($entity->translation[$langcode]) ? $entity->translation[$langcode] : array();
+
+    $translation += array(
+      'source' => '',
+      'uid' => $GLOBALS['user']->uid,
+      'translate' => FALSE,
+      'status' => TRUE,
+      'created' => REQUEST_TIME,
+      'changed' => REQUEST_TIME,
+    );
+
+    $translation['entity_type'] = $entity->entityType();
+    $translation['entity_id'] = $entity->id();
+    $translation['langcode'] = $langcode;
+
+    // Reorder values to match the schema.
+    $values = array();
+    foreach ($fields as $field_name) {
+      $value = is_bool($translation[$field_name]) ? intval($translation[$field_name]) : $translation[$field_name];
+      $values[$field_name] = $value;
+    }
+    $query->values($values);
   }
 
   $query->execute();
diff --git a/core/modules/translation_entity/translation_entity.pages.inc b/core/modules/translation_entity/translation_entity.pages.inc
index be06e6a..50a43e0 100644
--- a/core/modules/translation_entity/translation_entity.pages.inc
+++ b/core/modules/translation_entity/translation_entity.pages.inc
@@ -69,7 +69,7 @@ function translation_entity_overview(EntityInterface $entity) {
 
       if (isset($translations[$langcode])) {
         // Existing translation in the translation set: display status.
-        $source = isset($entity->source[$langcode]) ? $entity->source[$langcode] : '';
+        $source = isset($entity->translation[$langcode]['source']) ? $entity->translation[$langcode]['source'] : '';
         $is_original = $langcode == $original;
         $translation = $translations[$langcode];
         $label = $entity->label($langcode);
@@ -85,12 +85,10 @@ function translation_entity_overview(EntityInterface $entity) {
           $links['edit']['title'] = t('edit');
         }
 
-        // @todo Consider supporting the ability to track translation publishing
-        // status independently from entity status, as it may not exist.
-        $translation = $entity->getTranslation($langcode, FALSE);
-        $status = !isset($translation->status) || $translation->status ? t('Published') : t('Not published');
+        $translation = $entity->translation[$langcode];
+        $status = !empty($translation['status']) ? t('Published') : t('Not published');
         // @todo Add a theming function here.
-        $status = '<span class="status">' . $status . '</span>' . (!empty($entity->retranslate[$langcode]) ? ' <span class="marker">' . t('outdated') . '</span>' : '');
+        $status = '<span class="status">' . $status . '</span>' . (!empty($translation['translate']) ? ' <span class="marker">' . t('outdated') . '</span>' : '');
 
         if ($is_original) {
           $language_name = t('<strong>@language_name</strong>', array('@language_name' => $language_name));
@@ -255,8 +253,10 @@ function translation_entity_delete_confirm_submit(array $form, array &$form_stat
   $entity->save();
 
   // Remove any existing path alias for the removed translation.
+  // @todo This should be taken care of by the Path module.
   if (module_exists('path')) {
-    path_delete(array('source' => $controller->getViewPath($entity), 'langcode' => $language->langcode));
+    $conditions = array('source' => $controller->getViewPath($entity), 'langcode' => $language->langcode);
+    drupal_container()->get('path.crud')->delete($conditions);
   }
 
   $form_state['redirect'] = $controller->getBasePath($entity) . '/translations';
