diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 0a7b261..0b1c1af 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1356,7 +1356,7 @@ function comment_node_predelete(Node $node) {
 /**
  * Implements hook_node_update_index().
  */
-function comment_node_update_index(Node $node) {
+function comment_node_update_index(Node $node, $langcode) {
   $index_comments = &drupal_static(__FUNCTION__);
 
   if ($index_comments === NULL) {
@@ -1384,7 +1384,7 @@ function comment_node_update_index(Node $node) {
     if ($node->comment && $cids = comment_get_thread($node, $mode, $comments_per_page)) {
       $comments = comment_load_multiple($cids);
       comment_prepare_thread($comments);
-      $build = comment_view_multiple($comments, $node);
+      $build = comment_view_multiple($comments, $node, $langcode);
       return drupal_render($build);
     }
   }
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index f4fb5f5..4ac7f0e 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -658,6 +658,8 @@ function hook_node_prepare(Drupal\node\Node $node) {
  *
  * @param Drupal\node\Node $node
  *   The node being displayed in a search result.
+ * @param $langcode
+ *   Language code of result being displayed.
  *
  * @return array
  *   Extra information to be displayed with search result. This information
@@ -670,7 +672,7 @@ function hook_node_prepare(Drupal\node\Node $node) {
  *
  * @ingroup node_api_hooks
  */
-function hook_node_search_result(Drupal\node\Node $node) {
+function hook_node_search_result(Drupal\node\Node $node, $langcode) {
   $comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->nid))->fetchField();
   return array('comment' => format_plural($comments, '1 comment', '@count comments'));
 }
@@ -722,13 +724,15 @@ function hook_node_update(Drupal\node\Node $node) {
  *
  * @param Drupal\node\Node $node
  *   The node being indexed.
+ * @param $langcode
+ *   Language code of the variant of the node being indexed.
  *
  * @return string
  *   Additional node information to be indexed.
  *
  * @ingroup node_api_hooks
  */
-function hook_node_update_index(Drupal\node\Node $node) {
+function hook_node_update_index(Drupal\node\Node $node, $langcode) {
   $text = '';
   $comments = db_query('SELECT subject, comment, format FROM {comment} WHERE nid = :nid AND status = :status', array(':nid' => $node->nid, ':status' => COMMENT_PUBLISHED));
   foreach ($comments as $comment) {
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index d4bd4a4..ddc391f 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1619,28 +1619,32 @@ function node_search_execute($keys = NULL, $conditions = NULL) {
 
   // Load results.
   $find = $query
+    // Add the language code of the indexed item to the result of the query, since the node
+    // will be rendered using the respective language.
+    ->fields('i', array('langcode'))
     ->limit(10)
     ->execute();
   $results = array();
   foreach ($find as $item) {
     // Render the node.
     $node = node_load($item->sid);
-    $build = node_view($node, 'search_result');
+    $build = node_view($node, 'search_result', $item->langcode);
     unset($build['#theme']);
     $node->rendered = drupal_render($build);
 
     // Fetch comments for snippet.
-    $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
+    $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node, $item->langcode);
 
-    $extra = module_invoke_all('node_search_result', $node);
+    $extra = module_invoke_all('node_search_result', $node, $item->langcode);
 
+    $language = language_load($item->langcode);
     $uri = entity_uri('node', $node);
     $results[] = array(
-      'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE))),
+      'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE, 'language' => $language))),
       'type' => check_plain(node_type_get_name($node)),
-      'title' => $node->label(),
+      'title' => $node->label($item->langcode),
       'user' => theme('username', array('account' => $node)),
-      'date' => $node->changed,
+      'date' => $node->get('changed', $item->langcode),
       'node' => $node,
       'extra' => $extra,
       'score' => $item->calculated_score,
@@ -2653,7 +2657,14 @@ function node_update_index() {
     return;
   }
 
+  // The indexing throttle should be aware of the number of language variants of a node.
+  $counter = 0;
   foreach (node_load_multiple($nids) as $node) {
+    // Determine when the maximum number of indexable items is reached.
+    $counter += 1 + count($node->translations());
+    if ($counter > $limit) {
+      break;
+    }
     _node_index_node($node);
   }
 }
@@ -2670,21 +2681,26 @@ function _node_index_node(Node $node) {
   // results half-life calculation.
   variable_set('node_cron_last', $node->changed);
 
-  // Render the node.
-  $build = node_view($node, 'search_index');
-  unset($build['#theme']);
-  $node->rendered = drupal_render($build);
+  $languages = array_merge(array(language_load($node->langcode)), $node->translations());
 
-  $text = '<h1>' . check_plain($node->label()) . '</h1>' . $node->rendered;
+  foreach ($languages as $language) {
+    // Render the node.
+    $build = node_view($node, 'search_index', $language->langcode);
 
-  // Fetch extra data normally not visible
-  $extra = module_invoke_all('node_update_index', $node);
-  foreach ($extra as $t) {
-    $text .= $t;
-  }
+    unset($build['#theme']);
+    $node->rendered = drupal_render($build);
+
+    $text = '<h1>' . check_plain($node->label($language->langcode)) . '</h1>' . $node->rendered;
 
-  // Update index
-  search_index($node->nid, 'node', $text);
+    // Fetch extra data normally not visible.
+    $extra = module_invoke_all('node_update_index', $node, $language->langcode);
+    foreach ($extra as $t) {
+      $text .= $t;
+    }
+
+    // Update index.
+    search_index($node->nid, 'node', $text, $language->langcode);
+  }
 }
 
 /**
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php
index 1253cb3..118cbf7 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchMatchTest.php
@@ -37,10 +37,10 @@ class SearchMatchTest extends SearchTestBase {
     config('search.settings')->set('index.minimum_word_size', 3)->save();
 
     for ($i = 1; $i <= 7; ++$i) {
-      search_index($i, SEARCH_TYPE, $this->getText($i));
+      search_index($i, SEARCH_TYPE, $this->getText($i), LANGUAGE_NOT_SPECIFIED);
     }
     for ($i = 1; $i <= 5; ++$i) {
-      search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i));
+      search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i), LANGUAGE_NOT_SPECIFIED);
     }
     // No getText builder function for Japanese text; just a simple array.
     foreach (array(
@@ -48,7 +48,7 @@ class SearchMatchTest extends SearchTestBase {
       14 => 'ドルーパルが大好きよ！',
       15 => 'コーヒーとケーキ',
     ) as $i => $jpn) {
-      search_index($i, SEARCH_TYPE_JPN, $jpn);
+      search_index($i, SEARCH_TYPE_JPN, $jpn, LANGUAGE_NOT_SPECIFIED);
     }
     search_update_totals();
   }
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
new file mode 100644
index 0000000..5c5640b
--- /dev/null
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\search\Tests\SearchMultilingualEntityTest.
+ */
+
+namespace Drupal\search\Tests;
+
+/**
+ * Tests entities with multilingual fields.
+ */
+class SearchMultilingualEntityTest extends SearchTestBase {
+
+  protected $searchable_nodes = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Multilingual entities',
+      'description' => 'Tests entities with multilingual fields.',
+      'group' => 'Search',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('language', 'locale');
+
+    // Add two new languages.
+    $language = (object) array(
+      'langcode' => 'hu',
+      'name' => 'Hungarian',
+    );
+    language_save($language);
+    $language = (object) array(
+      'langcode' => 'sv',
+      'name' => 'Swedish',
+    );
+    language_save($language);
+
+    // Make the body field translatable.
+    // (The parent class has already created the article and page content types.)
+    $field = field_info_field('body');
+    $field['translatable'] = TRUE;
+    field_update_field($field);
+
+    // Create a few page nodes with multilingual body values.
+    $default_format = filter_default_format();
+    $nodes = array(
+      array(
+        'type' => 'page',
+        'body' => array(
+          'en' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+        ),
+        'langcode' => 'en',
+      ),
+      array(
+        'type' => 'page',
+        'body' => array(
+          'en' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+          'hu' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+        ),
+        'langcode' => 'en',
+      ),
+      array(
+        'type' => 'page',
+        'body' => array(
+          'en' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+          'hu' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+          'sv' => array(array('value' => $this->randomName(32), 'format' => $default_format)),
+        ),
+        'langcode' => 'en',
+      ),
+    );
+    $this->searchable_nodes = array();
+    foreach ($nodes as $node) {
+      $this->searchable_nodes[] = $this->drupalCreateNode($node);
+    }
+  }
+
+  function testIndexingThrottle() {
+    // Index only 4 items per cron run.
+    config('search.settings')->set('index.cron_limit', 4)->save();
+    // Update the index and then run the shutdown method.
+    node_update_index();
+    search_update_totals();
+    $status = module_invoke('node', 'search_status');
+    $this->assertEqual($status['remaining'], 1, t('Remaining items after updating the search index is 1.'));
+  }
+
+  function testSearchingMultilingualFieldValues() {
+    // Update the index and then run the shutdown method.
+    node_update_index();
+    search_update_totals();
+    foreach ($this->searchable_nodes as $node) {
+      // Each searchable node that we created contains values in the body field in one or more
+      // languages. Let's pick the last language variant from the body array and execute a search
+      // using that as a search keyword.
+      $body_language_variant = end($node->body);
+      $search_result = node_search_execute($body_language_variant[0]['value']);
+      // See whether we get the same node as a result.
+      $this->assertEqual($search_result[0]['node']->nid, $node->nid, t('The search has resulted the correct node.'));
+    }
+  }
+}
+
diff --git a/core/modules/search/search.api.php b/core/modules/search/search.api.php
index 01b3d21..40ac0d5 100644
--- a/core/modules/search/search.api.php
+++ b/core/modules/search/search.api.php
@@ -206,7 +206,7 @@ function hook_search_execute($keys = NULL, $conditions = NULL) {
 
   // Insert special keywords.
   $query->setOption('type', 'n.type');
-  $query->setOption('language', 'n.language');
+  $query->setOption('langcode', 'n.langcode');
   if ($query->setOption('term', 'ti.tid')) {
     $query->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
   }
@@ -224,28 +224,30 @@ function hook_search_execute($keys = NULL, $conditions = NULL) {
     ->execute();
   $results = array();
   foreach ($find as $item) {
-    // Build the node body.
+    // Render the node.
     $node = node_load($item->sid);
-    node_build_content($node, 'search_result');
-    $node->body = drupal_render($node->content);
+    $build = node_view($node, 'search_result', $item->langcode);
+    unset($build['#theme']);
+    $node->rendered = drupal_render($build);
 
     // Fetch comments for snippet.
-    $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
-    // Fetch terms for snippet.
-    $node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node);
+    $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node, $item->langcode);
 
-    $extra = module_invoke_all('node_search_result', $node);
+    $extra = module_invoke_all('node_search_result', $node, $item->langcode);
 
+    $language = language_load($item->langcode);
+    $uri = entity_uri('node', $node);
     $results[] = array(
-      'link' => url('node/' . $item->sid, array('absolute' => TRUE)),
+      'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE, 'language' => $language))),
       'type' => check_plain(node_type_get_name($node)),
-      'title' => $node->label(),
+      'title' => $node->label($item->langcode),
       'user' => theme('username', array('account' => $node)),
-      'date' => $node->changed,
+      'date' => $node->get('changed', $item->langcode),
       'node' => $node,
       'extra' => $extra,
       'score' => $item->calculated_score,
-      'snippet' => search_excerpt($keys, $node->body),
+      'snippet' => search_excerpt($keys, $node->rendered),
+      'langcode' => $node->langcode,
     );
   }
   return $results;
diff --git a/core/modules/search/search.install b/core/modules/search/search.install
index a561509..cc17523 100644
--- a/core/modules/search/search.install
+++ b/core/modules/search/search.install
@@ -19,6 +19,12 @@ function search_schema() {
         'default' => 0,
         'description' => 'Search item ID, e.g. node ID for nodes.',
       ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => '12',
+        'not null' => TRUE,
+        'description' => 'The {languages}.langcode of the item variant.',
+      ),
       'type' => array(
         'type' => 'varchar',
         'length' => 16,
@@ -39,7 +45,7 @@ function search_schema() {
         'description' => 'Set to force node reindexing.',
       ),
     ),
-    'primary key' => array('sid', 'type'),
+    'primary key' => array('sid', 'langcode', 'type'),
   );
 
   $schema['search_index'] = array(
@@ -59,6 +65,12 @@ function search_schema() {
         'default' => 0,
         'description' => 'The {search_dataset}.sid of the searchable item to which the word belongs.',
       ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => '12',
+        'not null' => TRUE,
+        'description' => 'The {languages}.langcode of the item variant.',
+      ),
       'type' => array(
         'type' => 'varchar',
         'length' => 16,
@@ -72,7 +84,7 @@ function search_schema() {
       ),
     ),
     'indexes' => array(
-      'sid_type' => array('sid', 'type'),
+      'sid_type' => array('sid', 'langcode', 'type'),
     ),
     'foreign keys' => array(
       'search_dataset' => array(
@@ -83,7 +95,7 @@ function search_schema() {
         ),
       ),
     ),
-    'primary key' => array('word', 'sid', 'type'),
+    'primary key' => array('word', 'sid', 'langcode', 'type'),
   );
 
   $schema['search_total'] = array(
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index a77ccfd..1de3c46 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -315,20 +315,33 @@ function _search_menu_access($name) {
  * @param $module
  *   (optional) The machine-readable name of the module for the item to remove
  *   from the search index.
+ * @param $reindex
+ *   (optional) Boolean to specify whether reindexing happens.
+ * @param $langcode
+ *   (optional) Language code for the operation. If not provided, all
+ *   index records for the $sid will be deleted.
  */
-function search_reindex($sid = NULL, $module = NULL, $reindex = FALSE) {
+function search_reindex($sid = NULL, $module = NULL, $reindex = FALSE, $langcode = NULL) {
   if ($module == NULL && $sid == NULL) {
     module_invoke_all('search_reset');
   }
   else {
-    db_delete('search_dataset')
+    $query = db_delete('search_dataset')
       ->condition('sid', $sid)
-      ->condition('type', $module)
-      ->execute();
-    db_delete('search_index')
+      ->condition('type', $module);
+    if (!empty($langcode)) {
+      $query->condition('langcode', $langcode);
+    }
+    $query->execute();
+
+    $query = db_delete('search_index')
       ->condition('sid', $sid)
-      ->condition('type', $module)
-      ->execute();
+      ->condition('type', $module);
+    if (!empty($langcode)) {
+      $query->condition('langcode', $langcode);
+    }
+    $query->execute();
+
     // Don't remove links if re-indexing.
     if (!$reindex) {
       db_delete('search_node_links')
@@ -557,10 +570,12 @@ function search_invoke_preprocess(&$text) {
  *   that implements hook_search_info()).
  * @param $text
  *   The content of this item. Must be a piece of HTML or plain text.
+ * @param $langcode
+ *   Language code for text being indexed.
  *
  * @ingroup search
  */
-function search_index($sid, $module, $text) {
+function search_index($sid, $module, $text, $langcode) {
   $minimum_word_size = config('search.settings')->get('index.minimum_word_size');
 
   // Link matching
@@ -690,12 +705,13 @@ function search_index($sid, $module, $text) {
     $tag = !$tag;
   }
 
-  search_reindex($sid, $module, TRUE);
+  search_reindex($sid, $module, TRUE, $langcode);
 
   // Insert cleaned up data into dataset
   db_insert('search_dataset')
     ->fields(array(
       'sid' => $sid,
+      'langcode' => $langcode,
       'type' => $module,
       'data' => $accum,
       'reindex' => 0,
@@ -711,6 +727,7 @@ function search_index($sid, $module, $text) {
       ->key(array(
         'word' => $word,
         'sid' => $sid,
+        'langcode' => $langcode,
         'type' => $module,
       ))
       ->fields(array('score' => $score))
