diff --git a/core/modules/block/block.js b/core/modules/block/block.js
index 72b5673..67cbb31 100644
--- a/core/modules/block/block.js
+++ b/core/modules/block/block.js
@@ -32,6 +32,17 @@ Drupal.behaviors.blockSettingsSummary = {
       return vals.join(', ');
     });
 
+    $('fieldset#edit-langcode', context).drupalSetSummary(function (context) {
+      var vals = [];
+      $('input[type="checkbox"]:checked', context).each(function () {
+        vals.push($.trim($(this).next('label').text()));
+      });
+      if (!vals.length) {
+        vals.push(Drupal.t('Not restricted'));
+      }
+      return vals.join(', ');
+    });
+
     $('fieldset#edit-role', context).drupalSetSummary(function (context) {
       var vals = [];
       $('input[type="checkbox"]:checked', context).each(function () {
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
index ecf637d..055c5ed 100644
--- a/core/modules/language/language.install
+++ b/core/modules/language/language.install
@@ -71,6 +71,33 @@ function language_schema() {
       'list' => array('weight', 'name'),
     ),
   );
+  $schema['block_language'] = array(
+    'description' => 'Sets up display criteria for blocks based on langcode',
+    'fields' => array(
+      'module' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'description' => "The block's origin module, from {block}.module.",
+      ),
+      'delta' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'description' => "The block's unique delta within module, from {block}.delta.",
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'description' => "The machine-readable name of this language from {language}.langcode.",
+      ),
+    ),
+    'primary key' => array('module', 'delta', 'langcode'),
+    'indexes' => array(
+      'langcode' => array('langcode'),
+    ),
+  );
   return $schema;
 }
 
@@ -101,3 +128,39 @@ function language_update_8000() {
     variable_set('language_default', $language_default);
   }
 }
+
+
+/**
+ * Create the {block_language} table to store the langcode visibility settings
+ * per block.
+ */
+function language_update_8001() {
+  $schema = array(
+    'description' => 'Sets up display criteria for blocks based on langcode.',
+    'fields' => array(
+      'module' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'description' => "The block's origin module, from {block}.module.",
+      ),
+      'delta' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'description' => "The block's unique delta within module, from {block}.delta.",
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'description' => "The machine-readable name of this language from {language}.langcode.",
+      ),
+    ),
+    'primary key' => array('module', 'delta', 'langcode'),
+    'indexes' => array(
+      'langcode' => array('langcode'),
+    ),
+  );
+  db_create_table('block_language', $schema);
+}
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 20f7c62..82748ff 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -186,6 +186,17 @@ function language_delete($langcode) {
 }
 
 /**
+ * Implements hook_modules_uninstalled().
+ *
+ * Cleans up {block_language} table from modules' blocks.
+ */
+function language_modules_uninstalled($modules) {
+  db_delete('block_language')
+    ->condition('module', $modules, 'IN')
+    ->execute();
+}
+
+/**
  * Implements hook_css_alter().
  *
  * This function checks all CSS files currently added via drupal_add_css() and
@@ -211,3 +222,115 @@ function language_css_alter(&$css) {
     }
   }
 }
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds per language block visibility options to block add form.
+ *
+ * @see language_form_block_admin_configure_submit()
+ * @see block_admin_configure()
+ * @see language_form_block_admin_configure_alter()
+ */
+
+function language_form_block_add_block_form_alter(&$form, &$form_state) {
+  language_form_block_admin_configure_alter($form, $form_state);
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds per language block visibility options to block configuration form.
+ *
+ * @see language_form_block_admin_configure_submit()
+ * @see block_admin_configure()
+ * @see language_form_block_add_block_form_alter()
+ */
+function language_form_block_admin_configure_alter(&$form, &$form_state) {
+  $default_langcode_options = db_query("SELECT langcode FROM {block_language} WHERE module = :module AND delta = :delta", array(
+    ':module' => $form['module']['#value'],
+    ':delta' => $form['delta']['#value'],
+  ))->fetchCol();
+
+  if ($default_langcode_options === FALSE) {
+    $default_langcode_options = array();
+  }
+
+  // Fetch the enabled languages.
+  $enabled_languages = language_list(TRUE);
+  foreach ($enabled_languages as $language) {
+    $langcodes_options[$language->langcode] = t($language->name);
+  }
+  $form['visibility']['langcode'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Languages'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#group' => 'visibility',
+    '#weight' => 5,
+    '#access' => language_multilingual(),
+  );
+  $form['visibility']['langcode']['langcodes'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Show block for specific languages'),
+    '#default_value' => $default_langcode_options,
+    '#options' => $langcodes_options,
+    '#description' => t('Show this block only for the selected language(s). If you select no languages, the block will be visible to all users.'),
+  );
+  $form['#submit'][] = 'language_form_block_admin_configure_submit';
+}
+
+/**
+ * Form submission handler for locale_form_block_admin_configure_alter().
+ */
+function language_form_block_admin_configure_submit($form, &$form_state) {
+  db_delete('block_language')
+    ->condition('module', $form_state['values']['module'])
+    ->condition('delta', $form_state['values']['delta'])
+    ->execute();
+  $query = db_insert('block_language')->fields(array(
+    'langcode', 'module', 'delta'
+  ));
+  foreach (array_filter($form_state['values']['langcodes']) as $langcode) {
+    $query->values(array(
+      'langcode' => $langcode,
+      'module' => $form_state['values']['module'],
+      'delta' => $form_state['values']['delta'],
+    ));
+  }
+  $query->execute();
+}
+
+/**
+ * Implements hook_block_list_alter().
+ *
+ * Hide the blocks that have been disabled for langcodes visibility setting.
+ */
+function language_block_list_alter(&$blocks) {
+  global $language_interface, $theme_key;
+
+  $result = db_query('SELECT module, delta, langcode FROM {block_language}');
+  $block_langcodes = array();
+  foreach ($result as $record) {
+    $block_langcodes[$record->module][$record->delta][$record->langcode] = TRUE;
+  }
+  foreach ($blocks as $key => $block) {
+    // Any module using this alter should inspect the data before changing it,
+    // to ensure it is what they expect.
+    if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) {
+      // This block was added by a contrib module, leave it in the list.
+      continue;
+    }
+
+    if (!isset($block_langcodes[$block->module][$block->delta])) {
+      // No language setting for this block, leave it in the list.
+      continue;
+    }
+
+    if (!isset($block_langcodes[$block->module][$block->delta][$language_interface->langcode])) {
+      // This block should not be displayed with the active language, remove
+      // from the list.
+      unset($blocks[$key]);
+    }
+  }
+}
