diff --git a/includes/tmgmt.entity.inc b/includes/tmgmt.entity.inc
index 2759c88..fe2ef2d 100755
--- a/includes/tmgmt.entity.inc
+++ b/includes/tmgmt.entity.inc
@@ -663,6 +663,9 @@ class TMGMTJobItem extends Entity {
   /**
    * Recursively writes translated data to the data array of a job item.
    *
+   * While doing this the #status of each data item is set to
+   * TMGMT_DATA_ITEM_STATE_TRANSLATED.
+   *
    * @param $translation
    *   Nested array of translated data. Can either be a single text entry, the
    *   whole data structure or parts of it.
@@ -672,19 +675,12 @@ class TMGMTJobItem extends Entity {
    *   means that it will replace the whole translated data array.
    */
   protected function addTranslatedDataRecursive($translation, $key = array()) {
-    if (empty($key)) {
-      $key = array();
-    }
-    if (!is_array($key)) {
-      $key = explode(TMGMT_ARRAY_DELIMITER, $key);
-    }
     if (isset($translation['#text'])) {
-      $exists = FALSE;
-      drupal_array_get_nested_value($this->data, array_merge($key, array('#text')), $exists);
-      if ($exists) {
-        $translation['#finished'] = TRUE;
-        drupal_array_set_nested_value($this->data, array_merge($key, array('#translation')), $translation);
-      }
+      $values = array(
+        '#translation' => $translation,
+        '#status' => TMGMT_DATA_ITEM_STATE_TRANSLATED,
+      );
+      $this->updateData($key, $values);
       return;
     }
     foreach (element_children($translation) as $item) {
@@ -693,8 +689,47 @@ class TMGMTJobItem extends Entity {
   }
 
   /**
+   * Updates the values for a specific substructure in the data array.
+   *
+   * The values are either set or updated but never deleted.
+   *
+   * @param $key
+   *   Key pointing to the item the values should be applied.
+   *   The key can be either be an array containing the keys of a nested array
+   *   hierarchy path or a string with '][' or '|' as delimiter.
+   * @param $values
+   *   Nested array of values to set.
+   */
+  public function updateData($key, $values = array()) {
+    foreach ($values as $index => $value) {
+      // In order to preserve existing values, we can not aplly the values array
+      // at once. We need to apply each containing value on its own.
+      // If $value is an array we need to advance the hierarchy level.
+      if (is_array($value)) {
+        $this->updateData(array_merge(tmgmt_ensure_keys_array($key), array($index)), $value);
+      }
+      // Apply the value.
+      else {
+        drupal_array_set_nested_value($this->data, array_merge(tmgmt_ensure_keys_array($key), array($index)), $value);
+      }
+    }
+  }
+
+  /**
    * Adds translated data to a job item.
    *
+   * This function calls for TMGMTJobItem::addTranslatedDataRecursive() which
+   * sets the status of each added data item to TMGMT_DATA_ITEM_STATE_TRANSLATED.
+   *
+   * If all data items are translated, the status of the job item is updated to
+   * needs review.
+   *
+   * @todo
+   * To update the job item status to needs review we could take advantage of
+   * the TMGMTJobItem::getCountPending() and TMGMTJobItem::getCountTranslated().
+   * The catch is, that this counter gets updated while saveing which not yet
+   * hapened.
+   *
    * @param $translation
    *   Nested array of translated data. Can either be a single text entry, the
    *   whole data structure or parts of it.
@@ -713,7 +748,7 @@ class TMGMTJobItem extends Entity {
       $data = array_filter($data, '_tmgmt_filter_data');
       $finished = TRUE;
       foreach ($data as $item) {
-        if (empty($item['#translation']['#finished'])) {
+        if (empty($item['#status']) || $item['#status'] == TMGMT_DATA_ITEM_STATE_PENDING) {
           $finished = FALSE;
           break;
         }
diff --git a/includes/tmgmt.plugin.inc b/includes/tmgmt.plugin.inc
index a4ef99b..35eb78f 100755
--- a/includes/tmgmt.plugin.inc
+++ b/includes/tmgmt.plugin.inc
@@ -260,6 +260,54 @@ interface TMGMTTranslatorPluginControllerInterface extends TMGMTPluginBaseInterf
    */
   public function hasCheckoutSettings(TMGMTJob $job);
 
+  /**
+   * Approve one single data item.
+   *
+   * @todo Using job item breaks the current convention which uses jobs.
+   *
+   * @param $job_item
+   *   The Job item the approved data item belongs to.
+   * @param $key
+   *   The key of the approved data item.
+   *   The key is an array containing the keys of a nested array hierarchy path.
+   *
+   * @return
+   *   TRUE if the approving was succesfull, FALSE otherwise.
+   *   In case of an error, it is the responsibility of the translator to
+   *   provide informations about the failure by adding a message to the job
+   *   item.
+   */
+  public function approveDataItem(TMGMTJobItem $job_item, array $key);
+
+}
+
+/**
+ * Handle reject on data item level.
+ *
+ * Implement this interface in a translator plugin to signal that this plugin is
+ * capable of handling a reject of single data items.
+ *
+ * @ingroup tmgmt_translator
+ */
+interface TMGMTTranslatorRejectDataItem {
+
+  /**
+   * Reject one single data item.
+   *
+   * @todo Using job item breaks the current convention which uses jobs.
+   *
+   * @param $job_item
+   *   The job item to which the rejected data item belongs.
+   * @param $key
+   *   The key of the approved data item.
+   *   The key is an array containing the keys of a nested array hierarchy path.
+   *
+   * @return
+   *   TRUE if the reject was succesfull, else FALSE.
+   *   In case of an error, it is the responsibility of the translator to
+   *   provide informations about the faliure.
+   */
+  public function rejectDataItem(TMGMTJobItem $job_item, array $key);
 }
 
 /**
@@ -334,4 +382,10 @@ abstract class TMGMTDefaultTranslatorPluginController extends TMGMTPluginBase im
     return TRUE;
   }
 
+  /**
+   * Implements TMGMTTranslatorPluginControllerInterface::approveDataItem().
+   */
+  public function approveDataItem(TMGMTJobItem $job_item, array $key) {
+    return TRUE;
+  }
 }
diff --git a/tests/tmgmt_test.plugin.inc b/tests/tmgmt_test.plugin.inc
index 8d0ef8f..49d57e2 100755
--- a/tests/tmgmt_test.plugin.inc
+++ b/tests/tmgmt_test.plugin.inc
@@ -54,7 +54,7 @@ class TMGMTTestTranslatorUIController extends TMGMTDefaultTranslatorUIController
 
 }
 
