diff --git a/includes/update.inc b/includes/update.inc
index 1eb7a1d..747f33b 100644
--- a/includes/update.inc
+++ b/includes/update.inc
@@ -178,6 +178,17 @@ function update_prepare_d7_bootstrap() {
       $_COOKIE[session_name()] = $sid;
       session_id($sid);
     }
+
+    // Upgrading from D6 to D7.{0,1,2,3,4,8,...} is different than upgrading
+    // from D6 to D7.{5,6,7} (which should be considered screwed). To be able
+    // to properly handle this difference in node_update_701{2,3} we should
+    // variable_set() something (node_update_7012() may run in another
+    // invocation of a batch set than we are having here). We don't have
+    // proper Drupal variable handling here (which is a must for
+    // variable_set()), so setting only this PHP variable here, and do the
+    // variable_set() if and when we are ready to do so.
+    // @see http://drupal.org/node/1164852
+    $node_updates_from_d6 = TRUE;
   }
 
   // Create the registry tables.
@@ -302,6 +313,10 @@ function update_prepare_d7_bootstrap() {
     // Set the timezone for this request only.
     $GLOBALS['conf']['date_default_timezone'] = 'UTC';
   }
+
+  // Store if we're upgrading from D6 or not.
+  // @see http://drupal.org/node/1164852
+  variable_set('node_updates_from_d6', isset($node_updates_from_d6) ? $node_updates_from_d6 : FALSE);
 }
 
 /**
diff --git a/modules/field/field.install b/modules/field/field.install
index d56eb90..8e48155 100644
--- a/modules/field/field.install
+++ b/modules/field/field.install
@@ -435,5 +435,51 @@ function field_update_7001() {
 }
 
 /**
+ * Disable 'translatable' flag for all fields that do not contain language-specific field values.
+ */
+function field_update_7002() {
+  $fields = _update_7000_field_read_fields(array(
+    // Only currently enabled fields are affected.
+    'translatable' => 1,
+    // Field storage engines depend on the module and hook system; we can only
+    // query and update fields in SQL. Alternative field storage engines have to
+    // implement a corresponding module update on their own.
+    'storage_type' => 'field_sql_storage',
+    // Field configuration and values of deleted fields are irrelevant.
+    'deleted' => 0,
+  ));
+  $changed_fields = array();
+  foreach ($fields as $field) {
+    $tables = array("field_data_{$field['field_name']}", "field_revision_{$field['field_name']}");
+    $has_language = FALSE;
+    foreach ($tables as $table) {
+      // 'und' denotes LANGUAGE_NONE; constant values may change over time.
+      $query = db_select($table)->condition('language', 'und', '<>')->range(0, 1);
+      $query->addExpression(1);
+      $has_language = $has_language || $query->execute()->fetchField();
+    }
+    // Only in case there is no language-specific field value, update the
+    // field's configuration to mark it untranslatable.
+    // Note: There is a small chance of disabling the translatable flag for
+    // fields that actually have a field translation handler associated (but
+    // e.g., no values yet). However, since the field translation handler
+    // entirely depends on module hooks, it is impossible to gather this
+    // information in a module update.
+    if (!$has_language) {
+      $changed_fields[] = $field['field_name'];
+      db_update('field_config')
+        ->condition('id', $field['id'])
+        ->fields(array('translatable' => 0))
+        ->execute();
+    }
+  }
+  if ($changed_fields) {
+    drupal_set_message(t('The following fields have been changed to be no longer translatable: %field-list.', array(
+      '%field-list' => implode(', ', $changed_fields),
+    )));
+  }
+}
+
+/**
  * @} End of "addtogroup updates-6.x-to-7.x"
  */
diff --git a/modules/node/node.install b/modules/node/node.install
index 2498091..ed86e5d 100644
--- a/modules/node/node.install
+++ b/modules/node/node.install
@@ -610,6 +610,7 @@ function node_update_7006(&$sandbox) {
       'module' => 'text',
       'cardinality' => 1,
       'entity_types' => array('node'),
+      'translatable' => TRUE,
     );
     _update_7000_field_create_field($body_field);
 
