diff --git a/modules/field/field.module b/modules/field/field.module
index 9b21d3ba419fc5adddf6cf82294aa621d9f42a3f..5370838dba06e5cfed3d4697798d6645ba9c8cfd 100644
--- a/modules/field/field.module
+++ b/modules/field/field.module
@@ -471,98 +471,50 @@ function _field_filter_xss_display_allowed_tags() {
 }
 
 /**
- * Format a field item for display.
+ * Returns a renderable array for a single field value.
  *
- * TODO D7 : do we still need field_format ?
- * - backwards compatibility of templates - check what fallbacks we can propose...
- * - was used by Views integration in CCK in D6 - do we need now?
- * At least needs a little rehaul/update...
- *
- * Used to display a field's values outside the context of the $node, as
- * when fields are displayed in Views, or to display a field in a template
- * using a different formatter than the one set up on the 'Manage display' tab
- * for the node's context.
- *
- * @param $field
- *   Either a field array or the name of the field.
+ * @param $entity_type
+ *   The type of $entity; e.g. 'node' or 'user'.
+ * @param $entity
+ *   The entity containing the field to display. Must at least contain the id
+ *   key and the field data to display.
+ * @param $field_name
+ *   The name of the field to display.
  * @param $item
- *   The field item(s) to be formatted (such as $node->field_foo[0],
- *   or $node->field_foo if the formatter handles multiple values itself)
- * @param $formatter_type
- *   The name of the formatter type to use.
- * @param $node
- *   Optionally, the containing node object for context purposes and
- *   field-instance options.
- *
+ *   The field value to display, as found in
+ *   $entity->field_name[$langcode][$delta].
+ * @param $display
+ *   Can be either the name of a view mode, or an array of display settings.
+ *   See field_view_field() for more information.
+ * @param $langcode
+ *   (Optional) The language of the value in $item. If not provided, the
+ *   current language will be assumed.
  * @return
- *   A string containing the contents of the field item(s) sanitized for display.
- *   It will have been passed through the necessary check_plain() or check_markup()
- *   functions as necessary.
+ *   A renderable array for the field value.
  */
