Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.309
diff -u -p -r1.309 bootstrap.inc
--- includes/bootstrap.inc	9 Oct 2009 16:33:13 -0000	1.309
+++ includes/bootstrap.inc	15 Oct 2009 02:04:30 -0000
@@ -1627,7 +1627,7 @@ function drupal_language_initialize() {
 
   // Ensure the language is correctly returned, even without multilanguage support.
   // Useful for eg. XML/HTML 'lang' attributes.
-  if (variable_get('language_count', 1) == 1) {
+  if (!drupal_multilingual()) {
     $default = language_default();
     foreach ($types as $type) {
       $GLOBALS[$type] = $default;
@@ -1657,6 +1657,17 @@ function drupal_language_types() {
 }
 
 /**
+ * Return true if there is more than one language enabled.
+ */
+function drupal_multilingual() {
+  static $multilingual;
+  if (!isset($multilingual)) {
+    $multilingual = variable_get('language_count', 1) > 1;
+  }
+  return $multilingual;
+}
+
+/**
  * Return an array of the available language types.
  */
 function language_types() {
@@ -1672,7 +1683,7 @@ function language_list($field = 'languag
   $languages = &drupal_static(__FUNCTION__);
   // Init language list
   if (!isset($languages)) {
-    if (variable_get('language_count', 1) > 1 || module_exists('locale')) {
+    if (drupal_multilingual() || module_exists('locale')) {
       $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language');
     }
     else {
Index: includes/language.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/language.inc,v
retrieving revision 1.20
diff -u -p -r1.20 language.inc
--- includes/language.inc	9 Oct 2009 16:33:13 -0000	1.20
+++ includes/language.inc	15 Oct 2009 02:04:30 -0000
@@ -396,3 +396,36 @@ function language_url_split_prefix($path
 
   return array(FALSE, $path);
 }
+
+/**
+ * Return the possible fallback languages ordered by language weight.
+ *
+ * @param
+ *   The language type.
+ *
+ * @return
+ *   An array of language codes.
+ */
+function language_fallback_get_candidates($type = LANGUAGE_TYPE_CONTENT) {
+  $fallback_candidates = &drupal_static(__FUNCTION__);
+
+  if (!isset($fallback_candidates)) {
+    $fallback_candidates = array();
+
+    // Get languages ordered by weight.
+    // Use array keys to avoid duplicated entries.
+    foreach (language_list('weight') as $languages) {
+      foreach ($languages as $language) {
+        $fallback_candidates[$language->language] = NULL;
+      }
+    }
+
+    $fallback_candidates = array_keys($fallback_candidates);
+    $fallback_candidates[] = FIELD_LANGUAGE_NONE;
+
+    // Let other modules hook in and add/change candidates.
+    drupal_alter('language_get_fallback_candidates', $fallback_candidates);
+  }
+
+  return $fallback_candidates;
+}
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.232
diff -u -p -r1.232 locale.inc
--- includes/locale.inc	13 Oct 2009 21:34:14 -0000	1.232
+++ includes/locale.inc	15 Oct 2009 02:14:46 -0000
@@ -161,6 +161,7 @@ function locale_languages_overview_form_
 
   // Changing the language settings impacts the interface.
   cache_clear_all('*', 'cache_page', TRUE);
+  module_invoke_all('multilingual_settings_changed');
 
   $form_state['redirect'] = 'admin/config/regional/language';
   return;
@@ -362,6 +363,8 @@ function locale_languages_predefined_for
     batch_set($batch);
   }
 
+  module_invoke_all('multilingual_settings_changed');
+
   $form_state['redirect'] = 'admin/config/regional/language';
   return;
 }
@@ -469,7 +472,8 @@ function locale_languages_delete_form_su
       ->fields(array('language' => ''))
       ->condition('language', $form_state['values']['langcode'])
       ->execute();
-    $variables = array('%locale' => $languages[$form_state['values']['langcode']]->name);
+    module_invoke_all('multilingual_settings_changed');
+    $variables = array('%locale' => $languages[$form_state['values']['langcode']]->name);    
     drupal_set_message(t('The language %locale has been removed.', $variables));
     watchdog('locale', 'The language %locale has been removed.', $variables);
   }
Index: modules/field/field.info.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.info.inc,v
retrieving revision 1.21
diff -u -p -r1.21 field.info.inc
--- modules/field/field.info.inc	14 Oct 2009 14:55:12 -0000	1.21
+++ modules/field/field.info.inc	15 Oct 2009 02:04:30 -0000
@@ -147,7 +147,7 @@ function _field_info_collate_types($rese
             // Provide defaults.
             $entity_info += array(
               'cacheable' => TRUE,
-              'translation_handlers' => array(),
+              'translation' => array(),
               'bundles' => array(),
             );
             $entity_info['object keys'] += array(
Index: modules/field/field.multilingual.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.multilingual.inc,v
retrieving revision 1.1
diff -u -p -r1.1 field.multilingual.inc
--- modules/field/field.multilingual.inc	22 Aug 2009 00:58:52 -0000	1.1
+++ modules/field/field.multilingual.inc	15 Oct 2009 02:15:40 -0000
@@ -7,6 +7,13 @@
  */
 
 /**
+ * Implement hook_multilingual_settings_changed().
+ */
+function field_multilingual_settings_changed() {
+  cache_clear_all('field_info_types', 'cache_field');
+}
+
+/**
  * Collect the available languages for the given entity type and field.
  *
  * If an entity has a translation handler and the given field is translatable,
@@ -31,12 +38,14 @@ function field_multilingual_available_la
   $field_name = $field['field_name'];
 
   if (!isset($field_languages[$field_name]) || !empty($suggested_languages)) {
-    $obj_info = field_info_fieldable_types($obj_type);
-    if (!empty($obj_info['translation_handlers']) && $field['translatable']) {
-      $available_languages = field_multilingual_content_languages();
+    $translation_handlers = field_multilingual_check_translation_handlers($obj_type);
+
+    if ($translation_handlers && $field['translatable']) {
       // The returned languages are a subset of the intersection of enabled ones
       // and suggested ones.
+      $available_languages = field_multilingual_content_languages();
       $languages = !empty($suggested_languages) ? $available_languages = array_intersect($available_languages, $suggested_languages) : $available_languages;
+
       foreach (module_implements('field_languages') as $module) {
         $function = $module . '_field_languages';
         $function($obj_type, $field, $languages);
@@ -71,20 +80,36 @@ function field_multilingual_content_lang
   return array_keys(language_list() + array(FIELD_LANGUAGE_NONE => NULL));
 }
 
-
 /**
  * Check if a module is registered as a translation handler for a given entity.
  *
+ * If no handler is passed, simply check if there is any translation handler
+ * enabled for the given entity type.
+ *
  * @param $obj_type
  *   The type of the entity whose fields are to be translated.
  * @param $handler
  *   The name of the handler to be checked.
+ *
  * @return
  *   TRUE, if the handler is allowed to manage field translations.
  */
-function field_multilingual_check_translation_handler($obj_type, $handler) {
+function field_multilingual_check_translation_handlers($obj_type, $handler = NULL) {
   $obj_info = field_info_fieldable_types($obj_type);
-  return isset($obj_info['translation_handlers'][$handler]);
+
+  if (isset($handler)) {
+    return isset($obj_info['translation'][$handler]) && !empty($obj_info['translation'][$handler]);
+  }
+  elseif (isset($obj_info['translation'])) {
+    foreach ($obj_info['translation'] as $handler_info) {
+      // The translation handler must use a non-empty data structure.
+      if (!empty($handler_info)) {
+        return TRUE;
+      }
+    }
+  }
+
+  return FALSE;
 }
 
 /**
Index: modules/field/field.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.test,v
retrieving revision 1.59
diff -u -p -r1.59 field.test
--- modules/field/field.test	14 Oct 2009 14:55:12 -0000	1.59
+++ modules/field/field.test	15 Oct 2009 02:04:30 -0000
@@ -2253,11 +2253,12 @@ class FieldTranslationsTestCase extends 
    */
   function testFieldAvailableLanguages() {
     // Test 'translatable' fieldable info.
+    field_test_entity_info_translatable('test_entity', FALSE);
     $field = $this->field;
     $field['field_name'] .= '_untranslatable';
     $langcode = language_default();
     $suggested_languages = array($langcode->language);
-    $available_languages = field_multilingual_available_languages($this->obj_type, $field);
+    $available_languages = field_multilingual_available_languages($this->obj_type, $field, $suggested_languages);
     $this->assertTrue(count($available_languages) == 1 && $available_languages[0] === FIELD_LANGUAGE_NONE, t('Untranslatable entity: suggested language ignored.'));
 
     // Enable field translations for the entity.
@@ -2392,7 +2393,7 @@ class FieldTranslationsTestCase extends 
     // Enable field translations for nodes.
     field_test_entity_info_translatable('node', TRUE);
     $obj_info = field_info_fieldable_types('node');
-    $this->assertTrue(count($obj_info['translation_handlers']), t('Nodes are translatable.'));
+    $this->assertTrue(count($obj_info['translation']), t('Nodes are translatable.'));
 
     // Prepare the field translations.
     $eid = $evid = 1;
Index: modules/locale/locale.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.api.php,v
retrieving revision 1.5
diff -u -p -r1.5 locale.api.php
--- modules/locale/locale.api.php	9 Oct 2009 16:33:13 -0000	1.5
+++ modules/locale/locale.api.php	15 Oct 2009 02:17:37 -0000
@@ -137,5 +137,12 @@ function hook_language_negotiation_info_
 }
 
 /**
+ * Allow modules to react to language settings changes.
+ */
+function hook_multilingual_settings_changed() {
+  cache_clear_all('field_info_types', 'cache_field');
+}
+
+/**
  * @} End of "addtogroup hooks".
  */
Index: modules/locale/locale.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.info,v
retrieving revision 1.11
diff -u -p -r1.11 locale.info
--- modules/locale/locale.info	8 Jun 2009 09:23:52 -0000	1.11
+++ modules/locale/locale.info	15 Oct 2009 02:04:30 -0000
@@ -6,4 +6,5 @@ version = VERSION
 core = 7.x
 files[] = locale.module
 files[] = locale.install
+files[] = locale.field.inc
 files[] = locale.test
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.263
diff -u -p -r1.263 locale.module
--- modules/locale/locale.module	13 Oct 2009 21:34:14 -0000	1.263
+++ modules/locale/locale.module	15 Oct 2009 02:20:03 -0000
@@ -386,13 +386,23 @@ function locale_form_node_type_form_alte
 }
 
 /**
+ * Return whether the given content type has multilingual support.
+ *
+ * @return
+ *   True if multilingual support is enabled.
+ */
+function locale_multilingual_node_type($type_name) {
+  return (bool) variable_get('language_content_type_' . $type_name, 0);
+}
+
+/**
  * Implement hook_form_alter().
  *
  * Adds language fields to forms.
  */
 function locale_form_alter(&$form, &$form_state, $form_id) {
   // Only alter user forms if there is more than one language.
-  if (variable_get('language_count', 1) > 1) {
+  if (drupal_multilingual()) {
     // Display language selector when either creating a user on the admin
     // interface or editing a user account.
     if (($form_id == 'user_register_form' && user_access('administer users')) || ($form_id == 'user_profile_form' && $form['#user_category'] == 'account')) {
@@ -400,13 +410,14 @@ function locale_form_alter(&$form, &$for
     }
   }
   if (isset($form['#id']) && $form['#id'] == 'node-form') {
-    if (isset($form['#node']->type) && variable_get('language_content_type_' . $form['#node']->type, 0)) {
+    if (isset($form['#node']->type) && locale_multilingual_node_type($form['#node']->type)) {
       $form['language'] = array(
         '#type' => 'select',
         '#title' => t('Language'),
         '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''),
         '#options' => array('' => t('Language neutral')) + locale_language_list('name'),
       );
+      $form['#submit'][] = 'locale_field_node_form_submit';
     }
     // Node type without language selector: assign the default for new nodes
     elseif (!isset($form['#node']->nid)) {
@@ -420,6 +431,19 @@ function locale_form_alter(&$form, &$for
 }
 
 /**
+ * Form submit handler for node_form().
+ *
+ * Check if Locale is registered as a translation handler and handle possible
+ * node language changes.
+ */
+function locale_field_node_form_submit($form, &$form_state) {
+  if (field_multilingual_check_translation_handlers('node', 'locale')) {
+    module_load_include('inc', 'locale', 'locale.field');
+    locale_field_node_form_update_field_language($form, $form_state);
+  }
+}
+
+/**
  * Implement hook_theme().
  */
 function locale_theme() {
@@ -440,6 +464,34 @@ function locale_theme() {
 }
 
 /**
+ * Implement hook_field_attach_view_alter().
+ */
+function locale_field_attach_view_alter(&$output, $context) {
+  // In locale_field_fallback_view() we might call field_attach_view(). The
+  // static variable avoids unnecessary recursion. 
+  static $recursion;
+
+  // Do not apply fallback rules if disabled or if Locale is not registered as a
+  // translation handler.
+  if (!$recursion && variable_get('locale_field_fallback_view', TRUE) && field_multilingual_check_translation_handlers($context['obj_type'], 'locale')) {
+    $recursion = TRUE;
+    module_load_include('inc', 'locale', 'locale.field');
+    locale_field_fallback_view($output, $context);
+    $recursion = FALSE;
+  }
+}
+
+/**
+ * Implement hook_entity_info_alter().
+ */
+function locale_entity_info_alter(&$entity_info) {
+  $enabled = drupal_multilingual();
+  foreach ($entity_info as $type => $info) {
+    $entity_info[$type]['translation']['locale'] = $enabled;
+  }
+}
+
+/**
  * Implement hook_language_types_info().
  */
 function locale_language_types_info() {
@@ -829,7 +881,7 @@ function locale_block_info() {
  * Displays a language switcher. Only show if we have at least two languages.
  */
 function locale_block_view($type) {
-  if (variable_get('language_count', 1) > 1) {
+  if (drupal_multilingual()) {
     $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
     $links = language_negotiation_get_switch_links($type, $path);
 
Index: modules/locale/locale.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v
retrieving revision 1.44
diff -u -p -r1.44 locale.test
--- modules/locale/locale.test	13 Oct 2009 21:34:14 -0000	1.44
+++ modules/locale/locale.test	15 Oct 2009 02:04:30 -0000
@@ -16,6 +16,7 @@
  *  - a functional test for configuring a different path alias per language;
  *  - a functional test for configuring a different path alias per language;
  *  - a functional test for multilingual support by content type and on nodes.
+ *  - a functional test for multilingual fields.
  */
 
 
@@ -1619,6 +1620,73 @@ class UILanguageNegotiationTest extends 
   }
 }
 
+class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Multilingual fields',
+      'description' => 'Test multilingual support for fields.',
+      'group' => 'Locale',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('locale');
+  }
+
+  function testMultilingualNodeForm() {
+    // Setup users.
+    $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages', 'create page content', 'edit own page content'));
+    $this->drupalLogin($admin_user);
+
+    // Add a new language.
+    require_once DRUPAL_ROOT . '/includes/locale.inc';
+    locale_add_language('it', 'Italian', 'Italiano', LANGUAGE_LTR, '', '', TRUE, FALSE);
+
+    // Set page content type to use multilingual support.
+    $this->drupalGet('admin/structure/types/manage/page');
+    $this->assertText(t('Multilingual support'), t('Multilingual support fieldset present on content type configuration form.'));
+    $edit = array(
+      'language_content_type' => 1,
+    );
+    $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
+    $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Page')), t('Page content type has been updated.'));
+
+    // Create page content.
+    $langcode = FIELD_LANGUAGE_NONE;
+    $title_key = "title[$langcode][0][value]";
+    $title_value = $this->randomName(8);
+    $body_key = "body[$langcode][0][value]";
+    $body_value = $this->randomName(16);
+
+    // Create node to edit.
+    $edit = array();
+    $edit[$title_key] = $title_value;
+    $edit[$body_key] = $body_value;
+    $edit['language'] = 'en';
+    $this->drupalPost('node/add/page', $edit, t('Save'));
+
+    // Check that the node exists in the database.
+    $node = $this->drupalGetNodeByTitle($edit[$title_key]);
+    $this->assertTrue($node, t('Node found in database.'));
+
+    $assert = isset($node->body['en']) && !isset($node->body[FIELD_LANGUAGE_NONE]) && $node->body['en'][0]['value'] == $body_value;
+    $this->assertTrue($assert, t('Field language correctly set.'));
+
+    // Change node language.
+    $this->drupalGet("node/$node->nid/edit");
+    $edit = array(
+      $title_key => $this->randomName(8),
+      'language' => 'it'
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $node = $this->drupalGetNodeByTitle($edit[$title_key]);
+    $this->assertTrue($node, t('Node found in database.'));
+
+    $assert = isset($node->body['it']) && !isset($node->body['en']) && $node->body['it'][0]['value'] == $body_value;
+    $this->assertTrue($assert, t('Field language correctly changed.'));
+  }
+}
+
 /**
  * Functional tests for localizing date formats.
  */
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1145
diff -u -p -r1.1145 node.module
--- modules/node/node.module	11 Oct 2009 03:07:18 -0000	1.1145
+++ modules/node/node.module	15 Oct 2009 02:04:30 -0000
@@ -534,6 +534,7 @@ function node_configure_fields($type) {
       $field = array(
         'field_name' => 'body',
         'type' => 'text_with_summary',
+        'translatable' => TRUE,
       );
       $field = field_create_field($field);
     }
Index: modules/path/path.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/path/path.test,v
retrieving revision 1.22
diff -u -p -r1.22 path.test
--- modules/path/path.test	11 Oct 2009 03:07:19 -0000	1.22
+++ modules/path/path.test	15 Oct 2009 02:04:30 -0000
@@ -180,7 +180,6 @@ class PathLanguageTestCase extends Drupa
     $edit['langcode'] = 'fr';
 
     $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
 
     // Set language negotiation to "Path prefix with fallback".
     include_once DRUPAL_ROOT . '/includes/locale.inc';
@@ -214,9 +213,10 @@ class PathLanguageTestCase extends Drupa
     $this->drupalGet('node/' . $english_node->nid . '/translate');
     $this->clickLink(t('add translation'));
     $edit = array();
+    $langcode = 'fr';
+    $edit["body[$langcode][0][value]"] = $this->randomName();
     $langcode = FIELD_LANGUAGE_NONE;
     $edit["title[$langcode][0][value]"] = $this->randomName();
-    $edit["body[$langcode][0][value]"] = $this->randomName();
     $edit['path'] = $this->randomName();
     $this->drupalPost(NULL, $edit, t('Save'));
 
@@ -233,9 +233,9 @@ class PathLanguageTestCase extends Drupa
     $this->assertText($french_node->title[FIELD_LANGUAGE_NONE][0]['value'], 'Alias for French translation works.');
 
     // Confirm that the alias is returned by url().
+    drupal_static_reset('language_list');
     $languages = language_list();
     $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->language]));
     $this->assertTrue(strpos($url, $edit['path']), t('URL contains the path alias.'));
   }
 }
-
Index: modules/simpletest/tests/field_test.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/field_test.install,v
retrieving revision 1.5
diff -u -p -r1.5 field_test.install
--- modules/simpletest/tests/field_test.install	10 Sep 2009 06:38:19 -0000	1.5
+++ modules/simpletest/tests/field_test.install	15 Oct 2009 02:04:30 -0000
@@ -7,6 +7,17 @@
  */
 
 /**
+ * Implement hook_install().
+ */
+function field_test_install() {
+  // hook_entity_info_alter() needs to be executed as last.
+  db_update('system')
+    ->fields(array('weight' => 1))
+    ->condition('name', 'field_test')
+    ->execute();
+}
+
+/**
  * Implement hook_schema().
  */
 function field_test_schema() {
Index: modules/simpletest/tests/field_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/field_test.module,v
retrieving revision 1.29
diff -u -p -r1.29 field_test.module
--- modules/simpletest/tests/field_test.module	14 Oct 2009 14:55:12 -0000	1.29
+++ modules/simpletest/tests/field_test.module	15 Oct 2009 02:04:30 -0000
@@ -93,8 +93,13 @@ function field_test_entity_info() {
  * Implement hook_entity_info_alter().
  */
 function field_test_entity_info_alter(&$entity_info) {
+  // Enable/disable field_test as a translation handler.
   foreach (field_test_entity_info_translatable() as $obj_type => $translatable) {
-    $entity_info[$obj_type]['translation_handlers']['field_test'] = TRUE;
+    $entity_info[$obj_type]['translation']['field_test'] = $translatable;
+  }
+  // Disable locale as a translation handler.
+  foreach (field_info_fieldable_types() as $obj_type => $info) {
+    $entity_info[$obj_type]['translation']['locale'] = FALSE;
   }
 }
 
@@ -637,7 +642,7 @@ function field_test_field_languages($obj
  */
 function field_test_entity_info_translatable($obj_type = NULL, $translatable = NULL) {
   $stored_value = &drupal_static(__FUNCTION__, array());
-  if (isset($obj_type) && isset($translatable)) {
+  if (isset($obj_type)) {
     $stored_value[$obj_type] = $translatable;
     _field_info_collate_types(TRUE);
   }
Index: modules/translation/translation.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.test,v
retrieving revision 1.19
diff -u -p -r1.19 translation.test
--- modules/translation/translation.test	11 Oct 2009 03:07:21 -0000	1.19
+++ modules/translation/translation.test	15 Oct 2009 02:04:30 -0000
@@ -68,7 +68,7 @@ class TranslationTestCase extends Drupal
 
     // Update original and mark translation as outdated.
     $edit = array();
-    $edit["body[$langcode][0][value]"] = $this->randomName();
+    $edit["body[$node->language][0][value]"] = $this->randomName();
     $edit['translation[retranslate]'] = TRUE;
     $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
     $this->assertRaw(t('Page %title has been updated.', array('%title' => $node_title)), t('Original node updated.'));
@@ -79,7 +79,7 @@ class TranslationTestCase extends Drupal
 
     // Update translation and mark as updated.
     $edit = array();
-    $edit["body[$langcode][0][value]"] = $this->randomName();
+    $edit["body[$node_translation->language][0][value]"] = $this->randomName();
     $edit['translation[status]'] = FALSE;
     $this->drupalPost('node/' . $node_translation->nid . '/edit', $edit, t('Save'));
     $this->assertRaw(t('Page %title has been updated.', array('%title' => $node_translation_title)), t('Translated node updated.'));
@@ -133,10 +133,10 @@ class TranslationTestCase extends Drupal
     $edit["body[$langcode][0][value]"] = $body;
     $edit['language'] = $language;
     $this->drupalPost('node/add/page', $edit, t('Save'));
-    $this->assertRaw(t('Page %title has been created.', array('%title' => $edit["title[$langcode][0][value]"])), t('Page created.'));
+    $this->assertRaw(t('Page %title has been created.', array('%title' => $title)), t('Page created.'));
 
     // Check to make sure the node was created.
-    $node = $this->drupalGetNodeByTitle($edit["title[$langcode][0][value]"]);
+    $node = $this->drupalGetNodeByTitle($title);
     $this->assertTrue($node, t('Node found in database.'));
 
     return $node;
@@ -156,12 +156,12 @@ class TranslationTestCase extends Drupal
     $edit = array();
     $langcode = FIELD_LANGUAGE_NONE;
     $edit["title[$langcode][0][value]"] = $title;
-    $edit["body[$langcode][0][value]"] = $body;
+    $edit["body[$language][0][value]"] = $body;
     $this->drupalPost(NULL, $edit, t('Save'));
-    $this->assertRaw(t('Page %title has been created.', array('%title' => $edit["title[$langcode][0][value]"])), t('Translation created.'));
+    $this->assertRaw(t('Page %title has been created.', array('%title' => $title)), t('Translation created.'));
 
     // Check to make sure that translation was successful.
-    $node = $this->drupalGetNodeByTitle($edit["title[$langcode][0][value]"]);
+    $node = $this->drupalGetNodeByTitle($title);
     $this->assertTrue($node, t('Node found in database.'));
 
     return $node;
Index: modules/locale/locale.field.inc
===================================================================
RCS file: modules/locale/locale.field.inc
diff -N modules/locale/locale.field.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/locale/locale.field.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,77 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Field API multilingual handling.
+ */
+
+/**
+ * Form submit handler for node_form().
+ *
+ * Update the field language according to the node language, changing the
+ * previous language if necessary.
+ */
+function locale_field_node_form_update_field_language($form, &$form_state, $reset_previous = TRUE) {
+  $node = (object) $form_state['values'];
+  $available_languages = field_multilingual_content_languages();
+  // @todo: Unify language neutral language codes.
+  $selected_language = empty($node->language) ? FIELD_LANGUAGE_NONE : $node->language;
+  list(, , $bundle) = field_extract_ids('node', $node);
+
+  foreach (field_info_instances($bundle) as $instance) {
+    $field_name = $instance['field_name'];
+    $field = field_info_field($field_name);
+    $previous_language = $form[$field_name]['#language'];
+
+    // Handle a possible language change: previous language values are deleted,
+    // new ones are inserted.
+    if ($field['translatable'] && $previous_language != $selected_language) {
+      $form_state['values'][$field_name][$selected_language] = $node->{$field_name}[$previous_language];
+      if ($reset_previous) {
+        $form_state['values'][$field_name][$previous_language] = array();
+      }
+    }
+  }
+}
+
+/**
+ * Apply fallback rules to the given object.
+ *
+ * Parameters are the same of hook_field_attach_view().
+ */
+function locale_field_fallback_view(&$output, $context) {
+  // Lazily init fallback values and candidates to avoid unnecessary calls.
+  $fallback_values = array();
+  $fallback_candidates = NULL;
+  list(, , $bundle) = field_extract_ids($context['obj_type'], $context['object']);
+
+  foreach (field_info_instances($bundle) as $instance) {
+    $field_name = $instance['field_name'];
+    $field = field_info_field($field_name);
+
+    // If the items array is empty then we have a missing field translation.
+    // @todo: Verify this assumption.
+    if (empty($output[$field_name]['items'])) {
+      if (!isset($fallback_candidates)) {
+        require_once DRUPAL_ROOT . '/includes/language.inc';
+        $fallback_candidates = language_fallback_get_candidates();
+      }
+
+      foreach ($fallback_candidates as $langcode) {
+        // Again if we have a non-empty array we assume the field translation is
+        // valid.
+        if (!empty($context['object']->{$field_name}[$langcode])) {
+          // Cache fallback values per language as fields might have different
+          // fallback values.
+          if (!isset($fallback_values[$langcode])) {
+            $fallback_values[$langcode] = field_attach_view($context['obj_type'], $context['object'], $context['build_mode'], $langcode);
+          }
+          // We are done, skip to the next field.
+          $output[$field_name] = $fallback_values[$langcode][$field_name];
+          break;
+        }
+      }
+    }
+  }
+}