@@ -861,5 +862,126 @@ function node_update_7011() {
 }
 
 /**
+ * Switches body fields to untranslatable while upgrading from D6.
+ */
+function node_update_7012() {
+  // If we are upgrading from D6, then body fields should be set (back) to
+  // untranslatable (as D6 did not know about the idea of translating fields,
+  // but only nodes). Do nothing if we are upgrading from sites that started
+  // their life as 7.x ones, though: it _is_ a valid use case to have
+  // translatable body fields in D7 sites, anyways.
+  if (variable_get('node_updates_from_d6', FALSE)) {
+    // It is not acceptable to use field_update_field() throughout the upgrade
+    // process _and_ we do not have an update counterpart for
+    // _update_7000_field_create_field(). Because of this, we must do this
+    // terrible dance here. Remember: we are upgrading from D6 here, this code
+    // does not run for D7->D7 upgrades. If somebody ever wants to do
+    // something like this (modifying anything in the {field_config} table
+    // directly) in a D7->D7 upgrade, then it may lead directly to hell, and
+    // the gatekeeper there is chx. You have been warned.
+    db_update('field_config')
+      ->fields(array('translatable' => 0))
+      ->condition('field_name', 'body')
+      ->execute();
+    node_type_cache_reset();
+  }
+}
+
+/**
  * @} End of "addtogroup updates-6.x-to-7.x"
  */
+
+/**
+ * Searches for localized field values of non-translatable fields and corrects them.
+ */
+function node_update_7013(&$sandbox) {
+  $sandbox['#finished'] = 0;
+
+  if (!isset($sandbox['total'])) {
+    // Initial invocation: collect the tables that need to be updated, and
+    // count the total affected rows.
+    $fields = _update_7000_field_read_fields(array(
+      // Only currently enabled fields are affected.
+      'translatable' => 0,
+      // Field storage engines depend on the module and hook system; we can only
+      // query and update fields in SQL. Alternative field storage engines have to
+      // implement a corresponding module update on their own.
+      'storage_type' => 'field_sql_storage',
+      // Field configuration and values of deleted fields are irrelevant.
+      'deleted' => 0,
+    ));
+    $sandbox['tables'] = array();
+    $sandbox['total'] = 0;
+    foreach ($fields as $field) {
+      $tables = array("field_data_{$field['field_name']}", "field_revision_{$field['field_name']}");
+      foreach ($tables as $table) {
+        $query = db_select($table, 'b1');
+        $query->join($table, 'b2', 'b1.entity_type = b2.entity_type AND b1.entity_id = b2.entity_id AND b1.delta = b2.delta');
+        $query->fields('b1', array('entity_type', 'entity_id', 'delta'));
+        $query->condition('b1.deleted', 0);
+        // 'und' denotes LANGUAGE_NONE; constant values may change over time.
+        $query->condition('b2.language', 'und', '<>');
+        $query->groupBy('entity_type');
+        $query->groupBy('entity_id');
+        $query->groupBy('delta');
+        $query->having('COUNT(DISTINCT b1.language) = 1');
+        $affected_values_cnt = $query->countQuery()->execute()->fetchField();
+        // Do not memorize the table for subsequent invocations if it does
+        // not have data to be corrected.
+        if ($affected_values_cnt) {
+          $sandbox['tables'][] = $table;
+          $sandbox['total'] += $affected_values_cnt;
+        }
+      }
+    }
+    $sandbox['count'] = 0;
+    // If we have nothing to do, bail out early (to avoid infinite cycles).
+    if (empty($sandbox['tables'])) {
+      $sandbox['#finished'] = 1;
+    }
+  }
+
+  else {
+    // Subsequent invocations: do the actual corrections, 200 by 200. It's a
+    // bit tricky: the query that drives the actual update cycle returns only
+    // rows to be updated - which in turn means that any row that has been
+    // updated will not show up again in subsequent invocations of the query.
+    // For this same reason it's OK to (try to) update every table that's
+    // listed as affected.
+    $batch_size = 200;
+    $count = 0;
+    foreach ($sandbox['tables'] as $table) {
+      $query = db_select($table, 'b1');
+      $query->join($table, 'b2', 'b1.entity_type = b2.entity_type AND b1.entity_id = b2.entity_id AND b1.delta = b2.delta');
+      $query->fields('b1', array('entity_type', 'entity_id', 'delta'));
+      $query->condition('b1.deleted', 0);
+      $query->condition('b2.language', 'und', '<>');
+      $query->groupBy('entity_type');
+      $query->groupBy('entity_id');
+      $query->groupBy('delta');
+      $query->having('COUNT(DISTINCT b1.language) = 1');
+      $query->orderBy('entity_type');
+      $query->orderBy('entity_id');
+      $query->orderBy('delta');
+      $result = $query->execute();
+      foreach ($result as $row) {
+        db_update($table)
+          ->condition('entity_type', $row->entity_type)
+          ->condition('entity_id', $row->entity_id)
+          ->condition('delta', $row->delta)
+          ->condition('language', 'und', '<>') // For safety purposes.
+          ->fields(array('language' => LANGUAGE_NONE))
+          ->execute();
+        $count++;
+        $sandbox['count']++;
+        if ($count >= $batch_size) {
+          // If we have reached our own limit (per invocation), break out of
+          // both the foreach() cycles.
+          break 2;
+        }
+      }
+    }
+    // Inform the batch API of our progress.
+    $sandbox['#finished'] = $sandbox['count'] / $sandbox['total'];
+  }
+}
diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info
index f51804c..6b5f34d 100644
--- a/modules/simpletest/simpletest.info
+++ b/modules/simpletest/simpletest.info
@@ -45,5 +45,6 @@ files[] = tests/upgrade/upgrade.locale.test
 files[] = tests/upgrade/upgrade.menu.test
 files[] = tests/upgrade/upgrade.node.test
 files[] = tests/upgrade/upgrade.taxonomy.test
