diff --git a/core/modules/image/image.field.inc b/core/modules/image/image.field.inc
index ceb2c8a..11152ae 100644
--- a/core/modules/image/image.field.inc
+++ b/core/modules/image/image.field.inc
@@ -19,6 +19,20 @@ function image_field_info() {
       'settings' => array(
         'uri_scheme' => file_default_scheme(),
         'default_image' => 0,
+        'column_groups' => array(
+          'file' => array(
+            'label' => t('File'),
+            'columns' => array('fid', 'width', 'height'),
+          ),
+          'alt' => array(
+            'label' => t('Alt'),
+            'translatable' => TRUE,
+          ),
+          'title' => array(
+            'label' => t('Title'),
+            'translatable' => TRUE,
+          ),
+        ),
       ),
       'instance_settings' => array(
         'file_extensions' => 'png gif jpg jpeg',
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index bdba951..a3500fd 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -1021,6 +1021,7 @@ function language_content_settings_form(array $form, array $form_state, array $s
     $form['settings'][$entity_type] = array(
       '#title' => $labels[$entity_type],
       '#type' => 'container',
+      '#entity_type' => $entity_type,
       '#theme' => 'language_content_settings_table',
       '#bundle_label' => isset($info['bundle_label']) ? $info['bundle_label'] : $labels[$entity_type],
       '#states' => array(
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 eaecec9..ce9d3a0 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
@@ -430,6 +430,13 @@ public function entityFormEntityBuild($entity_type, EntityInterface $entity, arr
     if (!empty($values['retranslate'])) {
       $this->retranslate($entity, $form_langcode);
     }
+
+    // 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);
+    $attributes->set('source_langcode', $source_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
new file mode 100644
index 0000000..2463569
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizer.php
@@ -0,0 +1,191 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\translation_entity\FieldTranslationSynchronizer.
+ */
+
+namespace Drupal\translation_entity;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides field translation synchronization capabilities.
+ */
+class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterface {
+
+  /**
+   * Implements \Drupal\translation_entity\FieldTranslationSynchronizerInterface::synchronizeFields().
+   */
+  public function synchronizeFields(EntityInterface $entity, $sync_langcode, $original_langcode = NULL) {
+    $translations = $entity->getTranslationLanguages();
+
+    // If we have no information about what to sync to, if we are creating a new
+    // entity, if we have no translations for the current entity and we are not
+    // creating one, then there is nothing to synchronize.
+    if (empty($sync_langcode) || $entity->isNew() || (count($translations) < 2 && !$original_langcode)) {
+      return;
+    }
+
+    // If the entity language is being changed there is nothing to synchronize.
+    $entity_type = $entity->entityType();
+    // @todo Use the entity storage controller directly to avoid accessing the
+    //   global scope.
+    $entity_unchanged = isset($entity->original) ? $entity->original : entity_load_unchanged($entity_type, $entity->id());
+    if ($entity->language()->langcode != $entity_unchanged->language()->langcode) {
+      return;
+    }
+
+    // Enable compatibility mode for NG entities.
+    $entity_unchanged = $entity_unchanged->getBCEntity();
+
+    // @todo Use Entity Field API to retrieve field definitions.
+    $instances = field_info_instances($entity_type, $entity->bundle());
+    foreach ($instances as $field_name => $instance) {
+      $field = field_info_field($field_name);
+
+      // Sync when the field is not empty, when the synchronization translations
+      // setting is set, and the field is translatable.
+      if (!empty($entity->{$field_name}) && !empty($instance['settings']['translation_sync']) && field_is_translatable($entity_type, $field)) {
+        // Retrieve all the untranslatable column groups and merge them into
+        // single list.
+        $groups = array_keys(array_diff($instance['settings']['translation_sync'], array_filter($instance['settings']['translation_sync'])));
+        if (!empty($groups)) {
+          $columns = array();
+          foreach ($groups as $group) {
+            $info = $field['settings']['column_groups'][$group];
+            // A missing 'columns' key indicates we have a single-column group.
+            $columns = array_merge($columns, isset($info['columns']) ? $info['columns'] : array($group));
+          }
+          if (!empty($columns)) {
+            // If a translation is being created, the original values should be
+            // used as the unchanged items. In fact there are no unchanged items
+            // to check against.
+            $langcode = $original_langcode ?: $sync_langcode;
+            $unchanged_items = !empty($entity_unchanged->{$field_name}[$langcode]) ? $entity_unchanged->{$field_name}[$langcode] : array();
+            $this->synchronizeItems($entity->{$field_name}, $unchanged_items, $sync_langcode, array_keys($translations), $columns);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Implements \Drupal\translation_entity\FieldTranslationSynchronizerInterface::synchronizeItems().
+   */
+  public function synchronizeItems(array &$field_values, array $unchanged_items, $sync_langcode, array $translations, array $columns) {
+    $source_items = $field_values[$sync_langcode];
+
+    // Make sure we can detect any change in the source items.
+    $change_map = array();
+
+    // As a first step we build a map of the deltas corresponding to the column
+    // values to be synchronized. Recording both the old values and the new
+    // values will allow us to detect any change in the order of the new items
+    // for each column.
+    foreach (array('old' => $unchanged_items, 'new' => $source_items) as $key => $items) {
+      foreach ($items as $delta => $item) {
+        if ($item_id = $this->itemHash($items, $delta, $columns)) {
+          $change_map[$item_id][$key][] = $delta;
+        }
+      }
+    }
+
+    // Backup field values and the change map.
+    $original_field_values = $field_values;
+    $original_change_map = $change_map;
+
+    // Reset field values so that no spurious one is stored. Source values must
+    // be preserved in any case.
+    $field_values = array($sync_langcode => $source_items);
+
+    // By picking the maximum size between updated and unchanged items, we make
+    // sure to process also removed items.
+    $total = max(array(count($source_items), count($unchanged_items)));
+
+    // Update field translations.
+    foreach ($translations as $langcode) {
+      // We need to synchronize only values different from the source ones.
+      if ($langcode != $sync_langcode) {
+        // Reinitialize the change map as it is emptied while processing each
+        // language.
+        $change_map = $original_change_map;
+
+        // By using the maximum cardinality we ensure to process removed items.
+        for ($delta = 0; $delta < $total; $delta++) {
+          // By inspecting the map we built before we can tell whether a value
+          // has been created or removed. A changed value will be interpreted as
+          // a new value, in fact it did not exist before.
+          $old_delta = NULL;
+          $new_delta = NULL;
+
+          // If the item hash is empty we have no value for the current delta
+          // in the source items, hence the value must have been removed.
+          if ($item_id = $this->itemHash($source_items, $delta, $columns)) {
+            if (!empty($change_map[$item_id]['old'])) {
+              $old_delta = array_shift($change_map[$item_id]['old']);
+            }
+            if (!empty($change_map[$item_id]['new'])) {
+              $new_delta = array_shift($change_map[$item_id]['new']);
+            }
+          }
+
+          // If an item has been removed we do not store its translations.
+          if (!isset($new_delta)) {
+            continue;
+          }
+          // If a synchronized column has changed or has been created from
+          // scratch we need to override the full items array for all languages.
+          elseif (!isset($old_delta)) {
+            $field_values[$langcode][$delta] = $source_items[$delta];
+          }
+          // Otherwise the current item might have been reordered.
+          else {
+            // If for any reason the old value is not defined for the current
+            // language we fall back to the new source value, this way we ensure
+            // the new values are at least propagated to all the translations.
+            // If the value has only been reordered we just move the old one in
+            // the new position.
+            $item = isset($original_field_values[$langcode][$old_delta]) ? $original_field_values[$langcode][$old_delta] : $source_items[$new_delta];
+            $field_values[$langcode][$new_delta] = $item;
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Computes a hash code for the specified item.
+   *
+   * @param array $items
+   *   An array of field items.
+   * @param int $delta
+   *   The delta identifying the item to be processed.
+   * @param array $columns
+   *   An array of column names to be synchronized.
+   *
+   * @returns string
+   *   A hash code that can be used to identify the item.
+   */
+  protected function itemHash(array $items, $delta, array $columns) {
+    $values = array();
+
+    if (isset($items[$delta])) {
+      foreach ($columns as $column) {
+        if (!empty($items[$delta][$column])) {
+          $value = $items[$delta][$column];
+          // String and integer values are by far the most common item values,
+          // thus we special-case them to improve performance.
+          $values[] = is_string($value) || is_int($value) ? $value : hash('sha256', serialize($value));
+        }
+        else {
+          // Explicitly track also empty values.
+          $values[] = '';
+        }
+      }
+    }
+
+    return implode('.', $values);
+  }
+
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizerInterface.php b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizerInterface.php
new file mode 100644
index 0000000..1ae361c
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/FieldTranslationSynchronizerInterface.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\translation_entity\FieldTranslationSynchronizerInterface.
+ */
+
+namespace Drupal\translation_entity;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides field translation synchronization capabilities.
+ */
+interface FieldTranslationSynchronizerInterface {
+
+  /**
+   * Performs field column synchronization on the given entity.
+   *
+   * Field column synchronization takes care of propagating any change in the
+   * field items order and in the column values themselves to all the available
+   * translations. This functionality is provided by defining a
+   * 'translation_sync' key in the field instance settings, holding an array of
+   * column names to be synchronized. The synchronized column values are shared
+   * across translations, while the rest varies per-language. This is useful for
+   * instance to translate the "alt" and "title" textual elements of an image
+   * field, while keeping the same image on every translation.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity whose values should be synchronized.
+   * @param string $sync_langcode
+   *   The language of the translation whose values should be used as source for
+   *   synchronization.
+   * @param string $original_langcode
+   *   (optional) If a new translation is being created, this should be the
+   *   language code of the original values. Defaults to NULL.
+   */
+  public function synchronizeFields(EntityInterface $entity, $sync_langcode, $original_langcode = NULL);
+
+  /**
+   * Synchronize the items of a single field.
+   *
+   * All the column values of the "active" language are compared to the
+   * unchanged values to detect any addition, removal or change in the items
+   * order. Subsequently the detected changes are performed on the field items
+   * in other available languages.
+   *
+   * @param array $field_values
+   *   The field values to be synchronized.
+   * @param array $unchanged_items
+   *   The unchanged items to be used to detect changes.
+   * @param string $sync_langcode
+   *   The language code of the items to use as source values.
+   * @param array $translations
+   *   An array of all the available language codes for the given field.
+   * @param array $columns
+   *   An array of column names to be synchronized.
+   */
+  public function synchronizeItems(array &$field_values, array $unchanged_items, $sync_langcode, array $translations, array $columns);
+
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncImageTest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncImageTest.php
new file mode 100644
index 0000000..1e1abd1
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncImageTest.php
@@ -0,0 +1,229 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Tests\EntityTranslationSyncImageTest.
+ */
+
+namespace Drupal\translation_entity\Tests;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
+
+/**
+ * Tests the Entity Translation image field synchronization capability.
+ */
+class EntityTranslationSyncImageTest extends EntityTranslationTestBase {
+
+  /**
+   * The cardinality of the image field.
+   *
+   * @var int
+   */
+  protected $cardinality;
+
+  /**
+   * The test image files.
+   *
+   * @var array
+   */
+  protected $files;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('language', 'translation_entity', 'entity_test', 'image');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Image field synchronization',
+      'description' => 'Tests the field synchronization behavior for the image field.',
+      'group' => 'Entity Translation UI',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp();
+    $this->files = $this->drupalGetTestFiles('image');
+  }
+
+  /**
+   * Creates the test image field.
+   */
+  protected function setupTestFields() {
+    $this->fieldName = 'field_test_et_ui_image';
+    $this->cardinality = 3;
+
+    $field = array(
+      'field_name' => $this->fieldName,
+      'type' => 'image',
+      'cardinality' => $this->cardinality,
+      'translatable' => TRUE,
+    );
+    field_create_field($field);
+
+    $instance = array(
+      'entity_type' => $this->entityType,
+      'field_name' => $this->fieldName,
+      'bundle' => $this->entityType,
+      'label' => 'Test translatable image field',
+      'widget' => array(
+        'type' => 'image_image',
+        'weight' => 0,
+      ),
+      'settings' => array(
+        'translation_sync' => array(
+          'file' => FALSE,
+          'alt' => 'alt',
+          'title' => 'title',
+        ),
+      ),
+    );
+    field_create_instance($instance);
+  }
+
+  /**
+   * Tests image field field synchronization.
+   */
+  function testImageFieldSync() {
+    $default_langcode = $this->langcodes[0];
+    $langcode = $this->langcodes[1];
+
+    // Populate the required contextual values.
+    $attributes = drupal_container()->get('request')->attributes;
+    $attributes->set('working_langcode', $langcode);
+    $attributes->set('source_langcode', $default_langcode);
+
+    // Populate the test entity with some random initial values.
+    $values = array(
+      'name' => $this->randomName(),
+      'user_id' => mt_rand(1, 128),
+      'langcode' => $default_langcode,
+    );
+    $entity = entity_create($this->entityType, $values)->getBCEntity();
+
+    // Create some file entities from the generated test files and store them.
+    $values = array();
+    for ($delta = 0; $delta < $this->cardinality; $delta++) {
+      // For the default language use the same order for files and field items.
+      $index = $delta;
+
+      // Create the file entity for the image being processed and record its
+      // identifier.
+      $field_values = array(
+        'uri' => $this->files[$index]->uri,
+        'uid' => $GLOBALS['user']->uid,
+        'status' => FILE_STATUS_PERMANENT,
+      );
+      $file = entity_create('file', $field_values);
+      $file->save();
+      $fid = $file->id();
+      $this->files[$index]->fid = $fid;
+
+      // Generate the item for the current image file entity and attach it to
+      // the entity.
+      $item = array(
+        'fid' => $fid,
+        'alt' => $this->randomName(),
+        'title' => $this->randomName(),
+      );
+      $entity->{$this->fieldName}[$default_langcode][$delta] = $item;
+
+      // Store the generated values keying them by fid for easier lookup.
+      $values[$default_langcode][$fid] = $item;
+    }
+    $entity = $this->saveEntity($entity);
+
+    // Create some field translations for the test image field. The translated
+    // items will be one less than the original values to check that only the
+    // translated ones will be preserved. In fact we want the same fids and
+    // items order for both languages.
+    for ($delta = 0; $delta < $this->cardinality - 1; $delta++) {
+      // Simulate a field reordering: items are shifted one position ahead.
+      // The modulo operator ensures we start from the beginning after reaching
+      // the maximum allowed delta.
+      $index = ($delta + 1) % $this->cardinality;
+
+      // Generate the item for the current image file entity and attach it to
+      // the entity.
+      $fid = $this->files[$index]->fid;
+      $item = array(
+        'fid' => $fid,
+        'alt' => $this->randomName(),
+        'title' => $this->randomName(),
+      );
+      $entity->{$this->fieldName}[$langcode][$delta] = $item;
+
+      // Again store the generated values keying them by fid for easier lookup.
+      $values[$langcode][$fid] = $item;
+    }
+
+    // Perform synchronization: the translation language is used as source,
+    // while the default langauge is used as target.
+    $entity = $this->saveEntity($entity);
+
+    // Check that one value has been dropped from the original values.
+    $assert = count($entity->{$this->fieldName}[$default_langcode]) == 2;
+    $this->assertTrue($assert, 'One item correctly removed from the synchronized field values.');
+
+    // Check that fids have been synchronized and translatable column values
+    // have been retained.
+    $fids = array();
+    foreach ($entity->{$this->fieldName}[$default_langcode] as $delta => $item) {
+      $value = $values[$default_langcode][$item['fid']];
+      $source_item = $entity->{$this->fieldName}[$langcode][$delta];
+      $assert = $item['fid'] == $source_item['fid'] && $item['alt'] == $value['alt'] && $item['title'] == $value['title'];
+      $this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', array('@fid' => $item['fid'])));
+      $fids[$item['fid']] = TRUE;
+    }
+
+    // Check that the dropped value is the right one.
+    $removed_fid = $this->files[0]->fid;
+    $this->assertTrue(!isset($fids[$removed_fid]), format_string('Field item @fid has been correctly removed.', array('@fid' => $removed_fid)));
+
+    // Add back an item for the dropped value and perform synchronization again.
+    // @todo Actually we would need to reset the contextual information to test
+    //   an update, but there is no entity field class for image fields yet,
+    //   hence field translation update does not work properly for those.
+    $values[$langcode][$removed_fid] = array(
+      'fid' => $removed_fid,
+      'alt' => $this->randomName(),
+      'title' => $this->randomName(),
+    );
+    $entity->{$this->fieldName}[$langcode] = array_values($values[$langcode]);
+    $entity = $this->saveEntity($entity);
+
+    // Check that the value has been added to the default language.
+    $assert = count($entity->{$this->fieldName}[$default_langcode]) == 3;
+    $this->assertTrue($assert, 'One item correctly added to the synchronized field values.');
+
+    foreach ($entity->{$this->fieldName}[$default_langcode] as $delta => $item) {
+      // When adding an item its value is copied over all the target languages,
+      // thus in this case the source language needs to be used to check the
+      // values instead of the target one.
+      $fid_langcode = $item['fid'] != $removed_fid ? $default_langcode : $langcode;
+      $value = $values[$fid_langcode][$item['fid']];
+      $source_item = $entity->{$this->fieldName}[$langcode][$delta];
+      $assert = $item['fid'] == $source_item['fid'] && $item['alt'] == $value['alt'] && $item['title'] == $value['title'];
+      $this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', array('@fid' => $item['fid'])));
+    }
+  }
+
+  /**
+   * Saves the passed entity and reloads it, enabling compatibility mode.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to be saved.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The saved entity.
+   */
+  protected function saveEntity(EntityInterface $entity) {
+    $entity->save();
+    $entity = entity_test_mul_load($entity->id(), TRUE);
+    return $entity->getBCEntity();
+  }
+
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.php
new file mode 100644
index 0000000..63246d9
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationSyncUnitTest.php
@@ -0,0 +1,262 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Tests\EntityTranslationSyncUnitTest.
+ */
+
+namespace Drupal\translation_entity\Tests;
+
+use Drupal\Core\Language\Language;
+use Drupal\simpletest\DrupalUnitTestBase;
+use Drupal\translation_entity\FieldTranslationSynchronizer;
+
+/**
+ * Tests the Entity Translation field synchronization algorithm.
+ */
+class EntityTranslationSyncUnitTest extends DrupalUnitTestBase {
+
+  /**
+   * The synchronizer class to be tested.
+   *
+   * @var \Drupal\translation_entity\FieldTranslationSynchronizer
+   */
+  protected $synchronizer;
+
+  /**
+   * The colums to be synchronized.
+   *
+   * @var array
+   */
+  protected $synchronized;
+
+  /**
+   * All the field colums.
+   *
+   * @var array
+   */
+  protected $columns;
+
+  /**
+   * The available language codes.
+   *
+   * @var array
+   */
+  protected $langcodes;
+
+  /**
+   * The field cardinality.
+   *
+   * @var integer
+   */
+  protected $cardinality;
+
+  /**
+   * The unchanged field values.
+   *
+   * @var array
+   */
+  protected $unchangedFieldValues;
+
+  public static $modules = array('language', 'translation_entity');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Field synchronization',
+      'description' => 'Tests the field synchronization logic.',
+      'group' => 'Entity Translation UI',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->synchronizer = new FieldTranslationSynchronizer();
+    $this->synchronized = array('sync1', 'sync2');
+    $this->columns = array_merge($this->synchronized, array('var1', 'var2'));
+    $this->langcodes = array('en', 'it', 'fr', 'de', 'es');
+    $this->cardinality = 4;
+    $this->unchangedFieldValues = array();
+
+    // Set up an initial set of values in the correct state, that is with
+    // "synchronized" values being equal.
+    foreach ($this->langcodes as $langcode) {
+      for ($delta = 0; $delta < $this->cardinality; $delta++) {
+        foreach ($this->columns as $column) {
+          $sync = in_array($column, $this->synchronized) && $langcode != $this->langcodes[0];
+          $value = $sync ? $this->unchangedFieldValues[$this->langcodes[0]][$delta][$column] : $langcode . '-' . $delta . '-' . $column;
+          $this->unchangedFieldValues[$langcode][$delta][$column] = $value;
+        }
+      }
+    }
+  }
+
+  /**
+   * Tests the field synchronization algorithm.
+   */
+  public function testFieldSync() {
+    // Add a new item to the source items and check that it is added to all the
+    // translations.
+    $sync_langcode = $this->langcodes[2];
+    $unchanged_items = $this->unchangedFieldValues[$sync_langcode];
+    $field_values = $this->unchangedFieldValues;
+    $item = array();
+    foreach ($this->columns as $column) {
+      $item[$column] = $this->randomName();
+    }
+    $field_values[$sync_langcode][] = $item;
+    $this->synchronizer->synchronizeItems($field_values, $unchanged_items, $sync_langcode, $this->langcodes, $this->synchronized);
+    $result = TRUE;
+    foreach ($this->unchangedFieldValues as $langcode => $items) {
+      // Check that the old values are still in place.
+      for ($delta = 0; $delta < $this->cardinality; $delta++) {
+        foreach ($this->columns as $column) {
+          $result = $result && ($this->unchangedFieldValues[$langcode][$delta][$column] == $field_values[$langcode][$delta][$column]);
+        }
+      }
+      // Check that the new item is available in all languages.
+      foreach ($this->columns as $column) {
+        $result = $result && ($field_values[$langcode][$delta][$column] == $field_values[$sync_langcode][$delta][$column]);
+      }
+    }
+    $this->assertTrue($result, 'A new item has been correctly synchronized.');
+
+    // Remove an item from the source items and check that it is removed from
+    // all the translations.
+    $sync_langcode = $this->langcodes[1];
+    $unchanged_items = $this->unchangedFieldValues[$sync_langcode];
+    $field_values = $this->unchangedFieldValues;
+    $sync_delta = mt_rand(0, count($field_values[$sync_langcode]) - 1);
+    unset($field_values[$sync_langcode][$sync_delta]);
+    // Renumber deltas to start from 0.
+    $field_values[$sync_langcode] = array_values($field_values[$sync_langcode]);
+    $this->synchronizer->synchronizeItems($field_values, $unchanged_items, $sync_langcode, $this->langcodes, $this->synchronized);
+    $result = TRUE;
+    foreach ($this->unchangedFieldValues as $langcode => $items) {
+      $new_delta = 0;
+      // Check that the old values are still in place.
+      for ($delta = 0; $delta < $this->cardinality; $delta++) {
+        // Skip the removed item.
+        if ($delta != $sync_delta) {
+          foreach ($this->columns as $column) {
+            $result = $result && ($this->unchangedFieldValues[$langcode][$delta][$column] == $field_values[$langcode][$new_delta][$column]);
+          }
+          $new_delta++;
+        }
+      }
+    }
+    $this->assertTrue($result, 'A removed item has been correctly synchronized.');
+
+    // Move the items around in the source items and check that they are moved
+    // in all the translations.
+    $sync_langcode = $this->langcodes[3];
+    $unchanged_items = $this->unchangedFieldValues[$sync_langcode];
+    $field_values = $this->unchangedFieldValues;
+    $field_values[$sync_langcode] = array();
+    // Scramble the items.
+    foreach ($unchanged_items as $delta => $item) {
+      $new_delta = ($delta + 1) % $this->cardinality;
+      $field_values[$sync_langcode][$new_delta] = $item;
+    }
+    // Renumber deltas to start from 0.
+    ksort($field_values[$sync_langcode]);
+    $this->synchronizer->synchronizeItems($field_values, $unchanged_items, $sync_langcode, $this->langcodes, $this->synchronized);
+    $result = TRUE;
+    foreach ($field_values as $langcode => $items) {
+      for ($delta = 0; $delta < $this->cardinality; $delta++) {
+        foreach ($this->columns as $column) {
+          $value = $field_values[$langcode][$delta][$column];
+          if (in_array($column, $this->synchronized)) {
+            // If we are dealing with a synchronize column the current value is
+            // supposed to be the same of the source items.
+            $result = $result && $field_values[$sync_langcode][$delta][$column] == $value;
+          }
+          else {
+            // Otherwise the values should be unchanged.
+            $old_delta = ($delta > 0 ? $delta : $this->cardinality) - 1;
+            $result = $result && $this->unchangedFieldValues[$langcode][$old_delta][$column] == $value;
+          }
+        }
+      }
+    }
+    $this->assertTrue($result, 'Scrambled items have been correctly synchronized.');
+  }
+
+  /**
+   * Tests that items holding the same values are correctly synchronized.
+   */
+  public function testMultipleSyncedValues() {
+    $sync_langcode = $this->langcodes[1];
+    $unchanged_items = $this->unchangedFieldValues[$sync_langcode];
+
+    // Determine whether the unchanged values should be altered depending on
+    // their delta.
+    $delta_callbacks = array(
+      // Continuous field values: all values are equal.
+      function($delta) { return TRUE; },
+      // Alternated field values: only the even ones are equal.
+      function($delta) { return $delta % 2 !== 0; },
+      // Sparse field values: only the "middle" ones are equal.
+      function($delta) { return $delta === 1 || $delta === 2; },
+      // Sparse field values: only the "extreme" ones are equal.
+      function($delta) { return $delta === 0 || $delta === 3; },
+    );
+
+    foreach ($delta_callbacks as $delta_callback) {
+      $field_values = $this->unchangedFieldValues;
+
+      for ($delta = 0; $delta < $this->cardinality; $delta++) {
+        if ($delta_callback($delta)) {
+          foreach ($this->columns as $column) {
+            $field_values[$sync_langcode][$delta][$column] = $field_values[$sync_langcode][0][$column];
+          }
+        }
+      }
+
+      $changed_items = $field_values[$sync_langcode];
+      $this->synchronizer->synchronizeItems($field_values, $unchanged_items, $sync_langcode, $this->langcodes, $this->synchronized);
+
+      $result = TRUE;
+      foreach ($this->unchangedFieldValues as $langcode => $unchanged_items) {
+        for ($delta = 0; $delta < $this->cardinality; $delta++) {
+          foreach ($this->columns as $column) {
+            // The first item is always unchanged hence it is retained by the
+            // synchronization process. The other ones are retained or synced
+            // depending on the logic implemented by the delta callback.
+            $value = $delta > 0 && $delta_callback($delta) ? $changed_items[0][$column] : $unchanged_items[$delta][$column];
+            $result = $result && ($field_values[$langcode][$delta][$column] == $value);
+          }
+        }
+      }
+      $this->assertTrue($result, 'Multiple synced items have been correctly synchronized.');
+    }
+  }
+
+  /**
+   * Tests that one change in a synchronized column triggers a change in all columns.
+   */
+  public function testDifferingSyncedColumns() {
+    $sync_langcode = $this->langcodes[2];
+    $unchanged_items = $this->unchangedFieldValues[$sync_langcode];
+    $field_values = $this->unchangedFieldValues;
+
+    for ($delta = 0; $delta < $this->cardinality; $delta++) {
+      $index = ($delta % 2) + 1;
+      $field_values[$sync_langcode][$delta]['sync' . $index] .= '-updated';
+    }
+
+    $changed_items = $field_values[$sync_langcode];
+    $this->synchronizer->synchronizeItems($field_values, $unchanged_items, $sync_langcode, $this->langcodes, $this->synchronized);
+
+    $result = TRUE;
+    foreach ($this->unchangedFieldValues as $langcode => $unchanged_items) {
+      for ($delta = 0; $delta < $this->cardinality; $delta++) {
+        foreach ($this->columns as $column) {
+          $result = $result && ($field_values[$langcode][$delta][$column] == $changed_items[$delta][$column]);
+        }
+      }
+    }
+    $this->assertTrue($result, 'Differing synced columns have been correctly synchronized.');
+  }
+
+}
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/TranslationEntityBundle.php b/core/modules/translation_entity/lib/Drupal/translation_entity/TranslationEntityBundle.php
new file mode 100644
index 0000000..617014b
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/TranslationEntityBundle.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\translation_entity\TranslationEntityBundle.
+ */
+
+namespace Drupal\translation_entity;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Registers locale module's services to the container.
+ */
+class TranslationEntityBundle extends Bundle {
+
+  /**
+   * Implements \Symfony\Component\HttpKernel\Bundle\BundleInterface::build().
+   */
+  public function build(ContainerBuilder $container) {
+    $container->register('translation_entity.synchronizer', 'Drupal\translation_entity\FieldTranslationSynchronizer');
+  }
+
+}
diff --git a/core/modules/translation_entity/translation_entity.admin.css b/core/modules/translation_entity/translation_entity.admin.css
index 5f03b78..cab64f8 100644
--- a/core/modules/translation_entity/translation_entity.admin.css
+++ b/core/modules/translation_entity/translation_entity.admin.css
@@ -11,12 +11,18 @@
   font-weight: bold;
 }
 
-#language-content-settings-form table .field {
+#language-content-settings-form table .field,
+#language-content-settings-form table .column {
   width: 24%;
   padding-left: 3em;
 }
 
-#language-content-settings-form table .field label {
+#language-content-settings-form table .column {
+  padding-left: 5em;
+}
+
+#language-content-settings-form table .field label,
+#language-content-settings-form table .column label {
   font-weight: normal;
 }
 
diff --git a/core/modules/translation_entity/translation_entity.admin.inc b/core/modules/translation_entity/translation_entity.admin.inc
index aca0dd0..0e2316b 100644
--- a/core/modules/translation_entity/translation_entity.admin.inc
+++ b/core/modules/translation_entity/translation_entity.admin.inc
@@ -6,11 +6,44 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\FieldInstance;
 
 /**
- * Implements hook_form_FORM_ID_alter() for language_content_settings_form().
+ * Returns a form element to configure field synchronization.
  *
- * @see translation_entity_form_language_content_settings_form_alter()
+ * @param array $field
+ *   A field definition array.
+ * @param \Drupal\Field\FieldInstance $instance
+ *   A field instance definition object.
+ *
+ * @return array
+ *   A form element to configure field synchronization.
+ */
+function translation_entity_field_sync_widget(array $field, FieldInstance $instance) {
+  $element = array();
+
+  if (!empty($field['settings']['column_groups']) && count($field['settings']['column_groups']) > 1) {
+    $options = array();
+    $default = array();
+
+    foreach ($field['settings']['column_groups'] as $group => $info) {
+      $options[$group] = $info['label'];
+      $default[$group] = !empty($info['translatable']) ? $group : FALSE;
+    }
+
+    $element = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Translatable elements'),
+      '#options' => $options,
+      '#default_value' => !empty($instance['settings']['translation_sync']) ? $instance['settings']['translation_sync'] : $default,
+    );
+  }
+
+  return $element;
+}
+
+/**
+ * (proxied) Implements hook_form_FORM_ID_alter().
  */
 function _translation_entity_form_language_content_settings_form_alter(array &$form, array &$form_state) {
   // Inject into the content language settings the translation settings if the
@@ -48,6 +81,10 @@ function _translation_entity_form_language_content_settings_form_alter(array &$f
           '#type' => 'checkbox',
           '#default_value' => $field['translatable'],
         );
+        $column_element = translation_entity_field_sync_widget($field, $instance);
+        if ($column_element) {
+          $form['settings'][$entity_type][$bundle]['columns'][$field_name] = $column_element;
+        }
       }
     }
   }
@@ -57,6 +94,112 @@ function _translation_entity_form_language_content_settings_form_alter(array &$f
 }
 
 /**
+ * (proxied) Implements hook_preprocess_HOOK();
+ */
+function _translation_entity_preprocess_language_content_settings_table(&$variables) {
+  // Alter the 'build' variable injecting the translation settings if the user
+  // has the required permission.
+  if (!user_access('administer entity translation')) {
+    return;
+  }
+
+  $element = $variables['element'];
+  $build = &$variables['build'];
+
+  array_unshift($build['#header'], array('data' => t('Translatable'), 'class' => array('translatable')));
+  $rows = array();
+
+  foreach (element_children($element) as $bundle) {
+    $field_names = !empty($element[$bundle]['fields']) ? element_children($element[$bundle]['fields']) : array();
+    $checkbox_id = $element[$bundle]['translatable']['#id'];
+    $rows[$bundle] = $build['#rows'][$bundle];
+
+    $translatable = array(
+      'data' => $element[$bundle]['translatable'],
+      'class' => array('translatable'),
+    );
+    array_unshift($rows[$bundle]['data'], $translatable);
+
+    $rows[$bundle]['data'][1]['data']['#prefix'] = '<label for="' . $checkbox_id . '">';
+
+    foreach ($field_names as $field_name) {
+      $field_element = &$element[$bundle]['fields'][$field_name];
+      $rows[] = array(
+        'data' => array(
+          array(
+            'data' => drupal_render($field_element),
+            'class' => array('translatable'),
+          ),
+          array(
+            'data' => array(
+              '#prefix' => '<label for="' . $field_element['#id'] . '">',
+              '#suffix' => '</label>',
+              'bundle' => array(
+                '#prefix' => '<span class="element-invisible">',
+                '#suffix' => '</span> ',
+                '#markup' => check_plain($element[$bundle]['settings']['#label']),
+              ),
+              'field' => array(
+                '#markup' => check_plain($field_element['#label']),
+              ),
+            ),
+            'class' => array('field'),
+          ),
+          array(
+            'data' => '',
+            'class' => array('operations'),
+          ),
+        ),
+        'class' => array('field-settings'),
+      );
+
+      if (!empty($element[$bundle]['columns'][$field_name])) {
+        $column_element = &$element[$bundle]['columns'][$field_name];
+        foreach (element_children($column_element) as $key) {
+          $column_label = $column_element[$key]['#title'];
+          unset($column_element[$key]['#title']);
+          $rows[] = array(
+            'data' => array(
+              array(
+                'data' => drupal_render($column_element[$key]),
+                'class' => array('translatable'),
+              ),
+              array(
+                'data' => array(
+                  '#prefix' => '<label for="' . $column_element[$key]['#id'] . '">',
+                  '#suffix' => '</label>',
+                  'bundle' => array(
+                    '#prefix' => '<span class="element-invisible">',
+                    '#suffix' => '</span> ',
+                    '#markup' => check_plain($element[$bundle]['settings']['#label']),
+                  ),
+                  'field' => array(
+                    '#prefix' => '<span class="element-invisible">',
+                    '#suffix' => '</span> ',
+                    '#markup' => check_plain($field_element['#label']),
+                  ),
+                  'columns' => array(
+                    '#markup' => check_plain($column_label),
+                  ),
+                ),
+                'class' => array('column'),
+              ),
+              array(
+                'data' => '',
+                'class' => array('operations'),
+              ),
+            ),
+            'class' => array('column-settings'),
+          );
+        }
+      }
+    }
+  }
+
+  $build['#rows'] = $rows;
+}
+
+/**
  * Form validation handler for translation_entity_admin_settings_form().
  *
  * @see translation_entity_admin_settings_form_submit()
@@ -104,6 +247,11 @@ function translation_entity_form_language_content_settings_submit(array $form, a
       if (!empty($bundle_settings['fields'])) {
         foreach ($bundle_settings['fields'] as $field_name => $translatable) {
           $bundle_settings['fields'][$field_name] = $translatable && $bundle_settings['translatable'];
+          // If we have column settings and no column is translatable, no point
+          // in making the field translatable.
+          if (isset($bundle_settings['columns'][$field_name]) && !array_filter($bundle_settings['columns'][$field_name])) {
+            $bundle_settings['fields'][$field_name] = FALSE;
+          }
         }
       }
     }
diff --git a/core/modules/translation_entity/translation_entity.admin.js b/core/modules/translation_entity/translation_entity.admin.js
index 2f9f49f..bf253a1 100644
--- a/core/modules/translation_entity/translation_entity.admin.js
+++ b/core/modules/translation_entity/translation_entity.admin.js
@@ -7,11 +7,17 @@
  */
 Drupal.behaviors.translationEntity = {
   attach: function (context) {
-    var $context = $(context);
-    // Initially hide all field rows for non translatable bundles.
-    var $inputs = $context.find('table .bundle-settings .translatable :input');
-    $inputs.filter(':not(:checked)').once('translation-entity-admin-hide', function () {
-      $(this).closest('.bundle-settings').nextUntil('.bundle-settings').hide();
+    // Initially hide all field rows for non translatable bundles and all column
+    // rows for non translatable fields.
+    $(context).find('table .bundle-settings .translatable :input').once('translation-entity-admin-hide', function () {
+      var $input = $(this);
+      var $bundleSettings = $input.closest('.bundle-settings');
+      if (!$input.is(':checked')) {
+        $bundleSettings.nextUntil('.bundle-settings').hide();
+      }
+      else {
+        $bundleSettings.nextUntil('.bundle-settings', '.field-settings').find('.translatable :input:not(:checked)').closest('.field-settings').nextUntil(':not(.column-settings)').hide();
+      }
     });
 
     // When a bundle is made translatable all of its field instances should
@@ -20,14 +26,25 @@ Drupal.behaviors.translationEntity = {
     $('body').once('translation-entity-admin-bind').on('click', 'table .bundle-settings .translatable :input', function (e) {
       var $target = $(e.target);
       var $bundleSettings = $target.closest('.bundle-settings');
-      var $fieldSettings = $bundleSettings.nextUntil('.bundle-settings');
+      var $settings = $bundleSettings.nextUntil('.bundle-settings');
+      var $fieldSettings = $settings.filter('.field-settings');
       if ($target.is(':checked')) {
         $bundleSettings.find('.operations :input[name$="[language_show]"]').attr('checked', true);
         $fieldSettings.find('.translatable :input').attr('checked', true);
-        $fieldSettings.show();
+        $settings.show();
+      }
+      else {
+        $settings.hide();
+      }
+    }).on('click', 'table .field-settings .translatable :input', function (e) {
+      var $target = $(e.target);
+      var $fieldSettings = $target.closest('.field-settings');
+      var $columnSettings = $fieldSettings.nextUntil('.field-settings, .bundle-settings');
+      if ($target.is(':checked')) {
+        $columnSettings.show();
       }
       else {
-        $fieldSettings.hide();
+        $columnSettings.hide();
       }
     });
   }
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index e005b94..f397065 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -802,7 +802,7 @@ function translation_entity_field_extra_fields() {
 }
 
 /**
- * Implements hook_form_FORM_ID_alter().
+ * Implements hook_form_FORM_ID_alter() for field_ui_field_settings_form().
  */
 function translation_entity_form_field_ui_field_settings_form_alter(array &$form, array &$form_state, $form_id) {
   $field = $form['#field'];
@@ -837,6 +837,41 @@ function translation_entity_form_field_ui_field_settings_form_alter(array &$form
 }
 
 /**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function translation_entity_form_field_ui_field_edit_form_alter(array &$form, array &$form_state, $form_id) {
+  if ($form['#field']['translatable']) {
+    module_load_include('inc', 'translation_entity', 'translation_entity.admin');
+    $element = translation_entity_field_sync_widget($form['#field'], $form['#instance']);
+    if ($element) {
+      $form['instance']['settings']['translation_sync'] = $element;
+    }
+  }
+}
+
+/**
+ * Implements hook_field_info_alter().
+ */
+function translation_entity_field_info_alter(&$info) {
+  foreach ($info as $field_type => &$field_type_info) {
+    // By default no column has to be synchronized.
+    $field_type_info['settings'] += array('translation_sync' => FALSE);
+    // Synchronization can be enabled per instance.
+    $field_type_info['instance_settings'] += array('translation_sync' => FALSE);
+  }
+}
+
+/**
+ * Implements hook_field_attach_presave().
+ */
+function translation_entity_field_attach_presave(EntityInterface $entity) {
+  if (translation_entity_enabled($entity->entityType(), $entity->bundle())) {
+    $attributes = drupal_container()->get('request')->attributes;
+    drupal_container()->get('translation_entity.synchronizer')->synchronizeFields($entity, $attributes->get('working_langcode'), $attributes->get('source_langcode'));
+  }
+}
+
+/**
  * Implements hook_element_info_alter().
  */
 function translation_entity_element_info_alter(&$type) {
@@ -844,6 +879,7 @@ function translation_entity_element_info_alter(&$type) {
     $type['language_configuration']['#process'][] = 'translation_entity_language_configuration_element_process';
   }
 }
+
 /**
  * Returns a widget to enable entity translation per entity bundle.
  *
@@ -950,6 +986,14 @@ function translation_entity_form_language_content_settings_form_alter(array &$fo
 }
 
 /**
+ * Implements hook_preprocess_HOOK() for theme_language_content_settings_table().
+ */
+function translation_entity_preprocess_language_content_settings_table(&$variables) {
+  module_load_include('inc', 'translation_entity', 'translation_entity.admin');
+  _translation_entity_preprocess_language_content_settings_table($variables);
+}
+
+/**
  * Stores entity translation settings.
  *
  * @param array $settings
@@ -960,81 +1004,35 @@ function translation_entity_form_language_content_settings_form_alter(array &$fo
  *     language_save_default_configuration().
  *   - fields: An associative array with field names as keys and a boolean as
  *     value, indicating field translatability.
+ *   - columns: An associative array of translation synchronization settings
+ *     keyed by field names.
  */
 function translation_entity_save_settings($settings) {
   foreach ($settings as $entity_type => $entity_settings) {
     foreach ($entity_settings as $bundle => $bundle_settings) {
+      // Store bundle translatability.
       translation_entity_set_config($entity_type, $bundle, 'enabled', $bundle_settings['translatable']);
+
+      // Store column translatability.
+      if (!empty($bundle_settings['columns'])) {
+        foreach ($bundle_settings['columns'] as $field_name => $column_settings) {
+          $field = field_info_field($field_name);
+          $instance = field_info_instance($entity_type, $field_name, $bundle);
+          if ($field['translatable']) {
+            $instance['settings']['translation_sync'] = $column_settings;
+          }
+          // If the field is untransltable we need to reset the sync settings to
+          // their defaults.
+          else {
+            unset($instance['settings']['translation_sync']);
+          }
+          field_update_instance($instance);
+        }
+      }
     }
   }
+
   // Ensure entity and menu router information are correctly rebuilt.
   entity_info_cache_clear();
   menu_router_rebuild();
 }
-
-/**
- * Implements hook_preprocess_HOOK() for theme_language_content_settings_table().
- */
-function translation_entity_preprocess_language_content_settings_table(&$variables) {
-  // Alter the 'build' variable injecting the translation settings if the user
-  // has the required permission.
-  if (!user_access('administer entity translation')) {
-    return;
-  }
-
-  $element = $variables['element'];
-  $build = &$variables['build'];
-
-  array_unshift($build['#header'], array('data' => t('Translatable'), 'class' => array('translatable')));
-  $rows = array();
-
-  foreach (element_children($element) as $bundle) {
-    $field_names = !empty($element[$bundle]['fields']) ? element_children($element[$bundle]['fields']) : array();
-    $checkbox_id = $element[$bundle]['translatable']['#id'];
-    $rows[$bundle] = $build['#rows'][$bundle];
-
-    $translatable = array(
-      'data' => $element[$bundle]['translatable'],
-      'class' => array('translatable'),
-    );
-    array_unshift($rows[$bundle]['data'], $translatable);
-
-    $rows[$bundle]['data'][1]['data']['#prefix'] = '<label for="' . $checkbox_id . '">';
-
-    foreach ($field_names as $field_name) {
-      $field = $element[$bundle]['fields'][$field_name];
-      $checkbox_id = $field['#id'];
-
-      $rows[] = array(
-        'data' => array(
-          array(
-            'data' => drupal_render($field),
-            'class' => array('translatable'),
-          ),
-          array(
-            'data' => array(
-              '#prefix' => '<label for="' . $checkbox_id . '">',
-              '#suffix' => '</label>',
-              'bundle' => array(
-                '#prefix' => '<span class="element-invisible">',
-                '#suffix' => '</span> ',
-                '#markup' => check_plain($element[$bundle]['settings']['#label']),
-              ),
-              'field' => array(
-                '#markup' => check_plain($field['#label']),
-              ),
-            ),
-            'class' => array('field'),
-          ),
-          array(
-            'data' => '',
-            'class' => array('operations'),
-          ),
-        ),
-        'class' => array('field-settings'),
-      );
-    }
-  }
-
-  $build['#rows'] = $rows;
-}
