diff --git a/core/includes/locale.inc b/core/includes/locale.inc
index 1f9567b..3cf457c 100644
--- a/core/includes/locale.inc
+++ b/core/includes/locale.inc
@@ -6,38 +6,6 @@
  */
 
 /**
- * The language is determined using a URL language indicator:
- * path prefix or domain according to the configuration.
- */
-const LANGUAGE_NEGOTIATION_URL = 'locale-url';
-
-/**
- * The language is set based on the browser language settings.
- */
-const LANGUAGE_NEGOTIATION_BROWSER = 'locale-browser';
-
-/**
- * The language is determined using the current interface language.
- */
-const LANGUAGE_NEGOTIATION_INTERFACE = 'locale-interface';
-
-/**
- * If no URL language is available language is determined using an already
- * detected one.
- */
-const LANGUAGE_NEGOTIATION_URL_FALLBACK = 'locale-url-fallback';
-
-/**
- * The language is set based on the user language settings.
- */
-const LANGUAGE_NEGOTIATION_USER = 'locale-user';
-
-/**
- * The language is set based on the request/session parameters.
- */
-const LANGUAGE_NEGOTIATION_SESSION = 'locale-session';
-
-/**
  * Regular expression pattern used to localize JavaScript strings.
  */
 const LOCALE_JS_STRING = '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+';