+files[] = tests/upgrade/upgrade.translatable.test
 files[] = tests/upgrade/upgrade.upload.test
 files[] = tests/upgrade/upgrade.user.test
diff --git a/modules/simpletest/tests/upgrade/drupal-6.translatable.database.php b/modules/simpletest/tests/upgrade/drupal-6.translatable.database.php
new file mode 100644
index 0000000..5162116
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/drupal-6.translatable.database.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * Database additions for translatable tests.
+ */
+
+db_insert('node')->fields(array(
+  'nid',
+  'vid',
+  'type',
+  'language',
+  'title',
+  'uid',
+  'status',
+  'created',
+  'changed',
+  'comment',
+  'promote',
+  'moderate',
+  'sticky',
+  'tnid',
+  'translate',
+))
+->values(array(
+  'nid' => '53',
+  'vid' => '63',
+  'type' => 'translatable_page',
+  'language' => 'fr',
+  'title' => 'First translatable page',
+  'uid' => '1',
+  'status' => '1',
+  'created' => '1298363952',
+  'changed' => '1298363952',
+  'comment' => '2',
+  'promote' => '0',
+  'moderate' => '0',
+  'sticky' => '0',
+  'tnid' => '0',
+  'translate' => '0',
+))
+->execute();
+
+db_insert('node_revisions')->fields(array(
+  'nid',
+  'vid',
+  'uid',
+  'title',
+  'body',
+  'teaser',
+  'log',
+  'timestamp',
+  'format',
+))
+->values(array(
+  'nid' => '53',
+  'vid' => '63',
+  'uid' => '1',
+  'title' => 'First translatable page',
+  'body' => 'Body of the first translatable page.',
+  'teaser' => 'Teaser of the first translatable page.',
+  'log' => '',
+  'timestamp' => '1298363952',
+  'format' => '1',
+))
+->execute();
+
+db_insert('node_comment_statistics')->fields(array(
+  'nid',
+  'last_comment_timestamp',
+  'last_comment_name',
+  'last_comment_uid',
+  'comment_count',
+))
+->values(array(
+  'nid' => '53',
+  'last_comment_timestamp' => '1298363952',
+  'last_comment_name' => NULL,
+  'last_comment_uid' => '1',
+  'comment_count' => '0',
+))
+->execute();
+
+db_insert('node_type')->fields(array(
+  'type',
+  'name',
+  'module',
+  'description',
+  'help',
+  'has_title',
+  'title_label',
+  'has_body',
+  'body_label',
+  'min_word_count',
+  'custom',
+  'modified',
+  'locked',
+  'orig_type',
+))
+->values(array(
+  'type' => 'translatable_page',
+  'name' => 'Translatable page',
+  'module' => 'node',
+  'description' => 'A <em>translatable page</em> is like a normal page, but with multilanguage support.',
+  'help' => '',
+  'has_title' => '1',
+  'title_label' => 'Title',
+  'has_body' => '1',
+  'body_label' => 'Body',
+  'min_word_count' => '0',
+  'custom' => '0',
+  'modified' => '0',
+  'locked' => '1',
+  'orig_type' => '',
+))
+->execute();
+
+db_insert('variable')->fields(array(
+  'name',
+  'value',
+))
+->values(array(
+  'name' => 'language_content_type_translatable_page',
+  'value' => 's:1:"1";',
+))
+->execute();
diff --git a/modules/simpletest/tests/upgrade/upgrade.translatable.test b/modules/simpletest/tests/upgrade/upgrade.translatable.test
new file mode 100644
index 0000000..cb66e78
--- /dev/null
+++ b/modules/simpletest/tests/upgrade/upgrade.translatable.test
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Upgrade test for translatable content types of node.module.
+ */
+class TranslatableUpgradePathTestCase extends UpgradePathTestCase {
+  public static function getInfo() {
+    return array(
+      'name'  => 'Translatable upgrade path',
+      'description'  => 'Upgrade path tests for the translatable content types of node.module.',
+      'group' => 'Upgrade path',
+    );
+  }
+
+  public function setUp() {
+    // Path to the database dump files.
+    $this->databaseDumpFiles = array(
+      drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php',
+      drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.locale.database.php',
+      drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.translatable.database.php',
+    );
+    parent::setUp();
+
+    $this->uninstallModulesExcept(array('locale'));
+  }
+
+  /**
+   * Test a successful upgrade (no negotiation).
+   */
+  public function testTranslatableUpgrade() {
+    $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.'));
+
+    // The D6 database contains the translatable (English) node
+    // "First translatable page" with nid 53.
+    $nid = 53;
+    $title = 'First translatable page';
+    $teaser = 'Teaser of the first translatable page.';
+
+    // Check whether the node displays properly.
+    $this->drupalGet("node/$nid");
+    $this->assertText($body = 'Body of the first translatable page.', t('Translatable node body displays properly'));
+
+    // Retrieve node object, ensure that both the body and the teaser has
+    // survived upgrade properly.
+    $node = $this->drupalGetNodeByTitle($title);
+    $this->assertTrue($node != NULL, t('Node @title was loaded', array('@title' => $title)));
+    $this->verbose('Upgraded node: ' . var_export($node, TRUE));
+    $this->assertEqual($node->body[LANGUAGE_NONE][0]['value'], $body, 'Body of the node survided upgrade properly');
+    $this->assertEqual($node->body[LANGUAGE_NONE][0]['summary'], $teaser, 'Teaser of the node survided upgrade properly');
+  }
+}