-class TMGMTTestTranslatorPluginController extends TMGMTDefaultTranslatorPluginController {
+class TMGMTTestTranslatorPluginController extends TMGMTDefaultTranslatorPluginController implements TMGMTTranslatorRejectDataItem {
 
   /**
    * Overrides TMGMTDefaultTranslatorPluginController::hasCheckoutSettings().
@@ -119,6 +119,14 @@ class TMGMTTestTranslatorPluginController extends TMGMTDefaultTranslatorPluginCo
     return $languages;
   }
 
+  /**
+   * Overrides TMGMTTranslatorRejectDataItem::rejectDataItem().
+   */
+  public function rejectDataItem(TMGMTJobItem $job_item, array $key) {
+    $key = '[' . implode('][', $key) . ']';
+    $job_item->addMessage('Rejected data item @key for job item @item in job @job.', array('@key' => $key, '@item' => $job_item->tjiid, '@job' => $job_item->tjid));
+    return TRUE;
+  }
 }
 
 class TMGMTTestSourcePluginController extends TMGMTDefaultSourcePluginController {
diff --git a/tmgmt.install b/tmgmt.install
index d401b52..1e90792 100755
--- a/tmgmt.install
+++ b/tmgmt.install
@@ -375,7 +375,7 @@ function tmgmt_update_7003(&$sandbox) {
   if (!isset($sandbox['progress'])) {
     $sandbox['progress'] = 0;
     $sandbox['last_tjiid'] = 0;
-    $sandbox['max'] = db_query('SELECT COUNT(DISTINCT tjiid) FROM {tmgmt_job_item}')->fetchField();
+    $sandbox['max'] = db_query('SELECT COUNT(tjiid) FROM {tmgmt_job_item}')->fetchField();
   }
   $tjiids = db_query('SELECT tjiid FROM {tmgmt_job_item} WHERE tjiid > :last_tjiid ORDER BY tjiid LIMIT 10', array( ':last_tjiid' => $sandbox['last_tjiid']))->fetchCol();
   foreach (entity_load('tmgmt_job_item', $tjiids) as $job_item) {
@@ -387,3 +387,35 @@ function tmgmt_update_7003(&$sandbox) {
   $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
   $sandbox['last_tjiid'] = $job_item->tjiid;
 }
+
+/**
+ * Replace the [#translation][#finished] attribute with [#status].
+ */
+function tmgmt_update_7004(&$sandbox) {
+  if (!isset($sandbox['progress'])) {
+    $sandbox['progress'] = 0;
+    $sandbox['last_tjiid'] = 0;
+    $sandbox['max'] = db_query('SELECT COUNT(tjiid) FROM {tmgmt_job_item}')->fetchField();
+  }
+  $tjiids = db_query('SELECT tjiid FROM {tmgmt_job_item} WHERE tjiid > :last_tjiid ORDER BY tjiid LIMIT 10', array(':last_tjiid' => $sandbox['last_tjiid']))->fetchCol();
+  foreach (entity_load('tmgmt_job_item', $tjiids) as $job_item) {
+    $data = tmgmt_flatten_data($job_item->getData());
+    $data = array_filter($data, '_tmgmt_filter_data');
+    foreach ($data as $key => $values) {
+      if (!empty($values['#translation']['#finished'])) {
+        $finished = $values['#translation']['#finished'];
+        unset($values['#translation']['#finished']);
+        drupal_array_set_nested_value($job_item->data, array_merge($key, array('#translation')), $values['#translation']);
+        if ($finished && (empty($values['#status']) || $values['#status'] == TMGMT_DATA_ITEM_STATE_PENDING)) {
+          $values['#status'] = TMGMT_DATA_ITEM_STATE_TRANSLATED;
+        }
+        $job_item->updateData($key, $values);
+      }
+    }
+    $job_item->save();
+    $sandbox['progress']++;
+  }
+
+  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
+  $sandbox['last_tjiid'] = $job_item->tjiid;
+}
diff --git a/tmgmt.module b/tmgmt.module
index da71420..483c774 100644
--- a/tmgmt.module
+++ b/tmgmt.module
@@ -1099,6 +1099,28 @@ function tmgmt_flatten_data($data, $prefix = NULL, $label = array()) {
   }
   return $flattened_data;
 }
+/**
+ * Converts string keys to array keys.
+ *
+ * There are three conventions for data keys in use. This function accepts each
+ * of it an ensures a array of keys.
+ *
+ * @param $key
+ *   The key can be either be an array containing the keys of a nested array
+ *   hierarchy path or a string with '][' or '|' as delimiter.
+ *
+ * @return
+ *   Array of keys.
+ */
+function tmgmt_ensure_keys_array($key) {
+  if (!is_array($key)) {
+    if (strstr($key, '|')) {
+      $key = str_replace('|', TMGMT_ARRAY_DELIMITER, $key);
+    }
+    $key = explode(TMGMT_ARRAY_DELIMITER, $key);
+  }
+  return $key;
+}
 
 /**
  * Converts a flattened data structure into a nested array.
diff --git a/ui/css/tmgmt_ui.admin.css b/ui/css/tmgmt_ui.admin.css
index 4cf8c9b..66d1f18 100644
--- a/ui/css/tmgmt_ui.admin.css
+++ b/ui/css/tmgmt_ui.admin.css
@@ -21,3 +21,104 @@ table.tmgmt-ui-review tbody th {
   background: none;
   font-weight: bold;
 }
+
+.tmgmt-ui-data-item-label .form-item {
+  /* Get the label out of way, so the status icon can be placed at the same
+  relative position */
+  position: absolute;
+}
+
+.tmgmt-ui-state {
+  /* Visually align the icon in the center between left table border and the
+     first textarea.*/
+  margin: 1.8em -10px 1em 0;
+  text-align: center;
+}
+
+.tmgmt-ui-state .tmgmt-ui-icon {
+  margin: 0 auto;
+}
+
+/* Base settings for the icon. */
+
+.tmgmt-ui-icon {
+  color: #fff;
+  text-align: center;
+  border-style: solid;
+  box-shadow: 0 0 4px rgba(0,0,0,0.4);
+  -moz-box-shadow: 0 0 4px rgba(0,0,0,0.4);
+  -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.4);
+}
+
+
+/* Icon colors. */
+.tmgmt-ui-icon-green {
+  border-color: #080f00;
+  background: #336600;
+  background : -webkit-gradient(linear, left top, left bottom, from(#5dba00), to(#336600));
+  background : -moz-linear-gradient(top, #5dba00, #336600);
+}
+
+.tmgmt-ui-icon-grey {
+  border-color: #444;
+  background: #666;
+  background : -webkit-gradient(linear, left top, left bottom, from(#aaa), to(#666));
+  background : -moz-linear-gradient(top, #aaa, #666);
+}
+
+.tmgmt-ui-icon-yellow {
+  border-color: #544300;
+  background: #a88700;
+  background : -webkit-gradient(linear, left top, left bottom, from(#ffcc00), to(#a88700));
+  background : -moz-linear-gradient(top, #ffcc00, #a88700);
+}
+
+.tmgmt-ui-icon-red {
+  border-color: #210000;
+  background: #750000;
+  background : -webkit-gradient(linear, left top, left bottom, from(#cc0000), to(#750000));
+  background : -moz-linear-gradient(top, #cc0000, #750000);
+}
+
+/* Icon sizes. */
+.tmgmt-ui-icon-128 {
+  width: 128px;
+  height: 128px;
+  border-width: 3px;
+  line-height: 128px;
+  text-shadow: 0 -2px 0 rgba(0,0,0,0.5);
+  border-radius: 10px;
+  -moz-border-radius: 10px;
+}
+
+.tmgmt-ui-icon.tmgmt-ui-icon-128 span {
+  font-size: 118px;
+}
+
+.tmgmt-ui-icon-64 {
+  width: 64px;
+  height: 64px;
+  border-width: 1px;
+  line-height: 64px;
+  text-shadow: 0 -1px 0 rgba(0,0,0,0.5);
+  border-radius: 5px;
+  -moz-border-radius: 5px;
+}
+
+.tmgmt-ui-icon.tmgmt-ui-icon-64 span {
+  font-size: 52px;
+}
+
+.tmgmt-ui-icon-32 {
+  width: 32px;
+  height: 32px;
+  border-width: 1px;
+  line-height: 32px;
+  text-shadow: 0 -1px 0 rgba(0,0,0,0.5);
+  border-radius: 3px;
+  -moz-border-radius: 3px;
+}
+
+.tmgmt-ui-icon.tmgmt-ui-icon-32 span {
+  font-size: 23px;
+}
diff --git a/ui/includes/tmgmt_ui.theme.inc b/ui/includes/tmgmt_ui.theme.inc
index 7a508ce..a4e32cf 100644
--- a/ui/includes/tmgmt_ui.theme.inc
+++ b/ui/includes/tmgmt_ui.theme.inc
@@ -34,3 +34,130 @@ function theme_tmgmt_ui_element($variables) {
   $output = "<div{$attributes}>{$title}{$element['#children']}</div>";
   return $output;
 }
+
+/**
+ * Renders a data item status as an HTML/CSS icon.
+ */
+function theme_tmgmt_ui_translator_review_form_element_status($variables) {
+  $classes = array();
+  $classes[] = 'tmgmt-ui-icon';
+  $classes[] = 'tmgmt-ui-icon-32'; // Icon size 32px square.
+  switch ($variables['status']['#value']) {
+    case TMGMT_DATA_ITEM_STATE_APPROVED:
+      $title = t('Approved');
+      // Unicode character U+2611 BALLOT BOX WITH CHECK.
+      $icon = '&#x2611;';
+      $classes[] = 'tmgmt-ui-icon-green';
+      $classes[] = 'tmgmt-ui-state-approved';
+      break;
+    case TMGMT_DATA_ITEM_STATE_TRANSLATED:
+      $title = t('Translated');
+      // Unicode character U+2610 BALLOT BOX.
+      $icon = '&#x2610;';
+      $classes[] = 'tmgmt-ui-icon-yellow';
+      $classes[] = 'tmgmt-ui-state-translated';
+      break;
+    case TMGMT_DATA_ITEM_STATE_PENDING:
+    default:
+      $title = t('Pending');
+      // Just an empty icon without a sign.
+      $icon = '';
+      $classes[] = 'tmgmt-ui-icon-grey';
+      $classes[] = 'tmgmt-ui-state-pending';
+      break;
+  }
+  return sprintf('<div class="%s" title="%s"><span>%s</span></div>', implode(' ', $classes), $title, $icon);
+}
+
+/**
+ * Render one single data item as a table row.
+ */
+function theme_tmgmt_ui_translator_review_form_element($variables) {
+  $element = $variables['element'];
+  // Label of all element groups.
+  if (!isset($element['#top_label'])) {
+    $element['#top_label'] = array_shift($element['#parent_label']);
+  }
+  // Label of the current data item.
+  if (!isset($element['#leave_label'])) {
+    $element['#leave_label'] = array_pop($element['#parent_label']);
+  }
+  // Do not repeat labels inside the same hierarchy.
+  if ($element['#top_label'] == $element['#leave_label']) {
+    $element['#leave_label'] = '';
+  }
+  $result = '<tr' . drupal_attributes(array('class' => $element['#zebra'], 'id' => $element['#ajaxid'])) . '>';
+  $row = array();
+  $leaf_label = '<label>' . $element['#leave_label'] . '</label>';
+  $leaf_label = '<div class="form-item form-type-label">' . $leaf_label . '</div>';
+  $row[] = array(
+    'data' => $leaf_label . '<div class="tmgmt-ui-state">' . drupal_render($element['status']) . '</div>',
+    'class' => 'tmgmt-ui-data-item-label',
+  );
+  $row[] = array(
+    'data' => drupal_render($element['source']),
+    'class' => 'tmgmt-ui-data-item-source',
+  );
+  $row[] = array(
+    'data' => drupal_render($element['translation']),
+    'class' => 'tmgmt-ui-data-item-translation',
+  );
+  $row[] = array(
+    'data' => drupal_render($element['actions']),
+    'class' => 'tmgmt-ui-data-item-label',
+  );
+  foreach ($row as $cell) {
+    $result .= _theme_table_cell($cell);
+  }
+  $result .= '</tr>';
+  return $result;
+}
+
+/**
+ * Renders a table containing a group of data items belonging to the same field.
+ */
+function theme_tmgmt_ui_translator_review_form($variables) {
+  $element = $variables['element'];
+  $result = '';
+  $labels = '';
+  $parent_label = '';
+  $element_groups = array();
+  $element_group = '';
+  foreach (element_children($element) as $key) {
+    // Label of all element groups.
+    $parent_label = array_shift($element[$key]['#parent_label']);
+    $element[$key]['#top_label'] = $parent_label;
+    $element[$key]['#leave_label'] = array_pop($element[$key]['#parent_label']);
+    // Start a new element group.
+    if ($labels != $element[$key]['#parent_label']) {
+      $labels = $element[$key]['#parent_label'];
+      if (!empty($labels)) {
+        // Append to previous group to the group collection.
+        if (!empty($element_group)) {
+          $element_groups[] = '<tbody>' . $element_group . '</tbody>';
+        }
+        // Header row for the current element group.
+        $cell = array(
+          // @todo: Deal with right to left languages.
+          'data' => check_plain(implode(t(' > '), $labels)),
+          'colspan' => 4,
+        );
+        $element_group = '<tr>' . _theme_table_cell($cell, TRUE) . '</tr>';
+      }
+    }
+    $element_group .= drupal_render($element[$key]);
+  }
+
+  // Append the last group to the group collection.
+  $element_groups[] = '<tbody>' . $element_group . '</tbody>';
+
+  // Display the label of all element groups inside a table header.
+  if (!empty($parent_label)) {
+    $cell = array(
+      'data' => $parent_label,
+      'colspan' => 5,
+    );
+    $result = '<thead><tr>' . _theme_table_cell($cell, TRUE) . '</tr></thead>' . implode('', $element_groups);
+  }
+  return '<div id="tmgmt-status-messages-' . strtolower($parent_label) . '"></div><table class="tmgmt-ui-review"><colgroup width="100" /><colgroup width="*" span="2" /><colgroup width="100" />' . $result . '</table>';
+}
diff --git a/ui/tmgmt_ui.module b/ui/tmgmt_ui.module
index b8e0a70..148f04a 100644
--- a/ui/tmgmt_ui.module
+++ b/ui/tmgmt_ui.module
@@ -93,101 +93,24 @@ function tmgmt_ui_theme() {
     ),
     'tmgmt_ui_translator_review_form' => array(
       'render element' => 'form',
+      'file' => 'includes/tmgmt_ui.theme.inc',
+    ),
+    'tmgmt_ui_translator_review_form' => array(
+      'render element' => 'element',
+      'file' => 'includes/tmgmt_ui.theme.inc',
     ),
     'tmgmt_ui_translator_review_form_element' => array(
       'render element' => 'element',
+      'file' => 'includes/tmgmt_ui.theme.inc',
+    ),
+    'tmgmt_ui_translator_review_form_element_status' => array(
+      'render element' => 'status',
+      'file' => 'includes/tmgmt_ui.theme.inc',
     ),
   );
 }
 
 /**
- * Returns HTML for a group of review items.
- *
- * @param $variables
- *   An associative array containing several review items.
- * @return
- *   HTML table body fragment.
- */
-function theme_tmgmt_ui_translator_review_form_element($variables) {
-  $element = $variables['element'];
-  $result = '';
-  $labels = '';
-  $parent_label = '';
-  $element_groups = array();
-  $element_group = '';
-  $flip = array(
-    'even' => 'odd',
-    'odd' => 'even',
-  );
-  $class = 'even';
-  foreach (element_children($element) as $key) {
-    // Label of all element groups.
-    $parent_label = array_shift($element[$key]['#parent_label']);
-    // Label of the current data item.
-    $leaf_label = array_pop($element[$key]['#parent_label']);
-    // Do not repeat labels inside the same hierarchy.
-    if ($parent_label == $leaf_label) {
-      $leaf_label = '';
-    }
-    // Start a new element group.
-    if ($labels != $element[$key]['#parent_label']) {
-      $labels = $element[$key]['#parent_label'];
-      if (!empty($labels)) {
-        // Append to previous group to the group collection.
-        if (!empty($element_group)) {
-          $element_groups[] = '<tbody>' . $element_group . '</tbody>';
-        }
-        // Header row for the current element group.
-        $cell = array(
-          // @todo: Deal with right to left languages.
-          'data' => check_plain(implode(t(' > '), $labels)),
-          'colspan' => 4,
-        );
-        $element_group = '<tr>' . _theme_table_cell($cell, TRUE) . '</tr>';
-      }
-    }
-    $class = $flip[$class];
-    $element_group .= ' <tr' . drupal_attributes(array('class' => $class)) . '>';
-    $row = array();
-    $leaf_label = '<label>' . $leaf_label . '</label>';
-    $leaf_label = '<div class="form-item form-type-label">' . $leaf_label . '</div>';
-    $row[] = array(
-      'data' =>  $leaf_label . drupal_render($element[$key]['status']),
-      'class' => 'tmgmt-ui-data-item-label',
-    );
-    $row[] = array(
-      'data' => drupal_render($element[$key]['source']),
-      'class' => 'tmgmt-ui-data-item-source',
-    );
-    $row[] = array(
-      'data' => drupal_render($element[$key]['translation']),
-      'class' => 'tmgmt-ui-data-item-translation',
-    );
-    $row[] = array(
-      'data' => drupal_render($element[$key]['actions']),
-      'class' => 'tmgmt-ui-data-item-label',
-    );
-    foreach ($row as $cell) {
-      $element_group .= _theme_table_cell($cell);
-    }
-    $element_group .= '</tr>';
-  }
-
-  // Append the last group to the group collection.
-  $element_groups[] = '<tbody>' . $element_group . '</tbody>';
-
-  // Display the label of all element groups inside a table header.
-  if (!empty($parent_label)) {
-    $cell = array(
-      'data' => $parent_label,
-      'colspan' => 5,
-    );
-    $result = '<thead><tr>' . _theme_table_cell($cell, TRUE) . '</tr></thead>' . implode('', $element_groups);
-  }
-  return '<table class="tmgmt-ui-review"><colgroup width="100" /><colgroup width="*" span="2" /><colgroup width="100" />' . $result . '</table>';
-}
-
-/**
  * Implements hook_forms().
  */
 function tmgmt_ui_forms() {
@@ -407,7 +330,19 @@ function tmgmt_ui_job_needs_checkout_form(TMGMTJob $job) {
 function tmgmt_ui_job_request_translation(TMGMTJob $job) {
   // Process the translation request.
   $job->requestTranslation();
-  // Print all messages that occurred since our request to the screen.
+  return tmgmt_ui_write_request_messages($job);
+}
+
+/**
+ * Print all messages that occurred since our request to the screen.
+ *
+ * @param $job
+ *    The translation job for which the message should be written.
+ *
+ * @return
+ *   FALSE if there are message with severity error, TRUE otherwise.
+ */
+function tmgmt_ui_write_request_messages(TMGMTJob $job) {
   $errors = FALSE;
   foreach ($job->getMessagesSince() as $message) {
     // Ignore debug messages.
@@ -441,8 +376,9 @@ function tmgmt_ui_translation_review_form_defaults($form, &$form_state, TMGMTJob
   $data = $item->getData();
   // Need to keep the first hierarchy. So flatten must take place inside
   // of the foreach loop.
+  $zebra = 'even';
   foreach (element_children($data) as $key) {
-    $form['review'][$key] = _tmgmt_ui_review_form_element(tmgmt_flatten_data($data[$key], $key));
+    $form['review'][$key] = _tmgmt_ui_review_form_element(tmgmt_flatten_data($data[$key], $key), $item, $zebra);
   }
 
   // Add the form actions as well.
@@ -475,19 +411,63 @@ function tmgmt_ui_translation_review_form_defaults($form, &$form_state, TMGMTJob
  *
  * @param $data
  *   Flatened array of translation data items.
+ * @param $job_item
+ *   The job item related to this data.
+ * @param $zebra
+ *   String containing either odd or even. This is used to style the each
+ *   translation item with alternating colors.
  */
-function _tmgmt_ui_review_form_element($data) {
+function _tmgmt_ui_review_form_element($data, TMGMTJobItem $job_item, &$zebra) {
+  $flip = array(
+    'even' => 'odd',
+    'odd' => 'even',
+  );
   $form = array(
-    '#theme' => 'tmgmt_ui_translator_review_form_element',
+    '#theme' => 'tmgmt_ui_translator_review_form',
   );
   foreach (element_children($data) as $key) {
     // The char sequence '][' confuses the form API so we need to replace it.
     $target_key = str_replace('][', '|', $key);
     if (isset($data[$key]['#text']) && _tmgmt_filter_data($data[$key])) {
+      $zebra = $flip[$zebra];
       $form[$target_key] = array(
         '#tree' => TRUE,
-        '#parent_label' => $data[$key]['#parent_label']
+        '#theme' => 'tmgmt_ui_translator_review_form_element',
+        '#parent_label' => $data[$key]['#parent_label'],
+        '#ajaxid' => drupal_html_id('tmgmt-ui-element-' . $key),
+        '#zebra' => $zebra,
       );
+      $form[$target_key]['status'] = array(
+        '#theme' => 'tmgmt_ui_translator_review_form_element_status',
+        '#value' => $data[$key]['#status'],
+      );
+      $form[$target_key]['actions'] = array(
+        '#type' => 'container',
+      );
+      if ($data[$key]['#status'] != TMGMT_DATA_ITEM_STATE_APPROVED) {
+      $form[$target_key]['actions']['approve'] = array(
+        '#type' => 'submit',
+        '#value' => t('Approve'),
+        '#name' => 'approve-' . $target_key,
+        '#submit' => array('tmgmt_ui_translation_review_form_update_state', 'tmgmt_ui_translation_review_form_submit'),
+        '#ajax' => array(
+          'callback' => 'tmgmt_ui_translation_review_form_ajax',
+          'wrapper' => $form[$target_key]['#ajaxid'],
+        ),
+      );
+      }
+      if ($job_item->getTranslatorController() instanceof TMGMTTranslatorRejectDataItem && $data[$key]['#status'] != TMGMT_DATA_ITEM_STATE_PENDING) {
+        $form[$target_key]['actions']['reject'] = array(
+          '#type' => 'submit',
+          '#value' => t('Reject'),
+          '#name' => 'reject-' . $target_key,
+          '#submit' => array('tmgmt_ui_translation_review_form_update_state', 'tmgmt_ui_translation_review_form_submit'),
+          '#ajax' => array(
+            'callback' => 'tmgmt_ui_translation_review_form_ajax',
+            'wrapper' => $form[$target_key]['#ajaxid'],
+          ),
+        );
+      }
       $form[$target_key]['translation'] = array(
         '#type' => 'textarea',
         '#default_value' => $data[$key]['#translation']['#text'],
@@ -544,17 +524,14 @@ function tmgmt_ui_translation_review_form_submit($form, &$form_state) {
   $translator = tmgmt_translator_ui_controller($item->getTranslator()->plugin);
   $translator->reviewFormSubmit($form, $form_state, $item);
   // Write changes back to item.
-  $values = $form_state['values'];
-  $data = array();
-  foreach ($values as $key => $value) {
+  foreach ($form_state['values'] as $key => $value) {
     if (is_array($value) && isset($value['translation'])) {
-      // For the char sequence '][' confuses the forms API, it was replaced by
-      // '|' in tmgmt_ui_translation_review_form_defaults(). Now we need to
-      // replace it back.
-      $data[str_replace('|', '][', $key)] = array( '#text' => $value['translation']);
+      $data['#translation'] = array(
+        '#text' => $value['translation'],
+      );
+      $item->updateData($key, $data);
     }
   }
-  $item->addTranslatedData($data);
   // Check if the user clicked on 'Accept', 'Submit' or 'Reject'.
   if ($form_state['triggering_element']['#value'] == $form['actions']['accept']['#value']) {
     $item->acceptTranslation();
@@ -580,6 +557,62 @@ function tmgmt_ui_translation_review_form_submit($form, &$form_state) {
 }
 
 /**
+ * Callback for the action at the job item review form.
+ */
+function tmgmt_ui_translation_review_form_update_state($form, &$form_state) {
+  $form_state['rebuild'] = TRUE;
+  $matches = array();
+  // We should have an #name element
+  // and the name should beginn with approve-
+  // and the $matches should now kontain an element with with name key.
+  preg_match("/^(?P<action>[^-]+)-(?P<key>.+)/i", $form_state['triggering_element']['#name'], $matches);
+  $values = $form_state['values'];
+  $data = array();
+  $item = $form_state['item'];
+  foreach ($values as $key => $value) {
+    if ($key == $matches['key']) {
+      if (key_exists('translation', $value)) {
+        $data['#tranlsation'] = array(
+          '#text' => $value['translation'],
+        );
+      }
+      $controller = $item->getTranslatorController();
+      $success = TRUE;
+      switch ($matches['action']) {
+        case 'approve':
+          $data['#status'] = TMGMT_DATA_ITEM_STATE_APPROVED;
+          $success = $controller->approveDataItem($item, tmgmt_ensure_keys_array($key));
+          break;
+        case 'reject':
+          if ($controller instanceof TMGMTTranslatorRejectDataItem) {
+            $success = $item->getTranslatorController()->rejectDataItem($item, tmgmt_ensure_keys_array($key));
+          }
+        default:
+          $data['#status'] = TMGMT_DATA_ITEM_STATE_PENDING;
+          break;
+      }
+      if ($success) {
+        $item->updateData($key, $data);
+      }
+      break;
+    }
+  }
+}
+
+/**
+ * Ajax callback for the job item review form.
+ */
+function tmgmt_ui_translation_review_form_ajax($form, &$form_state) {
+  $key = array_slice($form_state['triggering_element']['#array_parents'], 0, 3);
+  $commands = array();
+  $render_data = drupal_array_get_nested_value($form, $key);
+  $commands[] = ajax_command_replace(NULL, drupal_render($render_data));
+  tmgmt_ui_write_request_messages($form_state['item']->getJob());
+  $commands[] = ajax_command_html('#tmgmt-status-messages-' . strtolower($render_data['#parent_label'][0]), theme('status_messages'));
+  return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
  * @addtogroup tmgmt_ui_redirect_queue
  * @{
  */