-function field_format($entity_type, $entity, $field, $item, $formatter_type = NULL, $formatter_settings = array()) {
-  if (!is_array($field)) {
-    $field = field_info_field($field);
-  }
-
-  if (field_access('view', $field, $entity_type, $entity)) {
-    $field_type = field_info_field_types($field['type']);
+function field_view_value($entity_type, $entity, $field_name, $item, $display = array(), $langcode = NULL) {
+  $output = array();
 
-    // We need $field, $instance, $entity_type, $entity to be able to display a value...
-    list(, , $bundle) = entity_extract_ids($entity_type, $entity);
-    $instance = field_info_instance($entity_type, $field['field_name'], $bundle);
+  if ($field = field_info_field($field_name)) {
+    $langcode = field_multilingual_valid_language($langcode, FALSE);
 
-    $display = array(
-      'type' => $formatter_type ? $formatter_type : $field_type['default_formatter'],
-      'settings' => $formatter_settings,
-    );
-    $display['settings'] += field_info_formatter_settings($display['type']);
-
-    if ($display['type'] !== 'hidden') {
-      $theme = $formatter['module'] . '_formatter_' . $display['type'];
-
-      $element = array(
-        '#theme' => $theme,
-        '#field_name' => $field['field_name'],
-        '#object_type' => $entity_type,
-        '#bundle' => $bundle,
-        '#formatter' => $display['type'],
-        '#settings' => $display['settings'],
-        '#object' => $entity,
-        '#object_type' => $entity_type,
-        '#delta' => isset($item['#delta']) ? $item['#delta'] : NULL,
-      );
-
-      if (field_behaviors_formatter('multiple values', $display) == FIELD_BEHAVIOR_DEFAULT) {
-        // Single value formatter.
-
-        // hook_field('sanitize') expects an array of items, so we build one.
-        $items = array($item);
-        $function = $field['module'] . '_field_sanitize';
-        if (function_exists($function)) {
-          $function($entity_type, $entity, $field, $instance, $items);
-        }
+    // Determine the langcode that will be used by language fallback.
+    $langcode = current(field_multilingual_available_languages($entity_type, $field, array($langcode)));
 
-        $element['#item'] = $items[0];
-      }
-      else {
-        // Multiple values formatter.
-        $items = $item;
-        $function = $field['module'] . '_field_sanitize';
-        if (function_exists($function)) {
-          $function($entity_type, $entity, $field, $instance, $items);
-        }
+    // Push the item as the single value for the field, and defer to
+    // field_view_field() to build the render array for the whole field.
+    $clone = clone $entity;
+    $clone->{$field_name}[$langcode] = array($item);
+    $elements = field_view_field($entity_type, $clone, $field_name, $display, $langcode);
 
-        foreach ($items as $delta => $item) {
-          $element[$delta] = array(
-            '#item' => $item,
-            '#weight' => $delta,
-          );
-        }
-      }
-
-      return theme($theme, $element);
+    // Extract the part of the render array we need.
+    $output = isset($elements[0]) ? $elements[0] : array();
+    if (isset($elements['#access'])) {
+      $output['#access'] = $elements['#access'];
     }
   }
+
+  return $output;
 }
 
 /**
@@ -638,12 +590,17 @@ function field_view_field($entity_type, $entity, $field_name, $display = array()
     // 'single field' mode, to reuse the language fallback logic.
     $options = array('field_name' => $field_name, 'language' => field_multilingual_valid_language($langcode, FALSE));
     $null = NULL;
-    list($id) = entity_extract_ids($entity_type, $entity);
 
-    // First let the field types do their preparation.
-    _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $display, $null, $options);
-    // Then let the formatters do their own specific massaging.
-    _field_invoke_multiple_default('prepare_view', $entity_type, array($id => $entity), $display, $null, $options);
+    // Invoke prepare_view steps if needed.
+    if (empty($entity->_field_view_prepared)) {
+      list($id) = entity_extract_ids($entity_type, $entity);
+
+      // First let the field types do their preparation.
+      _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $display, $null, $options);
+      // Then let the formatters do their own specific massaging.
+      _field_invoke_multiple_default('prepare_view', $entity_type, array($id => $entity), $display, $null, $options);
+    }
+
     // Build the renderable array.
     $result = _field_invoke_default('view', $entity_type, $entity, $display, $null, $options);
 
diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test
index f1bc28c71a50baa8658bc8480e2de6fb072d6eb8..dc56f4eb5fd0494cd39c839bfb4b6a0d9e83e056 100644
--- a/modules/field/tests/field.test
+++ b/modules/field/tests/field.test
@@ -925,7 +925,7 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
     $this->instance['display'] = array(
       'full' => array(
         'label' => 'above',
-        'type' => 'field_test_needs_additional_data',
+        'type' => 'field_test_with_prepare_view',
         'settings' => array(
           'test_formatter_setting_additional' => $formatter_setting,
         )
@@ -1805,7 +1805,7 @@ class FieldDisplayAPITestCase extends FieldTestCase {
   /**
    * Test the field_view_field() function.
    */
-  function testFieldBuildField() {
+  function testFieldViewField() {
     // No display settings: check that default display settings are used.
     $output = field_view_field('test_entity', $this->entity, $this->field_name);
     $this->drupalSetContent(drupal_render($output));
@@ -1834,6 +1834,23 @@ class FieldDisplayAPITestCase extends FieldTestCase {
     }
     $this->assertText($setting . '|' . implode('|', $array), t('Values were displayed with expected setting.'));
 
+    // Check the prepare_view steps are invoked.
+    $display = array(
+      'label' => 'hidden',
+      'type' => 'field_test_with_prepare_view',
+      'settings' => array(
+        'test_formatter_setting_additional' => $this->randomName(),
+      ),
+    );
+    $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
+    $view = drupal_render($output);
+    $this->drupalSetContent($view);
+    $setting = $display['settings']['test_formatter_setting_additional'];
+    $this->assertNoText($this->label, t('Label was not displayed.'));
+    foreach ($this->values as $delta => $value) {
+      $this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
     // View mode: check that display settings specified in the instance are
     // used.
     $output = field_view_field('test_entity', $this->entity, $this->field_name, 'full');
@@ -1854,6 +1871,73 @@ class FieldDisplayAPITestCase extends FieldTestCase {
       $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
     }
   }
+
+  /**
+   * Test the field_view_value() function.
+   */
+  function testFieldViewValue() {
+    // No display settings: check that default display settings are used.
+    $settings = field_info_formatter_settings('field_test_default');
+    $setting = $settings['test_formatter_setting'];
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item);
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Check that explicit display settings are used.
+    $display = array(
+      'type' => 'field_test_multiple',
+      'settings' => array(
+        'test_formatter_setting_multiple' => $this->randomName(),
+      ),
+    );
+    $setting = $display['settings']['test_formatter_setting_multiple'];
+    $array = array();
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|0:' .  $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Check that prepare_view steps are invoked.
+    $display = array(
+      'type' => 'field_test_with_prepare_view',
+      'settings' => array(
+        'test_formatter_setting_additional' => $this->randomName(),
+      ),
+    );
+    $setting = $display['settings']['test_formatter_setting_additional'];
+    $array = array();
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, $display);
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' .  $value['value'] . '|' . ($value['value'] + 1), t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // View mode: check that display settings specified in the instance are
+    // used.
+    $setting = $this->instance['display']['full']['settings']['test_formatter_setting'];
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'full');
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+
+    // Unknown view mode: check that display settings for 'full' view mode
+    // are used.
+    $setting = $this->instance['display']['full']['settings']['test_formatter_setting'];
+    foreach ($this->values as $delta => $value) {
+      $item = $this->entity->{$this->field_name}[LANGUAGE_NONE][$delta];
+      $output = field_view_value('test_entity', $this->entity, $this->field_name, $item, 'unknown_view_mode');
+      $this->drupalSetContent(drupal_render($output));
+      $this->assertText($setting . '|' . $value['value'], t('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
+    }
+  }
 }
 
 class FieldCrudTestCase extends FieldTestCase {
diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc
index 1213d4d4dab5716b80b09cd3393a79a8c3b382f7..faf1b1469739234cb27cabfbac5a32ed8344ab23 100644
--- a/modules/field/tests/field_test.field.inc
+++ b/modules/field/tests/field_test.field.inc
@@ -248,7 +248,7 @@ function field_test_field_formatter_info() {
         'test_formatter_setting_multiple' => 'dummy test string',
       ),
     ),
-    'field_test_needs_additional_data' => array(
+    'field_test_with_prepare_view' => array(
       'label' => t('Tests hook_field_formatter_prepare_view()'),
       'field types' => array('test_field'),
       'settings' => array(
@@ -264,8 +264,8 @@ function field_test_field_formatter_info() {
 function field_test_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
   foreach ($items as $id => $item) {
     // To keep the test non-intrusive, only act on the
-    // 'field_test_needs_additional_data' formatter.
-    if ($displays[$id]['type'] == 'field_test_needs_additional_data') {
+    // 'field_test_with_prepare_view' formatter.
+    if ($displays[$id]['type'] == 'field_test_with_prepare_view') {
       foreach ($item as $delta => $value) {
         // Don't add anything on empty values.
         if ($value) {
@@ -290,7 +290,7 @@ function field_test_field_formatter_view($entity_type, $entity, $field, $instanc
       }
       break;
 
-    case 'field_test_needs_additional_data':
+    case 'field_test_with_prepare_view':
       foreach ($items as $delta => $item) {
         $element[$delta] = array('#markup' => $settings['test_formatter_setting_additional'] . '|' . $item['value'] . '|' . $item['additional_formatter_value']);
       }