@@ -85,452 +53,6 @@ const LOCALE_IMPORT_OVERWRITE = 0;
 const LOCALE_IMPORT_KEEP = 1;
 
 /**
- * URL language negotiation: use the path prefix as URL language
- * indicator.
- */
-const LANGUAGE_NEGOTIATION_URL_PREFIX = 0;
-
-/**
- * URL language negotiation: use the domain as URL language
- * indicator.
- */
-const LANGUAGE_NEGOTIATION_URL_DOMAIN = 1;
-
-/**
- * @defgroup locale-languages-negotiation Language negotiation options
- * @{
- * Functions for language negotiation.
- *
- * There are functions that provide the ability to identify the
- * language. This behavior can be controlled by various options.
- */
-
-/**
- * Identifies the language from the current interface language.
- *
- * @return
- *   The current interface language code.
- */
-function locale_language_from_interface() {
-  global $language_interface;
-  return isset($language_interface->langcode) ? $language_interface->langcode : FALSE;
-}
-
-/**
- * Identify language from the Accept-language HTTP header we got.
- *
- * We perform browser accept-language parsing only if page cache is disabled,
- * otherwise we would cache a user-specific preference.
- *
- * @param $languages
- *   An array of language objects for enabled languages ordered by weight.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function locale_language_from_browser($languages) {
-  if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
-    return FALSE;
-  }
-
-  // The Accept-Language header contains information about the language
-  // preferences configured in the user's browser / operating system.
-  // RFC 2616 (section 14.4) defines the Accept-Language header as follows:
-  //   Accept-Language = "Accept-Language" ":"
-  //                  1#( language-range [ ";" "q" "=" qvalue ] )
-  //   language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
-  // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
-  $browser_langcodes = array();
-  if (preg_match_all('@([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) {
-    foreach ($matches as $match) {
-      // We can safely use strtolower() here, tags are ASCII.
-      // RFC2616 mandates that the decimal part is no more than three digits,
-      // so we multiply the qvalue by 1000 to avoid floating point comparisons.
-      $langcode = strtolower($match[1]);
-      $qvalue = isset($match[2]) ? (float) $match[2] : 1;
-      $browser_langcodes[$langcode] = (int) ($qvalue * 1000);
-    }
-  }
-
-  // We should take pristine values from the HTTP headers, but Internet Explorer
-  // from version 7 sends only specific language tags (eg. fr-CA) without the
-  // corresponding generic tag (fr) unless explicitly configured. In that case,
-  // we assume that the lowest value of the specific tags is the value of the
-  // generic language to be as close to the HTTP 1.1 spec as possible.
-  // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and
-  // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx
-  asort($browser_langcodes);
-  foreach ($browser_langcodes as $langcode => $qvalue) {
-    $generic_tag = strtok($langcode, '-');
-    if (!isset($browser_langcodes[$generic_tag])) {
-      $browser_langcodes[$generic_tag] = $qvalue;
-    }
-  }
-
-  // Find the enabled language with the greatest qvalue, following the rules
-  // of RFC 2616 (section 14.4). If several languages have the same qvalue,
-  // prefer the one with the greatest weight.
-  $best_match_langcode = FALSE;
-  $max_qvalue = 0;
-  foreach ($languages as $langcode => $language) {
-    // Language tags are case insensitive (RFC2616, sec 3.10).
-    $langcode = strtolower($langcode);
-
-    // If nothing matches below, the default qvalue is the one of the wildcard
-    // language, if set, or is 0 (which will never match).
-    $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0;
-
-    // Find the longest possible prefix of the browser-supplied language
-    // ('the language-range') that matches this site language ('the language tag').
-    $prefix = $langcode;
-    do {
-      if (isset($browser_langcodes[$prefix])) {
-        $qvalue = $browser_langcodes[$prefix];
-        break;
-      }
-    }
-    while ($prefix = substr($prefix, 0, strrpos($prefix, '-')));
-
-    // Find the best match.
-    if ($qvalue > $max_qvalue) {
-      $best_match_langcode = $language->langcode;
-      $max_qvalue = $qvalue;
-    }
-  }
-
-  return $best_match_langcode;
-}
-
-/**
- * Identify language from the user preferences.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function locale_language_from_user($languages) {
-  // User preference (only for logged users).
-  global $user;
-
-  if ($user->uid && !empty($user->preferred_langcode)) {
-    return $user->preferred_langcode;
-  }
-
-  // No language preference from the user.
-  return FALSE;
-}
-
-/**
- * Identify language from a request/session parameter.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function locale_language_from_session($languages) {
-  $param = variable_get('locale_language_negotiation_session_param', 'language');
-
-  // Request parameter: we need to update the session parameter only if we have
-  // an authenticated user.
-  if (isset($_GET[$param]) && isset($languages[$langcode = $_GET[$param]])) {
-    global $user;
-    if ($user->uid) {
-      $_SESSION[$param] = $langcode;
-    }
-    return $langcode;
-  }
-
-  // Session parameter.
-  if (isset($_SESSION[$param])) {
-    return $_SESSION[$param];
-  }
-
-  return FALSE;
-}
-
-/**
- * Identify language via URL prefix or domain.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function locale_language_from_url($languages) {
-  $language_url = FALSE;
-
-  if (!language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_URL)) {
-    return $language_url;
-  }
-
-  switch (variable_get('locale_language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX)) {
-    case LANGUAGE_NEGOTIATION_URL_PREFIX:
-      // $_GET['q'] might not be available at this time, because
-      // path initialization runs after the language bootstrap phase.
-      list($language, $_GET['q']) = language_url_split_prefix(isset($_GET['q']) ? $_GET['q'] : NULL, $languages);
-      if ($language !== FALSE) {
-        $language_url = $language->langcode;
-      }
-      break;
-
-    case LANGUAGE_NEGOTIATION_URL_DOMAIN:
-      $domains = locale_language_negotiation_url_domains();
-      foreach ($languages as $language) {
-        // Skip check if the language doesn't have a domain.
-        if (!empty($domains[$language->langcode])) {
-          // Only compare the domains not the protocols or ports.
-          // Remove protocol and add http:// so parse_url works
-          $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->langcode]);
-          $host = parse_url($host, PHP_URL_HOST);
-          if ($_SERVER['HTTP_HOST'] == $host) {
-            $language_url = $language->langcode;
-            break;
-          }
-        }
-      }
-      break;
-  }
-
-  return $language_url;
-}
-
-/**
- * Determines the language to be assigned to URLs when none is detected.
- *
- * The language negotiation process has a fallback chain that ends with the
- * default language negotiation method. Each built-in language type has a
- * separate initialization:
- * - Interface language, which is the only configurable one, always gets a valid
- *   value. If no request-specific language is detected, the default language
- *   will be used.
- * - Content language merely inherits the interface language by default.
- * - URL language is detected from the requested URL and will be used to rewrite
- *   URLs appearing in the page being rendered. If no language can be detected,
- *   there are two possibilities:
- *   - If the default language has no configured path prefix or domain, then the
- *     default language is used. This guarantees that (missing) URL prefixes are
- *     preserved when navigating through the site.
- *   - If the default language has a configured path prefix or domain, a
- *     requested URL having an empty prefix or domain is an anomaly that must be
- *     fixed. This is done by introducing a prefix or domain in the rendered
- *     page matching the detected interface language.
- *
- * @param $languages
- *   (optional) An array of valid language objects. This is passed by
- *   language_negotiation_method_invoke() to every language method callback,
- *   but it is not actually needed here. Defaults to NULL.
- * @param $language_type
- *   (optional) The language type to fall back to. Defaults to the interface
- *   language.
- *
- * @return
- *   A valid language code.
- */
-function locale_language_url_fallback($language = NULL, $language_type = LANGUAGE_TYPE_INTERFACE) {
-  $default = language_default();
-  $prefix = (variable_get('locale_language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX) == LANGUAGE_NEGOTIATION_URL_PREFIX);
-
-  // If the default language is not configured to convey language information,
-  // a missing URL language information indicates that URL language should be
-  // the default one, otherwise we fall back to an already detected language.
-  $domains = locale_language_negotiation_url_domains();
-  $prefixes = locale_language_negotiation_url_prefixes();
-  if (($prefix && empty($prefixes[$default->langcode])) || (!$prefix && empty($domains[$default->langcode]))) {
-    return $default->langcode;
-  }
-  else {
-    return $GLOBALS[$language_type]->langcode;
-  }
-}
-
-/**
- * Return links for the URL language switcher block.
- *
- * Translation links may be provided by other modules.
- */
-function locale_language_switcher_url($type, $path) {
-  // Get the enabled languages only.
-  $languages = language_list(TRUE);
-  $links = array();
-
-  foreach ($languages as $language) {
-    $links[$language->langcode] = array(
-      'href'       => $path,
-      'title'      => $language->name,
-      'language'   => $language,
-      'attributes' => array('class' => array('language-link')),
-    );
-  }
-
-  return $links;
-}
-
-/**
- * Return the session language switcher block.
- */
-function locale_language_switcher_session($type, $path) {
-  drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css');
-
-  $param = variable_get('locale_language_negotiation_session_param', 'language');
-  $language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : $GLOBALS[$type]->langcode;
-
-  // Get the enabled languages only.
-  $languages = language_list(TRUE);
-  $links = array();
-
-  $query = $_GET;
-  unset($query['q']);
-
-  foreach ($languages as $language) {
-    $langcode = $language->langcode;
-    $links[$langcode] = array(
-      'href'       => $path,
-      'title'      => $language->name,
-      'attributes' => array('class' => array('language-link')),
-      'query'      => $query,
-    );
-    if ($language_query != $langcode) {
-      $links[$langcode]['query'][$param] = $langcode;
-    }
-    else {
-      $links[$langcode]['attributes']['class'][] = ' session-active';
-    }
-  }
-
-  return $links;
-}
-
-/**
- * Rewrite URLs for the URL language negotiation method.
- */
-function locale_language_url_rewrite_url(&$path, &$options) {
-  static $drupal_static_fast;
-  if (!isset($drupal_static_fast)) {
-    $drupal_static_fast['languages'] = &drupal_static(__FUNCTION__);
-  }
-  $languages = &$drupal_static_fast['languages'];
-
-  if (!isset($languages)) {
-    // Get the enabled languages only.
-    $languages = language_list(TRUE);
-    $languages = array_flip(array_keys($languages));
-  }
-
-  // Language can be passed as an option, or we go for current URL language.
-  if (!isset($options['language'])) {
-    global $language_url;
-    $options['language'] = $language_url;
-  }
-  // We allow only enabled languages here.
-  elseif (!isset($languages[$options['language']->langcode])) {
-    unset($options['language']);
-    return;
-  }
-
-  if (isset($options['language'])) {
-    switch (variable_get('locale_language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX)) {
-      case LANGUAGE_NEGOTIATION_URL_DOMAIN:
-        $domains = locale_language_negotiation_url_domains();
-        if (!empty($domains[$options['language']->langcode])) {
-          // Ask for an absolute URL with our modified base_url.
-          global $is_https;
-          $url_scheme = ($is_https) ? 'https://' : 'http://';
-          $options['absolute'] = TRUE;
-          $options['base_url'] = $url_scheme . $domains[$options['language']->langcode];
-          if (isset($options['https']) && variable_get('https', FALSE)) {
-            if ($options['https'] === TRUE) {
-              $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
-            }
-            elseif ($options['https'] === FALSE) {
-              $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
-            }
-          }
-        }
-        break;
-
-      case LANGUAGE_NEGOTIATION_URL_PREFIX:
-        $prefixes = locale_language_negotiation_url_prefixes();
-        if (!empty($prefixes[$options['language']->langcode])) {
-          $options['prefix'] = $prefixes[$options['language']->langcode] . '/';
-        }
-        break;
-    }
-  }
-}
-
-/**
- * Reads language prefixes and uses the langcode if no prefix is set.
- */
-function locale_language_negotiation_url_prefixes() {
-  return variable_get('locale_language_negotiation_url_prefixes', array());
-}
-
-/**
- * Saves language prefix settings.
- */
-function locale_language_negotiation_url_prefixes_save(array $prefixes) {
-  variable_set('locale_language_negotiation_url_prefixes', $prefixes);
-}
-
-/**
- * Reads language domains.
- */
-function locale_language_negotiation_url_domains() {
-  return variable_get('locale_language_negotiation_url_domains', array());
-}
-
-/**
- * Saves the language domain settings.
- */
-function locale_language_negotiation_url_domains_save(array $domains) {
-  variable_set('locale_language_negotiation_url_domains', $domains);
-}
-
-/**
- * Rewrite URLs for the Session language negotiation method.
- */
-function locale_language_url_rewrite_session(&$path, &$options) {
-  static $query_rewrite, $query_param, $query_value;
-
-  // The following values are not supposed to change during a single page
-  // request processing.
-  if (!isset($query_rewrite)) {
-    global $user;
-    if (!$user->uid) {
-      // Get the enabled languages only.
-      $languages = language_list(TRUE);
-      $query_param = check_plain(variable_get('locale_language_negotiation_session_param', 'language'));
-      $query_value = isset($_GET[$query_param]) ? check_plain($_GET[$query_param]) : NULL;
-      $query_rewrite = isset($languages[$query_value]) && language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_SESSION);
-    }
-    else {
-      $query_rewrite = FALSE;
-    }
-  }
-
-  // If the user is anonymous, the user language negotiation method is enabled,
-  // and the corresponding option has been set, we must preserve any explicit
-  // user language preference even with cookies disabled.
-  if ($query_rewrite) {
-    if (is_string($options['query'])) {
-      $options['query'] = drupal_get_query_array($options['query']);
-    }
-    if (!isset($options['query'][$query_param])) {
-      $options['query'][$query_param] = $query_value;
-    }
-  }
-}
-
-/**
- * @} End of "locale-languages-negotiation"
- */
-
-/**
  * Check that a string is safe to be added or imported as a translation.
  *
  * This test can be used to detect possibly bad translation strings. It should
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index 33bc147..7aa78ac 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -436,3 +436,372 @@ function language_admin_predefined_list() {
   asort($predefined);
   return $predefined;
 }
+
+/**
+ * Builds the configuration form for language negotiation.
+ */
+function language_negotiation_configure_form() {
+  include_once DRUPAL_ROOT . '/core/includes/language.inc';
+
+  $form = array(
+    '#submit' => array('language_negotiation_configure_form_submit'),
+    '#theme' => 'language_negotiation_configure_form',
+    '#language_types' => language_types_get_configurable(FALSE),
+    '#language_types_info' => language_types_info(),
+    '#language_negotiation_info' => language_negotiation_info(),
+  );
+
+  foreach ($form['#language_types'] as $type) {
+    language_negotiation_configure_form_table($form, $type);
+  }
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save settings'),
+  );
+
+  return $form;
+}
+
+/**
+ * Builds a language negotiation method configuration table.
+ */
+function language_negotiation_configure_form_table(&$form, $type) {
+  $info = $form['#language_types_info'][$type];
+
+  $table_form = array(
+    '#title' => t('@type language detection', array('@type' => $info['name'])),
+    '#tree' => TRUE,
+    '#description' => $info['description'],
+    '#language_negotiation_info' => array(),
+    '#show_operations' => FALSE,
+    'weight' => array('#tree' => TRUE),
+    'enabled' => array('#tree' => TRUE),
+  );
+
+  $negotiation_info = $form['#language_negotiation_info'];
+  $enabled_methods = variable_get("language_negotiation_$type", array());
+  $methods_weight = variable_get("language_negotiation_methods_weight_$type", array());
+
+  // Add missing data to the methods lists.
+  foreach ($negotiation_info as $method_id => $method) {
+    if (!isset($methods_weight[$method_id])) {
+      $methods_weight[$method_id] = isset($method['weight']) ? $method['weight'] : 0;
+    }
+  }
+
+  // Order methods list by weight.
+  asort($methods_weight);
+
+  foreach ($methods_weight as $method_id => $weight) {
+    // A language method might be no more available if the defining module has
+    // been disabled after the last configuration saving.
+    if (!isset($negotiation_info[$method_id])) {
+      continue;
+    }
+
+    $enabled = isset($enabled_methods[$method_id]);
+    $method = $negotiation_info[$method_id];
+
+    // List the method only if the current type is defined in its 'types' key.
+    // If it is not defined default to all the configurable language types.
+    $types = array_flip(isset($method['types']) ? $method['types'] : $form['#language_types']);
+
+    if (isset($types[$type])) {
+      $table_form['#language_negotiation_info'][$method_id] = $method;
+      $method_name = check_plain($method['name']);
+
+      $table_form['weight'][$method_id] = array(
+        '#type' => 'weight',
+        '#title' => t('Weight for !title language detection method', array('!title' => drupal_strtolower($method_name))),
+        '#title_display' => 'invisible',
+        '#default_value' => $weight,
+        '#attributes' => array('class' => array("language-method-weight-$type")),
+      );
+
+      $table_form['title'][$method_id] = array('#markup' => $method_name);
+
+      $table_form['enabled'][$method_id] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Enable !title language detection method', array('!title' => drupal_strtolower($method_name))),
+        '#title_display' => 'invisible',
+        '#default_value' => $enabled,
+      );
+      if ($method_id === LANGUAGE_NEGOTIATION_DEFAULT) {
+        $table_form['enabled'][$method_id]['#default_value'] = TRUE;
+        $table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled');
+      }
+
+      $table_form['description'][$method_id] = array('#markup' => filter_xss_admin($method['description']));
+
+      $config_op = array();
+      if (isset($method['config'])) {
+        $config_op = array('#type' => 'link', '#title' => t('Configure'), '#href' => $method['config']);
+        // If there is at least one operation enabled show the operation column.
+        $table_form['#show_operations'] = TRUE;
+      }
+      $table_form['operation'][$method_id] = $config_op;
+    }
+  }
+
+  $form[$type] = $table_form;
+}
+
+/**
+ * Returns HTML for the language negotiation configuration form.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_language_negotiation_configure_form($variables) {
+  $form = $variables['form'];
+  $output = '';
+
+  foreach ($form['#language_types'] as $type) {
+    $rows = array();
+    $info = $form['#language_types_info'][$type];
+    $title = '<label>' . $form[$type]['#title'] . '</label>';
+    $description = '<div class="description">' . $form[$type]['#description'] . '</div>';
+
+    foreach ($form[$type]['title'] as $id => $element) {
+      // Do not take form control structures.
+      if (is_array($element) && element_child($id)) {
+        $row = array(
+          'data' => array(
+            '<strong>' . drupal_render($form[$type]['title'][$id]) . '</strong>',
+            drupal_render($form[$type]['description'][$id]),
+            drupal_render($form[$type]['enabled'][$id]),
+            drupal_render($form[$type]['weight'][$id]),
+          ),
+          'class' => array('draggable'),
+        );
+        if ($form[$type]['#show_operations']) {
+          $row['data'][] = drupal_render($form[$type]['operation'][$id]);
+        }
+        $rows[] = $row;
+      }
+    }
+
+    $header = array(
+      array('data' => t('Detection method')),
+      array('data' => t('Description')),
+      array('data' => t('Enabled')),
+      array('data' => t('Weight')),
+    );
+
+    // If there is at least one operation enabled show the operation column.
+    if ($form[$type]['#show_operations']) {
+      $header[] = array('data' => t('Operations'));
+    }
+
+    $variables = array(
+      'header' => $header,
+      'rows' => $rows,
+      'attributes' => array('id' => "language-negotiation-methods-$type"),
+    );
+    $table  = theme('table', $variables);
+    $table .= drupal_render_children($form[$type]);
+
+    drupal_add_tabledrag("language-negotiation-methods-$type", 'order', 'sibling', "language-method-weight-$type");
+
+    $output .= '<div class="form-item">' . $title . $description . $table . '</div>';
+  }
+
+  $output .= drupal_render_children($form);
+  return $output;
+}
+
+/**
+ * Submit handler for language negotiation settings.
+ */
+function language_negotiation_configure_form_submit($form, &$form_state) {
+  $configurable_types = $form['#language_types'];
+
+  foreach ($configurable_types as $type) {
+    $method_weights = array();
+    $enabled_methods = $form_state['values'][$type]['enabled'];
+    $enabled_methods[LANGUAGE_NEGOTIATION_DEFAULT] = TRUE;
+    $method_weights_input = $form_state['values'][$type]['weight'];
+
+    foreach ($method_weights_input as $method_id => $weight) {
+      if ($enabled_methods[$method_id]) {
+        $method_weights[$method_id] = $weight;
+      }
+    }
+
+    language_negotiation_set($type, $method_weights);
+    variable_set("language_negotiation_methods_weight_$type", $method_weights_input);
+  }
+
+  // Update non-configurable language types and the related language negotiation
+  // configuration.
+  language_types_set();
+
+  $form_state['redirect'] = 'admin/config/regional/language/detection';
+  drupal_set_message(t('Language negotiation configuration saved.'));
+}
+
+/**
+ * Builds the URL language negotiation method configuration form.
+ */
+function language_negotiation_configure_url_form($form, &$form_state) {
+  $form['language_negotiation_url_part'] = array(
+    '#title' => t('Part of the URL that determines language'),
+    '#type' => 'radios',
+    '#options' => array(
+      LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix'),
+      LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain'),
+    ),
+    '#default_value' => variable_get('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX),
+  );
+
+  $form['prefix'] = array(
+    '#type' => 'fieldset',
+    '#tree' => TRUE,
+    '#title' => t('Path prefix configuration'),
+    '#description' => t('Language codes or other custom text to use as a path prefix for URL language detection. For the default language, this value may be left blank. <strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "deutsch" as the path prefix code for German results in URLs like "example.com/deutsch/contact".'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="language_negotiation_url_part"]' => array(
+          'value' => (string) LANGUAGE_NEGOTIATION_URL_PREFIX,
+        ),
+      ),
+    ),
+  );
+  $form['domain'] = array(
+    '#type' => 'fieldset',
+    '#tree' => TRUE,
+    '#title' => t('Domain configuration'),
+    '#description' => t('The domain names to use for these languages. Leave blank for the default language. Use with caution in a production environment.<strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="language_negotiation_url_part"]' => array(
+          'value' => (string) LANGUAGE_NEGOTIATION_URL_DOMAIN,
+        ),
+      ),
+    ),
+  );
+
+  // Get the enabled languages only.
+  $languages = language_list(TRUE);
+  $prefixes = language_negotiation_url_prefixes();
+  $domains = language_negotiation_url_domains();
+  foreach ($languages as $langcode => $language) {
+    $form['prefix'][$langcode] = array(
+      '#type' => 'textfield',
+      '#title' => t('%language (%langcode) path prefix', array('%language' => $language->name, '%langcode' => $language->langcode)),
+      '#maxlength' => 64,
+      '#default_value' => isset($prefixes[$langcode]) ? $prefixes[$langcode] : '',
+      '#field_prefix' => url('', array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
+    );
+    $form['domain'][$langcode] = array(
+      '#type' => 'textfield',
+      '#title' => t('%language (%langcode) domain', array('%language' => $language->name, '%langcode' => $language->langcode)),
+      '#maxlength' => 128,
+      '#default_value' => isset($domains[$langcode]) ? $domains[$langcode] : '',
+    );
+  }
+
+  $form_state['redirect'] = 'admin/config/regional/language/detection';
+
+  $form['actions']['#type'] = 'actions';
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save configuration'),
+  );
+  return $form;
+}
+
+/**
+ * Validates the URL language negotiation method configuration.
+ *
+ * Validate that the prefixes and domains are unique, and make sure that
+ * the prefix and domain are only blank for the default.
+ */
+function language_negotiation_configure_url_form_validate($form, &$form_state) {
+  // Get the enabled languages only.
+  $languages = language_list(TRUE);
+  $default = language_default();
+
+  // Count repeated values for uniqueness check.
+  $count = array_count_values($form_state['values']['prefix']);
+  foreach ($languages as $langcode => $language) {
+    $value = $form_state['values']['prefix'][$langcode];
+
+    if ($value === '') {
+      if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_PREFIX) {
+        // Validation error if the prefix is blank for a non-default language, and value is for selected negotiation type.
+        form_error($form['prefix'][$langcode], t('The prefix may only be left blank for the default language.'));
+      }
+    }
+    else if (isset($count[$value]) && $count[$value] > 1) {
+      // Validation error if there are two languages with the same domain/prefix.
+      form_error($form['prefix'][$langcode], t('The prefix for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
+    }
+  }
+
+  // Count repeated values for uniqueness check.
+  $count = array_count_values($form_state['values']['domain']);
+  foreach ($languages as $langcode => $language) {
+    $value = $form_state['values']['domain'][$langcode];
+
+    if ($value === '') {
+      if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_DOMAIN) {
+        // Validation error if the domain is blank for a non-default language, and value is for selected negotiation type.
+        form_error($form['domain'][$langcode], t('The domain may only be left blank for the default language.'));
+      }
+    }
+    else if (isset($count[$value]) && $count[$value] > 1) {
+      // Validation error if there are two languages with the same domain/domain.
+      form_error($form['domain'][$langcode], t('The domain for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
+    }
+  }
+
+  // Domain names should not contain protocol and/or ports.
+  foreach ($languages as $langcode => $name) {
+    $value = $form_state['values']['domain'][$langcode];
+    if (!empty($value)) {
+      // Ensure we have a protocol but only one protocol in the setting for
+      // parse_url() checking against the hostname.
+      $host = 'http://' . str_replace(array('http://', 'https://'), '', $value);
+      if (parse_url($host, PHP_URL_HOST) != $value) {
+        form_error($form['domain'][$langcode], t('The domain for %language may only contain the domain name, not a protocol and/or port.', array( '%language' => $name)));
+      }
+    }
+  }
+}
+
+/**
+ * Saves the URL language negotiation method settings.
+ */
+function language_negotiation_configure_url_form_submit($form, &$form_state) {
+
+  // Save selected format (prefix or domain).
+  variable_set('language_negotiation_url_part', $form_state['values']['language_negotiation_url_part']);
+
+  // Save new domain and prefix values.
+  language_negotiation_url_prefixes_save($form_state['values']['prefix']);
+  language_negotiation_url_domains_save($form_state['values']['domain']);
+
+  drupal_set_message(t('Configuration saved.'));
+}
+
+/**
+ * Builds the session language negotiation method configuration form.
+ */
+function language_negotiation_configure_session_form($form, &$form_state) {
+  $form['language_negotiation_session_param'] = array(
+    '#title' => t('Request/session parameter'),
+    '#type' => 'textfield',
+    '#default_value' => variable_get('language_negotiation_session_param', 'language'),
+    '#description' => t('Name of the request/session parameter used to determine the desired language.'),
+  );
+
+  $form_state['redirect'] = 'admin/config/regional/language/detection';
+
+  return system_settings_form($form);
+}
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
index ecf637d..92c61f4 100644
--- a/core/modules/language/language.install
+++ b/core/modules/language/language.install
@@ -7,10 +7,34 @@
 
 /**
  * Implements hook_install().
+ *
+ * Enable URL language negotiation by default in order to have a basic working
+ * system on multilingual sites without needing any preliminary configuration.
  */
 function language_install() {
   // Add the default language to the database too.
   language_save(language_default());
+
+  require_once DRUPAL_ROOT . '/core/includes/language.inc';
+
+  // We cannot rely on language negotiation hooks here, because locale module is
+  // not enabled yet. Therefore language_negotiation_set() cannot be used.
+  $info = language_negotiation_info();
+  $method = $info[LANGUAGE_NEGOTIATION_URL];
+  $method_fields = array('callbacks', 'file', 'cache');
+  $negotiation = array();
+
+  // Store only the needed data.
+  foreach ($method_fields as $field) {
+    if (isset($method[$field])) {
+      $negotiation[LANGUAGE_NEGOTIATION_URL][$field] = $method[$field];
+    }
+  }
+
+  // Enable URL language detection for each (core) configurable language type.
+  foreach (language_types_get_configurable() as $type) {
+    variable_set("language_negotiation_$type", $negotiation);
+  }
 }
 
 /**
@@ -21,6 +45,20 @@ function language_uninstall() {
   variable_del('language_default');
   variable_del('language_count');
 
+  // Clear variables.
+  variable_del('language_types');
+  variable_del('language_negotiation_url_part');
+  variable_del('language_negotiation_url_prefixes');
+  variable_del('language_negotiation_url_domains');
+  variable_del('language_negotiation_session_param');
+  variable_del('language_content_type_default');
+  variable_del('language_content_type_negotiation');
+
+  foreach (language_types_get_all() as $type) {
+    variable_del("language_negotiation_$type");
+    variable_del("language_negotiation_methods_weight_$type");
+  }
+
   // Re-initialize the language system so successive calls to t() and other
   // functions will not expect languages to be present.
   drupal_language_initialize();
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 89f562c..795f2e4 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -6,6 +6,46 @@
  */
 
 /**
+ * The language is determined using path prefix or domain.
+ */
+const LANGUAGE_NEGOTIATION_URL = 'language-url';
+
+/**
+ * The language is set based on the browser language settings.
+ */
+const LANGUAGE_NEGOTIATION_BROWSER = 'language-browser';
+
+/**
+ * The language is determined using the current interface language.
+ */
+const LANGUAGE_NEGOTIATION_INTERFACE = 'language-interface';
+
+/**
+ * If no URL language, language is determined using an already detected one.
+ */
+const LANGUAGE_NEGOTIATION_URL_FALLBACK = 'language-url-fallback';
+
+/**
+ * The language is set based on the user language settings.
+ */
+const LANGUAGE_NEGOTIATION_USER = 'language-user';
+
+/**
+ * The language is set based on the request/session parameters.
+ */
+const LANGUAGE_NEGOTIATION_SESSION = 'language-session';
+
+/**
+ * URL language negotiation: use the path prefix as URL language indicator.
+ */
+const LANGUAGE_NEGOTIATION_URL_PREFIX = 0;
+
+/**
+ * URL language negotiation: use the domain as URL language indicator.
+ */
+const LANGUAGE_NEGOTIATION_URL_DOMAIN = 1;
+
+/**
  * Implements hook_help().
  */
 function language_help($path, $arg) {
@@ -18,6 +58,8 @@ function language_help($path, $arg) {
       $output .= '<dl>';
       $output .= '<dt>' . t('Configuring the list of languages') . '</dt>';
       $output .= '<dd>' . t('<a href="@configure-languages">Configure the list of languages</a> either using the built-in language list or providing any custom languages you wish.', array('@configure-languages' => url('admin/config/regional/language'))) . '</dd>';
+      $output .= '<dt>' . t('Configuring a multilingual site') . '</dt>';
+      $output .= '<dd>' . t("Language negotiation allows your site to automatically change language based on the domain or path used for each request. Users may (optionally) select their preferred language on their <em>My account</em> page, and your site can be configured to honor a web browser's preferred language settings. Site content can be translated using the <a href='@content-help'>Content Translation module</a>.", array('@content-help' => url('admin/help/translation'))) . '</dd>';
       $output .= '</dl>';
       return $output;
 
@@ -26,6 +68,20 @@ function language_help($path, $arg) {
 
     case 'admin/config/regional/language/add':
       return '<p>' . t('Add a language to be supported by your site. If your desired language is not available, pick <em>Custom language...</em> at the end and provide a language code and other details manually.') . '</p>';
+
+    case 'admin/config/regional/language/detection':
+      $output = '<p>' . t("Define how to decide which language is used to display page elements (primarily text provided by Drupal and modules, such as field labels and help text). This decision is made by evaluating a series of detection methods for languages; the first detection method that gets a result will determine which language is used for that type of text. Define the order of evaluation of language detection methods on this page.") . '</p>';
+      return $output;
+
+    case 'admin/config/regional/language/detection/session':
+      $output = '<p>' . t('Determine the language from a request/session parameter. Example: "http://example.com?language=de" sets language to German based on the use of "de" within the "language" parameter.') . '</p>';
+      return $output;
+
+    case 'admin/structure/block/manage/%/%':
+      if ($arg[4] == 'language' && $arg[5] == 'language_interface') {
+        return '<p>' . t('This block is only shown if <a href="@languages">at least two languages are enabled</a> and <a href="@configuration">language negotiation</a> is set to <em>URL</em> or <em>Session</em>.', array('@languages' => url('admin/config/regional/language'), '@configuration' => url('admin/config/regional/language/detection'))) . '</p>';
+      }
+      break;
   }
 }
 
@@ -33,6 +89,7 @@ function language_help($path, $arg) {
  * Implements hook_menu().
  */
 function language_menu() {
+  // Base language management and configuration.
   $items['admin/config/regional/language'] = array(
     'title' => 'Languages',
     'description' => 'Configure languages for content and the user interface.',
@@ -70,6 +127,34 @@ function language_menu() {
     'access arguments' => array('administer languages'),
     'file' => 'language.admin.inc',
   );
+
+  // Language negotiation.
+  $items['admin/config/regional/language/detection'] = array(
+    'title' => 'Detection and selection',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('language_negotiation_configure_form'),
+    'access arguments' => array('administer languages'),
+    'weight' => 10,
+    'file' => 'language.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['admin/config/regional/language/detection/url'] = array(
+    'title' => 'URL language detection configuration',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('language_negotiation_configure_url_form'),
+    'access arguments' => array('administer languages'),
+    'file' => 'language.admin.inc',
+    'type' => MENU_VISIBLE_IN_BREADCRUMB,
+  );
+  $items['admin/config/regional/language/detection/session'] = array(
+    'title' => 'Session language detection configuration',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('language_negotiation_configure_session_form'),
+    'access arguments' => array('administer languages'),
+    'file' => 'language.admin.inc',
+    'type' => MENU_VISIBLE_IN_BREADCRUMB,
+  );
+
   return $items;
 }
 
@@ -97,6 +182,9 @@ function language_theme() {
       'render element' => 'elements',
       'file' => 'language.admin.inc',
     ),
+    'language_negotiation_configure_form' => array(
+      'render element' => 'form',
+    ),
   );
 }
 
@@ -211,3 +299,262 @@ function language_css_alter(&$css) {
     }
   }
 }
+
+/**
+ * Implements hook_language_types_info().
+ *
+ * Defines the three core language types:
+ * - Interface language is the only configurable language type in core. It is
+ *   used by t() as the default language if none is specified.
+ * - Content language is by default non-configurable and inherits the interface
+ *   language negotiated value. It is used by the Field API to determine the
+ *   display language for fields if no explicit value is specified.
+ * - URL language is by default non-configurable and is determined through the
+ *   URL language negotiation method or the URL fallback language negotiation
+ *   method if no language can be detected. It is used by l() as the default
+ *   language if none is specified.
+ */
+function language_language_types_info() {
+  return array(
+    LANGUAGE_TYPE_INTERFACE => array(
+      'name' => t('User interface text'),
+      'description' => t('Order of language detection methods for user interface text. If a translation of user interface text is available in the detected language, it will be displayed.'),
+    ),
+    LANGUAGE_TYPE_CONTENT => array(
+      'name' => t('Content'),
+      'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'),
+      'fixed' => array(LANGUAGE_NEGOTIATION_INTERFACE),
+    ),
+    LANGUAGE_TYPE_URL => array(
+      'fixed' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_URL_FALLBACK),
+    ),
+  );
+}
+
+/**
+ * Implements hook_language_negotiation_info().
+ */
+function language_language_negotiation_info() {
+  $file = drupal_get_path('module', 'language') . '/language.negotiation.inc';
+
+  $negotiation_info = array();
+  $negotiation_info[LANGUAGE_NEGOTIATION_URL] = array(
+    'types' => array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL),
+    'callbacks' => array(
+      'negotiation' => 'language_from_url',
+      'language_switch' => 'language_switcher_url',
+      'url_rewrite' => 'language_url_rewrite_url',
+    ),
+    'file' => $file,
+    'weight' => -8,
+    'name' => t('URL'),
+    'description' => t('Determine the language from the URL (Path prefix or domain).'),
+    'config' => 'admin/config/regional/language/detection/url',
+  );
+
+  $negotiation_info[LANGUAGE_NEGOTIATION_SESSION] = array(
+    'callbacks' => array(
+      'negotiation' => 'language_from_session',
+      'language_switch' => 'language_switcher_session',
+      'url_rewrite' => 'language_url_rewrite_session',
+    ),
+    'file' => $file,
+    'weight' => -6,
+    'name' => t('Session'),
+    'description' => t('Determine the language from a request/session parameter.'),
+    'config' => 'admin/config/regional/language/detection/session',
+  );
+
+  $negotiation_info[LANGUAGE_NEGOTIATION_USER] = array(
+    'callbacks' => array('negotiation' => 'language_from_user'),
+    'file' => $file,
+    'weight' => -4,
+    'name' => t('User'),
+    'description' => t("Follow the user's language preference."),
+  );
+
+  $negotiation_info[LANGUAGE_NEGOTIATION_BROWSER] = array(
+    'callbacks' => array('negotiation' => 'language_from_browser'),
+    'file' => $file,
+    'weight' => -2,
+    'cache' => 0,
+    'name' => t('Browser'),
+    'description' => t("Determine the language from the browser's language settings."),
+  );
+
+  $negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE] = array(
+    'types' => array(LANGUAGE_TYPE_CONTENT),
+    'callbacks' => array('negotiation' => 'language_from_interface'),
+    'file' => $file,
+    'weight' => 8,
+    'name' => t('Interface'),
+    'description' => t('Use the detected interface language.'),
+  );
+
+  $negotiation_info[LANGUAGE_NEGOTIATION_URL_FALLBACK] = array(
+    'types' => array(LANGUAGE_TYPE_URL),
+    'callbacks' => array('negotiation' => 'language_url_fallback'),
+    'file' => $file,
+    'weight' => 8,
+    'name' => t('URL fallback'),
+    'description' => t('Use an already detected language for URLs if none is found.'),
+  );
+
+  return $negotiation_info;
+}
+
+/**
+ * Implements hook_modules_enabled().
+ */
+function language_modules_enabled($modules) {
+  include_once DRUPAL_ROOT . '/core/includes/language.inc';
+  language_types_set();
+  language_negotiation_purge();
+}
+
+/**
+ * Implements hook_modules_disabled().
+ */
+function language_modules_disabled($modules) {
+  language_modules_enabled($modules);
+}
+
+/**
+ * Implements hook_language_insert().
+ */
+function language_language_insert($language) {
+  // Add new language to the list of language prefixes.
+  $prefixes = language_negotiation_url_prefixes();
+  $prefixes[$language->langcode] = (empty($language->default) ? $language->langcode : '');
+  language_negotiation_url_prefixes_save($prefixes);
+
+  // Add language to the list of language domains.
+  $domains = language_negotiation_url_domains();
+  $domains[$language->langcode] = '';
+  language_negotiation_url_domains_save($domains);
+}
+
+/**
+ * Implements hook_language_update().
+ */
+function language_language_update($language) {
+  // If the language is the default, then ensure that no other languages have
+  // blank prefix codes.
+  if (!empty($language->default)) {
+    $prefixes = language_negotiation_url_prefixes();
+    foreach ($prefixes as $langcode => $prefix) {
+      if ($prefix == '' && $langcode != $language->langcode) {
+        $prefixes[$langcode] = $langcode;
+      }
+    }
+    language_negotiation_url_prefixes_save($prefixes);
+  }
+}
+
+/**
+ * Implements hook_language_delete().
+ */
+function language_language_delete($language) {
+  // Remove language from language prefix list.
+  $prefixes = language_negotiation_url_prefixes();
+  unset($prefixes[$language->langcode]);
+  language_negotiation_url_prefixes_save($prefixes);
+
+  // Remove language from language domain list.
+  $domains = language_negotiation_url_domains();
+  unset($domains[$language->langcode]);
+  language_negotiation_url_domains_save($domains);
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function language_block_info() {
+  include_once DRUPAL_ROOT . '/core/includes/language.inc';
+  $block = array();
+  $info = language_types_info();
+  foreach (language_types_get_configurable(FALSE) as $type) {
+    $block[$type] = array(
+      'info' => t('Language switcher (@type)', array('@type' => $info[$type]['name'])),
+      // Not worth caching.
+      'cache' => DRUPAL_NO_CACHE,
+    );
+  }
+  return $block;
+}
+
+/**
+ * Implements hook_block_view().
+ *
+ * Displays a language switcher. Only show if we have at least two languages.
+ */
+function language_block_view($type) {
+  if (language_multilingual()) {
+    $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
+    $links = language_negotiation_get_switch_links($type, $path);
+
+    if (isset($links->links)) {
+      $class = "language-switcher-{$links->method_id}";
+      $variables = array('links' => $links->links, 'attributes' => array('class' => array($class)));
+      $block['content'] = theme('links__language_block', $variables);
+      $block['subject'] = t('Languages');
+      return $block;
+    }
+  }
+}
+
+/**
+ * Implements hook_preprocess_block().
+ */
+function language_preprocess_block(&$variables) {
+  if ($variables['block']->module == 'language') {
+    $variables['attributes_array']['role'] = 'navigation';
+  }
+}
+
+/**
+ * Implements hook_url_outbound_alter().
+ *
+ * Rewrite outbound URLs with language based prefixes.
+ */
+function language_url_outbound_alter(&$path, &$options, $original_path) {
+  // Only modify internal URLs.
+  if (!$options['external'] && language_multilingual()) {
+    static $drupal_static_fast;
+    if (!isset($drupal_static_fast)) {
+      $drupal_static_fast['callbacks'] = &drupal_static(__FUNCTION__);
+    }
+    $callbacks = &$drupal_static_fast['callbacks'];
+
+    if (!isset($callbacks)) {
+      $callbacks = array();
+      include_once DRUPAL_ROOT . '/core/includes/language.inc';
+
+      foreach (language_types_get_configurable() as $type) {
+        // Get URL rewriter callbacks only from enabled language methods.
+        $negotiation = variable_get("language_negotiation_$type", array());
+
+        foreach ($negotiation as $method_id => $method) {
+          if (isset($method['callbacks']['url_rewrite'])) {
+            if (isset($method['file'])) {
+              require_once DRUPAL_ROOT . '/' . $method['file'];
+            }
+            // Avoid duplicate callback entries.
+            $callbacks[$method['callbacks']['url_rewrite']] = TRUE;
+          }
+        }
+      }
+
+      $callbacks = array_keys($callbacks);
+    }
+
+    foreach ($callbacks as $callback) {
+      $callback($path, $options);
+    }
+
+    // No language dependent path allowed in this mode.
+    if (empty($callbacks)) {
+      unset($options['language']);
+    }
+  }
+}
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
new file mode 100644
index 0000000..ef1d1cf
--- /dev/null
+++ b/core/modules/language/language.negotiation.inc
@@ -0,0 +1,425 @@
+<?php
+
+/**
+ * @file
+ * Language negotiation functions.
+ */
+
+/**
+ * Identifies the language from the current interface language.
+ *
+ * @return
+ *   The current interface language code.
+ */
+function language_from_interface() {
+  global $language_interface;
+  return isset($language_interface->langcode) ? $language_interface->langcode : FALSE;
+}
+
+/**
+ * Identify language from the Accept-language HTTP header we got.
+ *
+ * We perform browser accept-language parsing only if page cache is disabled,
+ * otherwise we would cache a user-specific preference.
+ *
+ * @param $languages
+ *   An array of language objects for enabled languages ordered by weight.
+ *
+ * @return
+ *   A valid language code on success, FALSE otherwise.
+ */
+function language_from_browser($languages) {
+  if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+    return FALSE;
+  }
+
+  // The Accept-Language header contains information about the language
+  // preferences configured in the user's browser / operating system.
+  // RFC 2616 (section 14.4) defines the Accept-Language header as follows:
+  //   Accept-Language = "Accept-Language" ":"
+  //                  1#( language-range [ ";" "q" "=" qvalue ] )
+  //   language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
+  // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
+  $browser_langcodes = array();
+  if (preg_match_all('@([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches, PREG_SET_ORDER)) {
+    foreach ($matches as $match) {
+      // We can safely use strtolower() here, tags are ASCII.
+      // RFC2616 mandates that the decimal part is no more than three digits,
+      // so we multiply the qvalue by 1000 to avoid floating point comparisons.
+      $langcode = strtolower($match[1]);
+      $qvalue = isset($match[2]) ? (float) $match[2] : 1;
+      $browser_langcodes[$langcode] = (int) ($qvalue * 1000);
+    }
+  }
+
+  // We should take pristine values from the HTTP headers, but Internet Explorer
+  // from version 7 sends only specific language tags (eg. fr-CA) without the
+  // corresponding generic tag (fr) unless explicitly configured. In that case,
+  // we assume that the lowest value of the specific tags is the value of the
+  // generic language to be as close to the HTTP 1.1 spec as possible.
+  // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and
+  // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx
+  asort($browser_langcodes);
+  foreach ($browser_langcodes as $langcode => $qvalue) {
+    $generic_tag = strtok($langcode, '-');
+    if (!isset($browser_langcodes[$generic_tag])) {
+      $browser_langcodes[$generic_tag] = $qvalue;
+    }
+  }
+
+  // Find the enabled language with the greatest qvalue, following the rules
+  // of RFC 2616 (section 14.4). If several languages have the same qvalue,
+  // prefer the one with the greatest weight.
+  $best_match_langcode = FALSE;
+  $max_qvalue = 0;
+  foreach ($languages as $langcode => $language) {
+    // Language tags are case insensitive (RFC2616, sec 3.10).
+    $langcode = strtolower($langcode);
+
+    // If nothing matches below, the default qvalue is the one of the wildcard
+    // language, if set, or is 0 (which will never match).
+    $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0;
+
+    // Find the longest possible prefix of the browser-supplied language
+    // ('the language-range') that matches this site language ('the language tag').
+    $prefix = $langcode;
+    do {
+      if (isset($browser_langcodes[$prefix])) {
+        $qvalue = $browser_langcodes[$prefix];
+        break;
+      }
+    }
+    while ($prefix = substr($prefix, 0, strrpos($prefix, '-')));
+
+    // Find the best match.
+    if ($qvalue > $max_qvalue) {
+      $best_match_langcode = $language->langcode;
+      $max_qvalue = $qvalue;
+    }
+  }
+
+  return $best_match_langcode;
+}
+
+/**
+ * Identify language from the user preferences.
+ *
+ * @param $languages
+ *   An array of valid language objects.
+ *
+ * @return
+ *   A valid language code on success, FALSE otherwise.
+ */
+function language_from_user($languages) {
+  // User preference (only for logged users).
+  global $user;
+
+  if ($user->uid && !empty($user->preferred_langcode)) {
+    return $user->preferred_langcode;
+  }
+
+  // No language preference from the user.
+  return FALSE;
+}
+
+/**
+ * Identify language from a request/session parameter.
+ *
+ * @param $languages
+ *   An array of valid language objects.
+ *
+ * @return
+ *   A valid language code on success, FALSE otherwise.
+ */
+function language_from_session($languages) {
+  $param = variable_get('language_negotiation_session_param', 'language');
+
+  // Request parameter: we need to update the session parameter only if we have
+  // an authenticated user.
+  if (isset($_GET[$param]) && isset($languages[$langcode = $_GET[$param]])) {
+    global $user;
+    if ($user->uid) {
+      $_SESSION[$param] = $langcode;
+    }
+    return $langcode;
+  }
+
+  // Session parameter.
+  if (isset($_SESSION[$param])) {
+    return $_SESSION[$param];
+  }
+
+  return FALSE;
+}
+
+/**
+ * Identify language via URL prefix or domain.
+ *
+ * @param $languages
+ *   An array of valid language objects.
+ *
+ * @return
+ *   A valid language code on success, FALSE otherwise.
+ */
+function language_from_url($languages) {
+  $language_url = FALSE;
+
+  if (!language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_URL)) {
+    return $language_url;
+  }
+
+  switch (variable_get('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX)) {
+    case LANGUAGE_NEGOTIATION_URL_PREFIX:
+      // $_GET['q'] might not be available at this time, because
+      // path initialization runs after the language bootstrap phase.
+      list($language, $_GET['q']) = language_url_split_prefix(isset($_GET['q']) ? $_GET['q'] : NULL, $languages);
+      if ($language !== FALSE) {
+        $language_url = $language->langcode;
+      }
+      break;
+
+    case LANGUAGE_NEGOTIATION_URL_DOMAIN:
+      $domains = language_negotiation_url_domains();
+      foreach ($languages as $language) {
+        // Skip check if the language doesn't have a domain.
+        if (!empty($domains[$language->langcode])) {
+          // Only compare the domains not the protocols or ports.
+          // Remove protocol and add http:// so parse_url works
+          $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->langcode]);
+          $host = parse_url($host, PHP_URL_HOST);
+          if ($_SERVER['HTTP_HOST'] == $host) {
+            $language_url = $language->langcode;
+            break;
+          }
+        }
+      }
+      break;
+  }
+
+  return $language_url;
+}
+
+/**
+ * Determines the language to be assigned to URLs when none is detected.
+ *
+ * The language negotiation process has a fallback chain that ends with the
+ * default language negotiation method. Each built-in language type has a
+ * separate initialization:
+ * - Interface language, which is the only configurable one, always gets a valid
+ *   value. If no request-specific language is detected, the default language
+ *   will be used.
+ * - Content language merely inherits the interface language by default.
+ * - URL language is detected from the requested URL and will be used to rewrite
+ *   URLs appearing in the page being rendered. If no language can be detected,
+ *   there are two possibilities:
+ *   - If the default language has no configured path prefix or domain, then the
+ *     default language is used. This guarantees that (missing) URL prefixes are
+ *     preserved when navigating through the site.
+ *   - If the default language has a configured path prefix or domain, a
+ *     requested URL having an empty prefix or domain is an anomaly that must be
+ *     fixed. This is done by introducing a prefix or domain in the rendered
+ *     page matching the detected interface language.
+ *
+ * @param $languages
+ *   (optional) An array of valid language objects. This is passed by
+ *   language_negotiation_method_invoke() to every language method callback,
+ *   but it is not actually needed here. Defaults to NULL.
+ * @param $language_type
+ *   (optional) The language type to fall back to. Defaults to the interface
+ *   language.
+ *
+ * @return
+ *   A valid language code.
+ */
+function language_url_fallback($language = NULL, $language_type = LANGUAGE_TYPE_INTERFACE) {
+  $default = language_default();
+  $prefix = (variable_get('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX) == LANGUAGE_NEGOTIATION_URL_PREFIX);
+
+  // If the default language is not configured to convey language information,
+  // a missing URL language information indicates that URL language should be
+  // the default one, otherwise we fall back to an already detected language.
+  $domains = language_negotiation_url_domains();
+  $prefixes = language_negotiation_url_prefixes();
+  if (($prefix && empty($prefixes[$default->langcode])) || (!$prefix && empty($domains[$default->langcode]))) {
+    return $default->langcode;
+  }
+  else {
+    return $GLOBALS[$language_type]->langcode;
+  }
+}
+
+/**
+ * Return links for the URL language switcher block.
+ *
+ * Translation links may be provided by other modules.
+ */
+function language_switcher_url($type, $path) {
+  // Get the enabled languages only.
+  $languages = language_list(TRUE);
+  $links = array();
+
+  foreach ($languages as $language) {
+    $links[$language->langcode] = array(
+      'href'       => $path,
+      'title'      => $language->name,
+      'language'   => $language,
+      'attributes' => array('class' => array('language-link')),
+    );
+  }
+
+  return $links;
+}
+
+/**
+ * Return the session language switcher block.
+ */
+function language_switcher_session($type, $path) {
+  $param = variable_get('language_negotiation_session_param', 'language');
+  $language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : $GLOBALS[$type]->langcode;
+
+  // Get the enabled languages only.
+  $languages = language_list(TRUE);
+  $links = array();
+
+  $query = $_GET;
+  unset($query['q']);
+
+  foreach ($languages as $language) {
+    $langcode = $language->langcode;
+    $links[$langcode] = array(
+      'href'       => $path,
+      'title'      => $language->name,
+      'attributes' => array('class' => array('language-link')),
+      'query'      => $query,
+    );
+    if ($language_query != $langcode) {
+      $links[$langcode]['query'][$param] = $langcode;
+    }
+    else {
+      $links[$langcode]['attributes']['class'][] = ' session-active';
+    }
+  }
+
+  return $links;
+}
+
+/**
+ * Rewrite URLs for the URL language negotiation method.
+ */
+function language_url_rewrite_url(&$path, &$options) {
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['languages'] = &drupal_static(__FUNCTION__);
+  }
+  $languages = &$drupal_static_fast['languages'];
+
+  if (!isset($languages)) {
+    // Get the enabled languages only.
+    $languages = language_list(TRUE);
+    $languages = array_flip(array_keys($languages));
+  }
+
+  // Language can be passed as an option, or we go for current URL language.
+  if (!isset($options['language'])) {
+    global $language_url;
+    $options['language'] = $language_url;
+  }
+  // We allow only enabled languages here.
+  elseif (!isset($languages[$options['language']->langcode])) {
+    unset($options['language']);
+    return;
+  }
+
+  if (isset($options['language'])) {
+    switch (variable_get('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX)) {
+      case LANGUAGE_NEGOTIATION_URL_DOMAIN:
+        $domains = language_negotiation_url_domains();
+        if (!empty($domains[$options['language']->langcode])) {
+          // Ask for an absolute URL with our modified base_url.
+          global $is_https;
+          $url_scheme = ($is_https) ? 'https://' : 'http://';
+          $options['absolute'] = TRUE;
+          $options['base_url'] = $url_scheme . $domains[$options['language']->langcode];
+          if (isset($options['https']) && variable_get('https', FALSE)) {
+            if ($options['https'] === TRUE) {
+              $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
+            }
+            elseif ($options['https'] === FALSE) {
+              $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
+            }
+          }
+        }
+        break;
+
+      case LANGUAGE_NEGOTIATION_URL_PREFIX:
+        $prefixes = language_negotiation_url_prefixes();
+        if (!empty($prefixes[$options['language']->langcode])) {
+          $options['prefix'] = $prefixes[$options['language']->langcode] . '/';
+        }
+        break;
+    }
+  }
+}
+
+/**
+ * Reads language prefixes and uses the langcode if no prefix is set.
+ */
+function language_negotiation_url_prefixes() {
+  return variable_get('language_negotiation_url_prefixes', array());
+}
+
+/**
+ * Saves language prefix settings.
+ */
+function language_negotiation_url_prefixes_save(array $prefixes) {
+  variable_set('language_negotiation_url_prefixes', $prefixes);
+}
+
+/**
+ * Reads language domains.
+ */
+function language_negotiation_url_domains() {
+  return variable_get('language_negotiation_url_domains', array());
+}
+
+/**
+ * Saves the language domain settings.
+ */
+function language_negotiation_url_domains_save(array $domains) {
+  variable_set('language_negotiation_url_domains', $domains);
+}
+
+/**
+ * Rewrite URLs for the Session language negotiation method.
+ */
+function language_url_rewrite_session(&$path, &$options) {
+  static $query_rewrite, $query_param, $query_value;
+
+  // The following values are not supposed to change during a single page
+  // request processing.
+  if (!isset($query_rewrite)) {
+    global $user;
+    if (!$user->uid) {
+      // Get the enabled languages only.
+      $languages = language_list(TRUE);
+      $query_param = check_plain(variable_get('language_negotiation_session_param', 'language'));
+      $query_value = isset($_GET[$query_param]) ? check_plain($_GET[$query_param]) : NULL;
+      $query_rewrite = isset($languages[$query_value]) && language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_SESSION);
+    }
+    else {
+      $query_rewrite = FALSE;
+    }
+  }
+
+  // If the user is anonymous, the user language negotiation method is enabled,
+  // and the corresponding option has been set, we must preserve any explicit
+  // user language preference even with cookies disabled.
+  if ($query_rewrite) {
+    if (is_string($options['query'])) {
+      $options['query'] = drupal_get_query_array($options['query']);
+    }
+    if (!isset($options['query'][$query_param])) {
+      $options['query'][$query_param] = $query_value;
+    }
+  }
+}
diff --git a/core/modules/locale/locale.admin.inc b/core/modules/locale/locale.admin.inc
index 321b8ce..7b44b8f 100644
--- a/core/modules/locale/locale.admin.inc
+++ b/core/modules/locale/locale.admin.inc
@@ -6,379 +6,6 @@
  */
 
 /**
- * Builds the configuration form for language negotiation.
- */
-function language_negotiation_configure_form() {
-  include_once DRUPAL_ROOT . '/core/includes/language.inc';
-
-  $form = array(
-    '#submit' => array('language_negotiation_configure_form_submit'),
-    '#theme' => 'language_negotiation_configure_form',
-    '#language_types' => language_types_get_configurable(FALSE),
-    '#language_types_info' => language_types_info(),
-    '#language_negotiation_info' => language_negotiation_info(),
-  );
-
-  foreach ($form['#language_types'] as $type) {
-    language_negotiation_configure_form_table($form, $type);
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save settings'),
-  );
-
-  return $form;
-}
-
-/**
- * Builds a language negotion method configuration table.
- */
-function language_negotiation_configure_form_table(&$form, $type) {
-  $info = $form['#language_types_info'][$type];
-
-  $table_form = array(
-    '#title' => t('@type language detection', array('@type' => $info['name'])),
-    '#tree' => TRUE,
-    '#description' => $info['description'],
-    '#language_negotiation_info' => array(),
-    '#show_operations' => FALSE,
-    'weight' => array('#tree' => TRUE),
-    'enabled' => array('#tree' => TRUE),
-  );
-
-  $negotiation_info = $form['#language_negotiation_info'];
-  $enabled_methods = variable_get("language_negotiation_$type", array());
-  $methods_weight = variable_get("locale_language_negotiation_methods_weight_$type", array());
-
-  // Add missing data to the methods lists.
-  foreach ($negotiation_info as $method_id => $method) {
-    if (!isset($methods_weight[$method_id])) {
-      $methods_weight[$method_id] = isset($method['weight']) ? $method['weight'] : 0;
-    }
-  }
-
-  // Order methods list by weight.
-  asort($methods_weight);
-
-  foreach ($methods_weight as $method_id => $weight) {
-    // A language method might be no more available if the defining module has
-    // been disabled after the last configuration saving.
-    if (!isset($negotiation_info[$method_id])) {
-      continue;
-    }
-
-    $enabled = isset($enabled_methods[$method_id]);
-    $method = $negotiation_info[$method_id];
-
-    // List the method only if the current type is defined in its 'types' key.
-    // If it is not defined default to all the configurable language types.
-    $types = array_flip(isset($method['types']) ? $method['types'] : $form['#language_types']);
-
-    if (isset($types[$type])) {
-      $table_form['#language_negotiation_info'][$method_id] = $method;
-      $method_name = check_plain($method['name']);
-
-      $table_form['weight'][$method_id] = array(
-        '#type' => 'weight',
-        '#title' => t('Weight for !title language detection method', array('!title' => drupal_strtolower($method_name))),
-        '#title_display' => 'invisible',
-        '#default_value' => $weight,
-        '#attributes' => array('class' => array("language-method-weight-$type")),
-      );
-
-      $table_form['title'][$method_id] = array('#markup' => $method_name);
-
-      $table_form['enabled'][$method_id] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Enable !title language detection method', array('!title' => drupal_strtolower($method_name))),
-        '#title_display' => 'invisible',
-        '#default_value' => $enabled,
-      );
-      if ($method_id === LANGUAGE_NEGOTIATION_DEFAULT) {
-        $table_form['enabled'][$method_id]['#default_value'] = TRUE;
-        $table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled');
-      }
-
-      $table_form['description'][$method_id] = array('#markup' => filter_xss_admin($method['description']));
-
-      $config_op = array();
-      if (isset($method['config'])) {
-        $config_op = array('#type' => 'link', '#title' => t('Configure'), '#href' => $method['config']);
-        // If there is at least one operation enabled show the operation column.
-        $table_form['#show_operations'] = TRUE;
-      }
-      $table_form['operation'][$method_id] = $config_op;
-    }
-  }
-
-  $form[$type] = $table_form;
-}
-
-/**
- * Returns HTML for the language negotiation configuration form.
- *
- * @param $variables
- *   An associative array containing:
- *   - form: A render element representing the form.
- *
- * @ingroup themeable
- */
-function theme_language_negotiation_configure_form($variables) {
-  $form = $variables['form'];
-  $output = '';
-
-  foreach ($form['#language_types'] as $type) {
-    $rows = array();
-    $info = $form['#language_types_info'][$type];
-    $title = '<label>' . $form[$type]['#title'] . '</label>';
-    $description = '<div class="description">' . $form[$type]['#description'] . '</div>';
-
-    foreach ($form[$type]['title'] as $id => $element) {
-      // Do not take form control structures.
-      if (is_array($element) && element_child($id)) {
-        $row = array(
-          'data' => array(
-            '<strong>' . drupal_render($form[$type]['title'][$id]) . '</strong>',
-            drupal_render($form[$type]['description'][$id]),
-            drupal_render($form[$type]['enabled'][$id]),
-            drupal_render($form[$type]['weight'][$id]),
-          ),
-          'class' => array('draggable'),
-        );
-        if ($form[$type]['#show_operations']) {
-          $row['data'][] = drupal_render($form[$type]['operation'][$id]);
-        }
-        $rows[] = $row;
-      }
-    }
-
-    $header = array(
-      array('data' => t('Detection method')),
-      array('data' => t('Description')),
-      array('data' => t('Enabled')),
-      array('data' => t('Weight')),
-    );
-
-    // If there is at least one operation enabled show the operation column.
-    if ($form[$type]['#show_operations']) {
-      $header[] = array('data' => t('Operations'));
-    }
-
-    $variables = array(
-      'header' => $header,
-      'rows' => $rows,
-      'attributes' => array('id' => "language-negotiation-methods-$type"),
-    );
-    $table  = theme('table', $variables);
-    $table .= drupal_render_children($form[$type]);
-
-    drupal_add_tabledrag("language-negotiation-methods-$type", 'order', 'sibling', "language-method-weight-$type");
-
-    $output .= '<div class="form-item">' . $title . $description . $table . '</div>';
-  }
-
-  $output .= drupal_render_children($form);
-  return $output;
-}
-
-/**
- * Submit handler for language negotiation settings.
- */
-function language_negotiation_configure_form_submit($form, &$form_state) {
-  $configurable_types = $form['#language_types'];
-
-  foreach ($configurable_types as $type) {
-    $method_weights = array();
-    $enabled_methods = $form_state['values'][$type]['enabled'];
-    $enabled_methods[LANGUAGE_NEGOTIATION_DEFAULT] = TRUE;
-    $method_weights_input = $form_state['values'][$type]['weight'];
-
-    foreach ($method_weights_input as $method_id => $weight) {
-      if ($enabled_methods[$method_id]) {
-        $method_weights[$method_id] = $weight;
-      }
-    }
-
-    language_negotiation_set($type, $method_weights);
-    variable_set("locale_language_negotiation_methods_weight_$type", $method_weights_input);
-  }
-
-  // Update non-configurable language types and the related language negotiation
-  // configuration.
-  language_types_set();
-
-  $form_state['redirect'] = 'admin/config/regional/language/detection';
-  drupal_set_message(t('Language negotiation configuration saved.'));
-}
-
-/**
- * Builds the URL language negotiation method configuration form.
- */
-function language_negotiation_configure_url_form($form, &$form_state) {
-  $form['locale_language_negotiation_url_part'] = array(
-    '#title' => t('Part of the URL that determines language'),
-    '#type' => 'radios',
-    '#options' => array(
-      LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix'),
-      LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain'),
-    ),
-    '#default_value' => variable_get('locale_language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX),
-  );
-
-  $form['prefix'] = array(
-    '#type' => 'fieldset',
-    '#tree' => TRUE,
-    '#title' => t('Path prefix configuration'),
-    '#description' => t('Language codes or other custom text to use as a path prefix for URL language detection. For the default language, this value may be left blank. <strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "deutsch" as the path prefix code for German results in URLs like "example.com/deutsch/contact".'),
-    '#states' => array(
-      'visible' => array(
-        ':input[name="locale_language_negotiation_url_part"]' => array(
-          'value' => (string) LANGUAGE_NEGOTIATION_URL_PREFIX,
-        ),
-      ),
-    ),
-  );
-  $form['domain'] = array(
-    '#type' => 'fieldset',
-    '#tree' => TRUE,
-    '#title' => t('Domain configuration'),
-    '#description' => t('The domain names to use for these languages. Leave blank for the default language. Use with caution in a production environment.<strong>Modifying this value may break existing URLs. Use with caution in a production environment.</strong> Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'),
-    '#states' => array(
-      'visible' => array(
-        ':input[name="locale_language_negotiation_url_part"]' => array(
-          'value' => (string) LANGUAGE_NEGOTIATION_URL_DOMAIN,
-        ),
-      ),
-    ),
-  );
-
-  // Get the enabled languages only.
-  $languages = language_list(TRUE);
-  $prefixes = locale_language_negotiation_url_prefixes();
-  $domains = locale_language_negotiation_url_domains();
-  foreach ($languages as $langcode => $language) {
-    $form['prefix'][$langcode] = array(
-      '#type' => 'textfield',
-      '#title' => t('%language (%langcode) path prefix', array('%language' => $language->name, '%langcode' => $language->langcode)),
-      '#maxlength' => 64,
-      '#default_value' => isset($prefixes[$langcode]) ? $prefixes[$langcode] : '',
-      '#field_prefix' => url('', array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
-    );
-    $form['domain'][$langcode] = array(
-      '#type' => 'textfield',
-      '#title' => t('%language (%langcode) domain', array('%language' => $language->name, '%langcode' => $language->langcode)),
-      '#maxlength' => 128,
-      '#default_value' => isset($domains[$langcode]) ? $domains[$langcode] : '',
-    );
-  }
-
-  $form_state['redirect'] = 'admin/config/regional/language/detection';
-
-  $form['actions']['#type'] = 'actions';
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save configuration'),
-  );
-  return $form;
-}
-
-/**
- * Validates the URL language negotiation method configuration.
- *
- * Validate that the prefixes and domains are unique, and make sure that
- * the prefix and domain are only blank for the default.
- */
-function language_negotiation_configure_url_form_validate($form, &$form_state) {
-  // Get the enabled languages only.
-  $languages = language_list(TRUE);
-  $default = language_default();
-
-  // Count repeated values for uniqueness check.
-  $count = array_count_values($form_state['values']['prefix']);
-  foreach ($languages as $langcode => $language) {
-    $value = $form_state['values']['prefix'][$langcode];
-
-    if ($value === '') {
-      if (!$language->default && $form_state['values']['locale_language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_PREFIX) {
-        // Validation error if the prefix is blank for a non-default language, and value is for selected negotiation type.
-        form_error($form['prefix'][$langcode], t('The prefix may only be left blank for the default language.'));
-      }
-    }
-    else if (isset($count[$value]) && $count[$value] > 1) {
-      // Validation error if there are two languages with the same domain/prefix.
-      form_error($form['prefix'][$langcode], t('The prefix for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
-    }
-  }
-
-  // Count repeated values for uniqueness check.
-  $count = array_count_values($form_state['values']['domain']);
-  foreach ($languages as $langcode => $language) {
-    $value = $form_state['values']['domain'][$langcode];
-
-    if ($value === '') {
-      if (!$language->default && $form_state['values']['locale_language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_DOMAIN) {
-        // Validation error if the domain is blank for a non-default language, and value is for selected negotiation type.
-        form_error($form['domain'][$langcode], t('The domain may only be left blank for the default language.'));
-      }
-    }
-    else if (isset($count[$value]) && $count[$value] > 1) {
-      // Validation error if there are two languages with the same domain/domain.
-      form_error($form['domain'][$langcode], t('The domain for %language, %value, is not unique.', array('%language' => $language->name, '%value' => $value)));
-    }
-  }
-
-  // Domain names should not contain protocol and/or ports.
-  foreach ($languages as $langcode => $name) {
-    $value = $form_state['values']['domain'][$langcode];
-    if (!empty($value)) {
-      // Ensure we have a protocol but only one protocol in the setting for
-      // parse_url() checking against the hostname.
-      $host = 'http://' . str_replace(array('http://', 'https://'), '', $value);
-      if (parse_url($host, PHP_URL_HOST) != $value) {
-        form_error($form['domain'][$langcode], t('The domain for %language may only contain the domain name, not a protocol and/or port.', array( '%language' => $name)));
-      }
-    }
-  }
-}
-
-/**
- * Saves the URL language negotiation method settings.
- */
-function language_negotiation_configure_url_form_submit($form, &$form_state) {
-
-  // Save selected format (prefix or domain).
-  variable_set('locale_language_negotiation_url_part', $form_state['values']['locale_language_negotiation_url_part']);
-
-  // Save new domain and prefix values.
-  locale_language_negotiation_url_prefixes_save($form_state['values']['prefix']);
-  locale_language_negotiation_url_domains_save($form_state['values']['domain']);
-
-  drupal_set_message(t('Configuration saved.'));
-}
-
-/**
- * Builds the session language negotiation method configuration form.
- */
-function language_negotiation_configure_session_form($form, &$form_state) {
-  $form['locale_language_negotiation_session_param'] = array(
-    '#title' => t('Request/session parameter'),
-    '#type' => 'textfield',
-    '#default_value' => variable_get('locale_language_negotiation_session_param', 'language'),
-    '#description' => t('Name of the request/session parameter used to determine the desired language.'),
-  );
-
-  $form_state['redirect'] = 'admin/config/regional/language/detection';
-
-  return system_settings_form($form);
-}
-
-/**
- * @} End of "locale-language-administration"
- */
-
-/**
  * Returns HTML for a locale date format form.
  *
  * @param $variables
diff --git a/core/modules/locale/locale.css b/core/modules/locale/locale.css
index 66de82b..81e4cf5 100644
--- a/core/modules/locale/locale.css
+++ b/core/modules/locale/locale.css
@@ -24,9 +24,3 @@
   float: left; /* LTR */
   padding: 3ex 0 0 1em; /* LTR */
 }
-.language-switcher-locale-session a.active {
-  color: #0062a0;
-}
-.language-switcher-locale-session a.session-active {
-  color: #000000;
-}
diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install
index d05d24b..3c96d8d 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -6,62 +6,6 @@
  */
 
 /**
- * Implements hook_install().
- *
- * Enable URL language negotiation by default in order to have a basic working
- * system on multilingual sites without needing any preliminary configuration.
- */
-function locale_install() {
-  require_once DRUPAL_ROOT . '/core/includes/language.inc';
-
-  // We cannot rely on language negotiation hooks here, because locale module is
-  // not enabled yet. Therefore language_negotiation_set() cannot be used.
-  $info = locale_language_negotiation_info();
-  $method = $info[LANGUAGE_NEGOTIATION_URL];
-  $method_fields = array('callbacks', 'file', 'cache');
-  $negotiation = array();
-
-  // Store only the needed data.
-  foreach ($method_fields as $field) {
-    if (isset($method[$field])) {
-      $negotiation[LANGUAGE_NEGOTIATION_URL][$field] = $method[$field];
-    }
-  }
-
-  // Enable URL language detection for each (core) configurable language type.
-  foreach (language_types_get_configurable() as $type) {
-    variable_set("language_negotiation_$type", $negotiation);
-  }
-}
-
-/**
- * Fill in the path prefixes and domains when enabled.
- *
- * Language module might change the list of languages, so we need to sync our
- * configuration for domains and paths with the current language list. This
- * should run every time the module is enabled.
- */
-function locale_enable() {
-  require_once DRUPAL_ROOT . '/core/includes/locale.inc';
-
-  $languages = language_list();
-  $prefixes_old = locale_language_negotiation_url_prefixes();
-  $domains_old = locale_language_negotiation_url_domains();
-
-  $prefixes = array();
-  $domains = array();
-  foreach ($languages as $langcode => $language) {
-    // Keep the old prefix or fill in based on whether the language is default.
-    $prefixes[$langcode] = empty($prefixes_old[$langcode]) ? (empty($language->default) ? $langcode : '') : $prefixes_old[$langcode];
-    // Keep the old domain or fill in empty value.
-    $domains[$langcode] = empty($domains_old[$langcode]) ? '' : $domains_old[$langcode];
-  }
-
-  locale_language_negotiation_url_prefixes_save($prefixes);
-  locale_language_negotiation_url_domains_save($domains);
-}
-
-/**
  * Implements hook_uninstall().
  */
 function locale_uninstall() {
@@ -82,13 +26,6 @@ function locale_uninstall() {
   }
 
   // Clear variables.
-  variable_del('language_types');
-  variable_del('locale_language_negotiation_url_part');
-  variable_del('locale_language_negotiation_url_prefixes');
-  variable_del('locale_language_negotiation_url_domains');
-  variable_del('locale_language_negotiation_session_param');
-  variable_del('language_content_type_default');
-  variable_del('language_content_type_negotiation');
   variable_del('locale_cache_strings');
   variable_del('locale_js_directory');
   variable_del('javascript_parsed');
@@ -97,11 +34,6 @@ function locale_uninstall() {
   variable_del('locale_translation_plurals');
   variable_del('locale_translation_javascript');
 
-  foreach (language_types_get_all() as $type) {
-    variable_del("language_negotiation_$type");
-    variable_del("locale_language_negotiation_methods_weight_$type");
-  }
-
   // Remove all node type language variables. Node module might have been
   // enabled, but may be disabled, so use a wildcard delete.
   db_delete('variable')
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index e942d0d..2acfa60 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -32,22 +32,12 @@ function locale_help($path, $arg) {
       $output .= '<li>' . t('Importing files from a set of existing translations, known as a translation package. A translation package enables the display of a specific version of Drupal in a specific language, and contains files in the Gettext Portable Object (<em>.po</em>) format. Although not all languages are available for every version of Drupal, translation packages for many languages are available for download from the <a href="@translations">Drupal translations page</a>.', array('@translations' => 'http://localize.drupal.org')) . '</li>';
       $output .= '<li>' . t("If an existing translation package does not meet your needs, the Gettext Portable Object (<em>.po</em>) files within a package may be modified, or new <em>.po</em> files may be created, using a desktop Gettext editor. The Locale module's <a href='@import'>import</a> feature allows the translated strings from a new or modified <em>.po</em> file to be added to your site. The Locale module's <a href='@export'>export</a> feature generates files from your site's translated strings, that can either be shared with others or edited offline by a Gettext translation editor.", array('@import' => url('admin/config/regional/translate/import'), '@export' => url('admin/config/regional/translate/export'))) . '</li>';
       $output .= '</ul></dd>';
-      $output .= '<dt>' . t('Configuring a multilingual site') . '</dt>';
-      $output .= '<dd>' . t("Language negotiation allows your site to automatically change language based on the domain or path used for each request. Users may (optionally) select their preferred language on their <em>My account</em> page, and your site can be configured to honor a web browser's preferred language settings. Site content can be translated using the <a href='@content-help'>Content Translation module</a>.", array('@content-help' => url('admin/help/translation'))) . '</dd>';
       $output .= '</dl>';
       return $output;
 
     case 'admin/config/regional/language':
       return '<p>' . t('Interface text can be translated. <a href="@translations">Download contributed translations</a> from Drupal.org.', array('@translations' => 'http://localize.drupal.org')) . '</p>';
 
-    case 'admin/config/regional/language/detection':
-      $output = '<p>' . t("Define how to decide which language is used to display page elements (primarily text provided by Drupal and modules, such as field labels and help text). This decision is made by evaluating a series of detection methods for languages; the first detection method that gets a result will determine which language is used for that type of text. Define the order of evaluation of language detection methods on this page.") . '</p>';
-      return $output;
-
-    case 'admin/config/regional/language/detection/session':
-      $output = '<p>' . t('Determine the language from a request/session parameter. Example: "http://example.com?language=de" sets language to German based on the use of "de" within the "language" parameter.') . '</p>';
-      return $output;
-
     case 'admin/config/regional/translate':
       $output = '<p>' . t('This page allows a translator to search for specific translated and untranslated strings, and is used when creating or editing translations. (Note: For translation tasks involving many strings, it may be more convenient to <a href="@export">export</a> strings for offline editing in a desktop Gettext translation editor.) Searches may be limited to strings in a specific language.', array('@export' => url('admin/config/regional/translate/export'))) . '</p>';
       return $output;
@@ -59,12 +49,6 @@ function locale_help($path, $arg) {
 
     case 'admin/config/regional/translate/export':
       return '<p>' . t('This page exports the translated strings used by your site. An export file may be in Gettext Portable Object (<em>.po</em>) form, which includes both the original string and the translation (used to share translations with others), or in Gettext Portable Object Template (<em>.pot</em>) form, which includes the original strings only (used to create new translations with a Gettext translation editor).') . '</p>';
-
-    case 'admin/structure/block/manage/%/%':
-      if ($arg[4] == 'locale' && $arg[5] == 'language') {
-        return '<p>' . t('This block is only shown if <a href="@languages">at least two languages are enabled</a> and <a href="@configuration">language negotiation</a> is set to <em>URL</em> or <em>Session</em>.', array('@languages' => url('admin/config/regional/language'), '@configuration' => url('admin/config/regional/language/detection'))) . '</p>';
-      }
-      break;
   }
 }
 
@@ -72,33 +56,6 @@ function locale_help($path, $arg) {
  * Implements hook_menu().
  */
 function locale_menu() {
-  // Language negotiation.
-  $items['admin/config/regional/language/detection'] = array(
-    'title' => 'Detection and selection',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('language_negotiation_configure_form'),
-    'access arguments' => array('administer languages'),
-    'weight' => 10,
-    'file' => 'locale.admin.inc',
-    'type' => MENU_LOCAL_TASK,
-  );
-  $items['admin/config/regional/language/detection/url'] = array(
-    'title' => 'URL language detection configuration',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('language_negotiation_configure_url_form'),
-    'access arguments' => array('administer languages'),
-    'file' => 'locale.admin.inc',
-    'type' => MENU_VISIBLE_IN_BREADCRUMB,
-  );
-  $items['admin/config/regional/language/detection/session'] = array(
-    'title' => 'Session language detection configuration',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('language_negotiation_configure_session_form'),
-    'access arguments' => array('administer languages'),
-    'file' => 'locale.admin.inc',
-    'type' => MENU_VISIBLE_IN_BREADCRUMB,
-  );
-
   // Translation functionality.
   $items['admin/config/regional/translate'] = array(
     'title' => 'User interface translation',
@@ -333,9 +290,6 @@ function locale_field_node_form_submit($form, &$form_state) {
  */
 function locale_theme() {
   return array(
-    'language_negotiation_configure_form' => array(
-      'render element' => 'form',
-    ),
     'locale_date_format_form' => array(
       'render element' => 'form',
     ),
@@ -405,140 +359,9 @@ function locale_entity_info_alter(&$entity_info) {
 }
 
 /**
- * Implements hook_language_types_info().
- *
- * Defines the three core language types:
- * - Interface language is the only configurable language type in core. It is
- *   used by t() as the default language if none is specified.
- * - Content language is by default non-configurable and inherits the interface
- *   language negotiated value. It is used by the Field API to determine the
- *   display language for fields if no explicit value is specified.
- * - URL language is by default non-configurable and is determined through the
- *   URL language negotiation method or the URL fallback language negotiation
- *   method if no language can be detected. It is used by l() as the default
- *   language if none is specified.
- */
-function locale_language_types_info() {
-  require_once DRUPAL_ROOT . '/core/includes/locale.inc';
-  return array(
-    LANGUAGE_TYPE_INTERFACE => array(
-      'name' => t('User interface text'),
-      'description' => t('Order of language detection methods for user interface text. If a translation of user interface text is available in the detected language, it will be displayed.'),
-    ),
-    LANGUAGE_TYPE_CONTENT => array(
-      'name' => t('Content'),
-      'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'),
-      'fixed' => array(LANGUAGE_NEGOTIATION_INTERFACE),
-    ),
-    LANGUAGE_TYPE_URL => array(
-      'fixed' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_URL_FALLBACK),
-    ),
-  );
-}
-
-/**
- * Implements hook_language_negotiation_info().
- */
-function locale_language_negotiation_info() {
-  require_once DRUPAL_ROOT . '/core/includes/locale.inc';
-  $file = '/core/includes/locale.inc';
-  $negotiation_info = array();
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_URL] = array(
-    'types' => array(LANGUAGE_TYPE_CONTENT, LANGUAGE_TYPE_INTERFACE, LANGUAGE_TYPE_URL),
-    'callbacks' => array(
-      'negotiation' => 'locale_language_from_url',
-      'language_switch' => 'locale_language_switcher_url',
-      'url_rewrite' => 'locale_language_url_rewrite_url',
-    ),
-    'file' => $file,
-    'weight' => -8,
-    'name' => t('URL'),
-    'description' => t('Determine the language from the URL (Path prefix or domain).'),
-    'config' => 'admin/config/regional/language/detection/url',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_SESSION] = array(
-    'callbacks' => array(
-      'negotiation' => 'locale_language_from_session',
-      'language_switch' => 'locale_language_switcher_session',
-      'url_rewrite' => 'locale_language_url_rewrite_session',
-    ),
-    'file' => $file,
-    'weight' => -6,
-    'name' => t('Session'),
-    'description' => t('Determine the language from a request/session parameter.'),
-    'config' => 'admin/config/regional/language/detection/session',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_USER] = array(
-    'callbacks' => array('negotiation' => 'locale_language_from_user'),
-    'file' => $file,
-    'weight' => -4,
-    'name' => t('User'),
-    'description' => t("Follow the user's language preference."),
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_BROWSER] = array(
-    'callbacks' => array('negotiation' => 'locale_language_from_browser'),
-    'file' => $file,
-    'weight' => -2,
-    'cache' => 0,
-    'name' => t('Browser'),
-    'description' => t("Determine the language from the browser's language settings."),
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE] = array(
-    'types' => array(LANGUAGE_TYPE_CONTENT),
-    'callbacks' => array('negotiation' => 'locale_language_from_interface'),
-    'file' => $file,
-    'weight' => 8,
-    'name' => t('Interface'),
-    'description' => t('Use the detected interface language.'),
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_URL_FALLBACK] = array(
-    'types' => array(LANGUAGE_TYPE_URL),
-    'callbacks' => array('negotiation' => 'locale_language_url_fallback'),
-    'file' => $file,
-    'weight' => 8,
-    'name' => t('URL fallback'),
-    'description' => t('Use an already detected language for URLs if none is found.'),
-  );
-
-  return $negotiation_info;
-}
-
-/**
- * Implements hook_modules_enabled().
- */
-function locale_modules_enabled($modules) {
-  include_once DRUPAL_ROOT . '/core/includes/language.inc';
-  language_types_set();
-  language_negotiation_purge();
-}
-
-/**
- * Implements hook_modules_disabled().
- */
-function locale_modules_disabled($modules) {
-  locale_modules_enabled($modules);
-}
-
-/**
  * Implements hook_language_insert().
  */
 function locale_language_insert($language) {
-  // Add new language to the list of language prefixes.
-  $prefixes = locale_language_negotiation_url_prefixes();
-  $prefixes[$language->langcode] = (empty($language->default) ? $language->langcode : '');
-  locale_language_negotiation_url_prefixes_save($prefixes);
-
-  // Add language to the list of language domains.
-  $domains = locale_language_negotiation_url_domains();
-  $domains[$language->langcode] = '';
-  locale_language_negotiation_url_domains_save($domains);
-
   // @todo move these two cache clears out. See http://drupal.org/node/1293252
   // Changing the language settings impacts the interface.
   cache('page')->flush();
@@ -550,19 +373,6 @@ function locale_language_insert($language) {
  * Implements hook_language_update().
  */
 function locale_language_update($language) {
-
-  // If the language is the default, then ensure that no other languages have
-  // blank prefix codes.
-  if (!empty($language->default)) {
-    $prefixes = locale_language_negotiation_url_prefixes();
-    foreach ($prefixes as $langcode => $prefix) {
-      if ($prefix == '' && $langcode != $language->langcode) {
-        $prefixes[$langcode] = $langcode;
-      }
-    }
-    locale_language_negotiation_url_prefixes_save($prefixes);
-  }
-
   // @todo move these two cache clears out. See http://drupal.org/node/1293252
   // Changing the language settings impacts the interface.
   cache('page')->flush();
@@ -574,16 +384,6 @@ function locale_language_update($language) {
  * Implements hook_language_delete().
  */
 function locale_language_delete($language) {
-  // Remove language from language prefix list.
-  $prefixes = locale_language_negotiation_url_prefixes();
-  unset($prefixes[$language->langcode]);
-  locale_language_negotiation_url_prefixes_save($prefixes);
-
-  // Remove language from language domain list.
-  $domains = locale_language_negotiation_url_domains();
-  unset($domains[$language->langcode]);
-  locale_language_negotiation_url_domains_save($domains);
-
   // Remove translations.
   db_delete('locales_target')
     ->condition('language', $language->langcode)
@@ -894,103 +694,6 @@ function locale_library_info_alter(&$libraries, $module) {
   }
 }
 
-// ---------------------------------------------------------------------------------
-// Language switcher block
-
-/**
- * Implements hook_block_info().
- */
-function locale_block_info() {
-  include_once DRUPAL_ROOT . '/core/includes/language.inc';
-  $block = array();
-  $info = language_types_info();
-  foreach (language_types_get_configurable(FALSE) as $type) {
-    $block[$type] = array(
-      'info' => t('Language switcher (@type)', array('@type' => $info[$type]['name'])),
-      // Not worth caching.
-      'cache' => DRUPAL_NO_CACHE,
-    );
-  }
-  return $block;
-}
-
-/**
- * Implements hook_block_view().
- *
- * Displays a language switcher. Only show if we have at least two languages.
- */
-function locale_block_view($type) {
-  if (language_multilingual()) {
-    $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
-    $links = language_negotiation_get_switch_links($type, $path);
-
-    if (isset($links->links)) {
-      drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css');
-      $class = "language-switcher-{$links->method_id}";
-      $variables = array('links' => $links->links, 'attributes' => array('class' => array($class)));
-      $block['content'] = theme('links__locale_block', $variables);
-      $block['subject'] = t('Languages');
-      return $block;
-    }
-  }
-}
-
-/**
- * Implements hook_preprocess_block().
- */
-function locale_preprocess_block(&$variables) {
-  if ($variables['block']->module == 'locale') {
-    $variables['attributes_array']['role'] = 'navigation';
-  }
-}
-
-/**
- * Implements hook_url_outbound_alter().
- *
- * Rewrite outbound URLs with language based prefixes.
- */
-function locale_url_outbound_alter(&$path, &$options, $original_path) {
-  // Only modify internal URLs.
-  if (!$options['external'] && language_multilingual()) {
-    static $drupal_static_fast;
-    if (!isset($drupal_static_fast)) {
-      $drupal_static_fast['callbacks'] = &drupal_static(__FUNCTION__);
-    }
-    $callbacks = &$drupal_static_fast['callbacks'];
-
-    if (!isset($callbacks)) {
-      $callbacks = array();
-      include_once DRUPAL_ROOT . '/core/includes/language.inc';
-
-      foreach (language_types_get_configurable() as $type) {
-        // Get URL rewriter callbacks only from enabled language methods.
-        $negotiation = variable_get("language_negotiation_$type", array());
-
-        foreach ($negotiation as $method_id => $method) {
-          if (isset($method['callbacks']['url_rewrite'])) {
-            if (isset($method['file'])) {
-              require_once DRUPAL_ROOT . '/' . $method['file'];
-            }
-            // Avoid duplicate callback entries.
-            $callbacks[$method['callbacks']['url_rewrite']] = TRUE;
-          }
-        }
-      }
-
-      $callbacks = array_keys($callbacks);
-    }
-
-    foreach ($callbacks as $callback) {
-      $callback($path, $options);
-    }
-
-    // No language dependent path allowed in this mode.
-    if (empty($callbacks)) {
-      unset($options['language']);
-    }
-  }
-}
-
 /**
  * Implements hook_form_FORM_ID_alter() for language_admin_overview_form().
  */
