diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index b08697f..b03594b 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -168,17 +168,17 @@ define('LANGUAGE_NONE', 'und');
 /**
  * The type of language used to define the content language.
  */
-define('LANGUAGE_TYPE_CONTENT', 'language_content');
+define('LANGUAGE_TYPE_CONTENT', 'content');
 
 /**
  * The type of language used to select the user interface.
  */
-define('LANGUAGE_TYPE_INTERFACE', 'language');
+define('LANGUAGE_TYPE_INTERFACE', 'ui');
 
 /**
  * The type of language used for URLs.
  */
-define('LANGUAGE_TYPE_URL', 'language_url');
+define('LANGUAGE_TYPE_URL', 'url');
 
 /**
  * Language written left to right. Possible value of $language->direction.
@@ -1253,12 +1253,12 @@ function drupal_unpack($obj, $field = 'data') {
  * @ingroup sanitization
  */
 function t($string, array $args = array(), array $options = array()) {
-  global $language;
+  $context = drupal_get_context();
   static $custom_strings;
 
   // Merge in default.
   if (empty($options['langcode'])) {
-    $options['langcode'] = isset($language->language) ? $language->language : 'en';
+    $options['langcode'] = isset($context['language:ui']->language) ? $context['language:ui']->language : 'en';
   }
   if (empty($options['context'])) {
     $options['context'] = '';
@@ -1920,10 +1920,6 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
           _drupal_bootstrap_page_header();
           break;
 
-        case DRUPAL_BOOTSTRAP_LANGUAGE:
-          drupal_language_initialize();
-          break;
-
         case DRUPAL_BOOTSTRAP_FULL:
           require_once DRUPAL_ROOT . '/includes/common.inc';
           _drupal_bootstrap_full();
@@ -2280,31 +2276,6 @@ function get_t() {
 }
 
 /**
- * Initialize all the defined language types.
- */
-function drupal_language_initialize() {
-  $types = language_types();
-
-  // Ensure the language is correctly returned, even without multilanguage
-  // support. Also make sure we have a $language fallback, in case a language
-  // negotiation callback needs to do a full bootstrap.
-  // Useful for eg. XML/HTML 'lang' attributes.
-  $default = language_default();
-  foreach ($types as $type) {
-    $GLOBALS[$type] = $default;
-  }
-  if (drupal_multilingual()) {
-    include_once DRUPAL_ROOT . '/includes/language.inc';
-    foreach ($types as $type) {
-      $GLOBALS[$type] = language_initialize($type);
-    }
-    // Allow modules to react on language system initialization in multilingual
-    // environments.
-    bootstrap_invoke_all('language_init');
-  }
-}
-
-/**
  * The built-in language types.
  *
  * @return
diff --git a/includes/common.inc b/includes/common.inc
index 1ef681f..f3948f8 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -1579,8 +1579,8 @@ function filter_xss_bad_protocol($string, $decode = TRUE) {
  * Arbitrary elements may be added using the $args associative array.
  */
 function format_rss_channel($title, $link, $description, $items, $langcode = NULL, $args = array()) {
-  global $language_content;
-  $langcode = $langcode ? $langcode : $language_content->language;
+  $context = drupal_get_context();
+  $langcode = $langcode ? $langcode : $context['language:content']->language;
 
   $output = "<channel>\n";
   $output .= ' <title>' . check_plain($title) . "</title>\n";
@@ -1856,6 +1856,7 @@ function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
  *   A translated date string in the requested format.
  */
 function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
+  $context = drupal_get_context();
   // Use the advanced drupal_static() pattern, since this is called very often.
   static $drupal_static_fast;
   if (!isset($drupal_static_fast)) {
@@ -1873,9 +1874,8 @@ function format_date($timestamp, $type = 'medium', $format = '', $timezone = NUL
   }
 
   // Use the default langcode if none is set.
-  global $language;
   if (empty($langcode)) {
-    $langcode = isset($language->language) ? $language->language : 'en';
+    $langcode = $context['language:ui']->language;
   }
 
   switch ($type) {
@@ -2040,7 +2040,7 @@ function format_username($account) {
  *   - 'external': Whether the given path is an external URL.
  *   - 'language': An optional language object. If the path being linked to is
  *     internal to the site, $options['language'] is used to look up the alias
- *     for the URL. If $options['language'] is omitted, the global $language_url
+ *     for the URL. If $options['language'] is omitted, $context['language:url']
  *     will be used.
  *   - 'https': Whether this URL should point to a secure location. If not
  *     defined, the current scheme is used, so the user stays on http or https
@@ -2305,7 +2305,7 @@ function drupal_attributes(array $attributes = array()) {
  *   An HTML string containing a link to the given path.
  */
 function l($text, $path, array $options = array()) {
-  global $language_url;
+  $context = drupal_get_context();
   static $use_theme = NULL;
 
   // Merge in defaults.
@@ -2316,7 +2316,7 @@ function l($text, $path, array $options = array()) {
 
   // Append active class.
   if (($path == $_GET['q'] || ($path == '<front>' && drupal_is_front_page())) &&
-      (empty($options['language']) || $options['language']->language == $language_url->language)) {
+      (empty($options['language']) || $options['language']->language == $context['language:url']->language)) {
     $options['attributes']['class'][] = 'active';
   }
 
@@ -5950,6 +5950,7 @@ function drupal_render_cache_by_query($query, $function, $expire = CACHE_TEMPORA
  *   $granularity was passed in, more parts are added.
  */
 function drupal_render_cid_parts($granularity = NULL) {
+  $context = drupal_get_context();
   global $theme, $base_root, $user;
 
   $cid_parts[] = $theme;
@@ -5957,7 +5958,7 @@ function drupal_render_cid_parts($granularity = NULL) {
   // part.
   if (drupal_multilingual()) {
     foreach (language_types_configurable() as $language_type) {
-      $cid_parts[] = $GLOBALS[$language_type]->language;
+      $cid_parts[] = $context["language:$language_type"]->language;
     }
   }
 
@@ -7277,7 +7278,7 @@ function drupal_check_incompatibility($v, $current_version) {
  *   to return an array with info about all types.
  */
 function entity_get_info($entity_type = NULL) {
-  global $language;
+  $context = drupal_get_context();
 
   // Use the advanced drupal_static() pattern, since this is called very often.
   static $drupal_static_fast;
@@ -7288,7 +7289,7 @@ function entity_get_info($entity_type = NULL) {
 
   // hook_entity_info() includes translated strings, so each language is cached
   // separately.
-  $langcode = $language->language;
+  $langcode = $context['language:ui']->language;
 
   if (empty($entity_info)) {
     if ($cache = cache_get("entity_info:$langcode")) {
@@ -7526,7 +7527,8 @@ function entity_get_controller($entity_type) {
  */
 function entity_prepare_view($entity_type, $entities, $langcode = NULL) {
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $context = drupal_get_context();
+    $langcode = $context['language:content']->language;
   }
 
   // To ensure hooks are only run once per entity, check for an
diff --git a/includes/context.inc b/includes/context.inc
index 7fab597..614ebcc 100644
--- a/includes/context.inc
+++ b/includes/context.inc
@@ -536,6 +536,33 @@ class ContextHandlerHttp extends ContextHandlerAbstract {
 }
 
 /**
+ * Context handler for different language keys.
+ */
+class ContextHandlerLanguage extends ContextHandlerAbstract {
+  function getValue(Array $args = array()) {
+    static $types;
+    static $default;
+    $type = $args[0];
+
+    if (empty($types)) {
+      $types = language_types();
+    }
+    if (empty($default)) {
+      $default = language_default();
+    }
+
+    if (!isset($types[$type])) {
+      $types[$type] = $default;
+      if (drupal_multilingual()) {
+        include_once DRUPAL_ROOT . '/includes/language.inc';
+        $types[$type] = language_initialize($type);
+      }
+    }
+    return $types[$type];
+  }
+}
+
+/**
  * Interface for context value objects.
  *
  * ContextValueInterface includes a method - contextKey() - that will return a
diff --git a/includes/install.core.inc b/includes/install.core.inc
index a74dfdf..ee25379 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -242,6 +242,7 @@ function install_begin_request(&$install_state) {
 
   require_once DRUPAL_ROOT . '/modules/system/system.install';
   require_once DRUPAL_ROOT . '/includes/common.inc';
+  require_once DRUPAL_ROOT . '/includes/context.inc';
   require_once DRUPAL_ROOT . '/includes/file.inc';
   require_once DRUPAL_ROOT . '/includes/install.inc';
   require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc');
@@ -250,9 +251,6 @@ function install_begin_request(&$install_state) {
   include_once DRUPAL_ROOT . '/includes/module.inc';
   include_once DRUPAL_ROOT . '/includes/session.inc';
 
-  // Set up $language, so t() caller functions will still work.
-  drupal_language_initialize();
-
   include_once DRUPAL_ROOT . '/includes/entity.inc';
   require_once DRUPAL_ROOT . '/includes/ajax.inc';
   $module_list['system']['filename'] = 'modules/system/system.module';
diff --git a/includes/locale.inc b/includes/locale.inc
index 780459b..14eb830 100644
--- a/includes/locale.inc
+++ b/includes/locale.inc
@@ -82,8 +82,8 @@ define('LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN', 1);
  *   The current interface language code.
  */
 function locale_language_from_interface() {
-  global $language;
-  return isset($language->language) ? $language->language : FALSE;
+  $context = drupal_get_context();
+  return $context['language:ui']->language;
 }
 
 /**
@@ -252,6 +252,7 @@ function locale_language_from_url($languages) {
  *   A valid language code.
  */
 function locale_language_url_fallback($language = NULL, $language_type = LANGUAGE_TYPE_INTERFACE) {
+  $context = drupal_get_context();
   $default = language_default();
   $prefix = (variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX);
 
@@ -262,7 +263,7 @@ function locale_language_url_fallback($language = NULL, $language_type = LANGUAG
     return $default->language;
   }
   else {
-    return $GLOBALS[$language_type]->language;
+    return $context["language:$language_type"]->language;
   }
 }
 
@@ -324,6 +325,8 @@ function locale_language_switcher_session($type, $path) {
  * Rewrite URLs for the URL language provider.
  */
 function locale_language_url_rewrite_url(&$path, &$options) {
+  $context = drupal_get_context();
+
   static $drupal_static_fast;
   if (!isset($drupal_static_fast)) {
     $drupal_static_fast['languages'] = &drupal_static(__FUNCTION__);
@@ -337,8 +340,7 @@ function locale_language_url_rewrite_url(&$path, &$options) {
 
   // 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;
+    $options['language'] = $context['language:url'];
   }
   // We allow only enabled languages here.
   elseif (!isset($languages[$options['language']->language])) {
@@ -1369,8 +1371,6 @@ function _locale_import_parse_quoted($string) {
  * Drupal.formatPlural() and inserts them into the database.
  */
 function _locale_parse_js_file($filepath) {
-  global $language;
-
   // The file path might contain a query string, so make sure we only use the
   // actual file.
   $parsed_url = drupal_parse_url($filepath);
@@ -1712,7 +1712,8 @@ function _locale_invalidate_js($langcode = NULL) {
  */
 function _locale_rebuild_js($langcode = NULL) {
   if (!isset($langcode)) {
-    global $language;
+    $context = drupal_get_context();
+    $language = $context['language:ui'];
   }
   else {
     // Get information about the locale.
diff --git a/includes/menu.inc b/includes/menu.inc
index 3e775db..a815b1d 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -1086,12 +1086,13 @@ function menu_tree_output($tree) {
  *   An tree of menu links in an array, in the order they should be rendered.
  */
 function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) {
+  $context = drupal_get_context();
   $tree = &drupal_static(__FUNCTION__, array());
 
   // Use $mlid as a flag for whether the data being loaded is for the whole tree.
   $mlid = isset($link['mlid']) ? $link['mlid'] : 0;
   // Generate a cache ID (cid) specific for this $menu_name, $link, $language, and depth.
-  $cid = 'links:' . $menu_name . ':all:' . $mlid . ':' . $GLOBALS['language']->language . ':' . (int) $max_depth;
+  $cid = 'links:' . $menu_name . ':all:' . $mlid . ':' . $context['language:ui']->language . ':' . (int) $max_depth;
 
   if (!isset($tree[$cid])) {
     // If the static variable doesn't have the data, check {cache_menu}.
@@ -1156,6 +1157,7 @@ function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) {
  *   same structure described for the top-level array.
  */
 function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = FALSE) {
+  $context = drupal_get_context();
   $tree = &drupal_static(__FUNCTION__, array());
 
   // Load the menu item corresponding to the current page.
@@ -1164,7 +1166,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
       $max_depth = min($max_depth, MENU_MAX_DEPTH);
     }
     // Generate a cache ID (cid) specific for this page.
-    $cid = 'links:' . $menu_name . ':page:' . $item['href'] . ':' . $GLOBALS['language']->language . ':' . (int) $item['access'] . ':' . (int) $max_depth;
+    $cid = 'links:' . $menu_name . ':page:' . $item['href'] . ':' . $context['language:ui']->language . ':' . (int) $item['access'] . ':' . (int) $max_depth;
     // If we are asked for the active trail only, and $menu_name has not been
     // built and cached for this page yet, then this likely means that it
     // won't be built anymore, as this function is invoked from
@@ -1305,6 +1307,7 @@ function menu_build_tree($menu_name, array $parameters = array()) {
  * @see menu_build_tree()
  */
 function _menu_build_tree($menu_name, array $parameters = array()) {
+  $context = drupal_get_context();
   // Static cache of already built menu trees.
   $trees = &drupal_static(__FUNCTION__, array());
 
@@ -1313,7 +1316,7 @@ function _menu_build_tree($menu_name, array $parameters = array()) {
   if (isset($parameters['expanded'])) {
     sort($parameters['expanded']);
   }
-  $tree_cid = 'links:' . $menu_name . ':tree-data:' . $GLOBALS['language']->language . ':' . hash('sha256', serialize($parameters));
+  $tree_cid = 'links:' . $menu_name . ':tree-data:' . $context['language:ui']->language . ':' . hash('sha256', serialize($parameters));
 
   // If we do not have this tree in the static cache, check {cache_menu}.
   if (!isset($trees[$tree_cid])) {
diff --git a/includes/path.inc b/includes/path.inc
index db60537..e8452bf 100644
--- a/includes/path.inc
+++ b/includes/path.inc
@@ -43,7 +43,7 @@ function drupal_path_initialize() {
  *   found.
  */
 function drupal_lookup_path($action, $path = '', $path_language = NULL) {
-  global $language_url;
+  $context = drupal_get_context();
   // Use the advanced drupal_static() pattern, since this is called very often.
   static $drupal_static_fast;
   if (!isset($drupal_static_fast)) {
@@ -74,7 +74,7 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
   // language. If we used a language different from the one conveyed by the
   // requested URL, we might end up being unable to check if there is a path
   // alias matching the URL path.
-  $path_language = $path_language ? $path_language : $language_url->language;
+  $path_language = $path_language ? $path_language : $context['language:url']->language;
 
   if ($action == 'wipe') {
     $cache = array();
diff --git a/includes/theme.inc b/includes/theme.inc
index 6c2b640..b2370fd 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -1409,10 +1409,10 @@ function theme_link($variables) {
  *     http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
  */
 function theme_links($variables) {
+  $context = drupal_get_context();
   $links = $variables['links'];
   $attributes = $variables['attributes'];
   $heading = $variables['heading'];
-  global $language_url;
   $output = '';
 
   if (count($links) > 0) {
@@ -1453,7 +1453,7 @@ function theme_links($variables) {
         $class[] = 'last';
       }
       if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
-          && (empty($link['language']) || $link['language']->language == $language_url->language)) {
+          && (empty($link['language']) || $link['language']->language == $context['language:url']->language)) {
         $class[] = 'active';
       }
       $output .= '<li' . drupal_attributes(array('class' => $class)) . '>';
@@ -2151,6 +2151,7 @@ function template_process(&$variables, $hook) {
  * @see html.tpl.php
  */
 function template_preprocess_html(&$variables) {
+  $context = drupal_get_context();
   // Compile a list of classes that are going to be applied to the body element.
   // This allows advanced theming based on context (home page, node of certain type, etc.).
   // Add a class that tells us whether we're on the front page or not.
@@ -2194,8 +2195,8 @@ function template_preprocess_html(&$variables) {
   // using an associated GRDDL profile.
   $variables['rdf_namespaces']    = drupal_get_rdf_namespaces();
   $variables['grddl_profile']     = 'http://www.w3.org/1999/xhtml/vocab';
-  $variables['language']          = $GLOBALS['language'];
-  $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
+  $variables['language']          = $context['language:ui'];
+  $variables['language']->dir     = $context['language:ui']->direction ? 'rtl' : 'ltr';
 
   // Add favicon.
   if (theme_get_setting('toggle_favicon')) {
@@ -2244,6 +2245,7 @@ function template_preprocess_html(&$variables) {
  * @see page.tpl.php
  */
 function template_preprocess_page(&$variables) {
+  $context = drupal_get_context();
   // Move some variables to the top level for themer convenience and template cleanliness.
   $variables['show_messages'] = $variables['page']['#show_messages'];
 
@@ -2265,8 +2267,8 @@ function template_preprocess_page(&$variables) {
   $variables['base_path']         = base_path();
   $variables['front_page']        = url();
   $variables['feed_icons']        = drupal_get_feeds();
-  $variables['language']          = $GLOBALS['language'];
-  $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
+  $variables['language']          = $context['language:ui'];
+  $variables['language']->dir     = $context['language:ui']->direction ? 'rtl' : 'ltr';
   $variables['logo']              = theme_get_setting('logo');
   $variables['main_menu']         = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array();
   $variables['secondary_menu']    = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array();
@@ -2421,6 +2423,7 @@ function theme_get_suggestions($args, $base, $delimiter = '__') {
  * @see maintenance-page.tpl.php
  */
 function template_preprocess_maintenance_page(&$variables) {
+  $context = drupal_get_context();
   // Add favicon
   if (theme_get_setting('toggle_favicon')) {
     $favicon = theme_get_setting('favicon');
@@ -2463,9 +2466,6 @@ function template_preprocess_maintenance_page(&$variables) {
     }
   }
 
-  // set the default language if necessary
-  $language = isset($GLOBALS['language']) ? $GLOBALS['language'] : language_default();
-
   $variables['head_title_array']  = $head_title;
   $variables['head_title']        = implode(' | ', $head_title);
   $variables['base_path']         = base_path();
@@ -2473,8 +2473,8 @@ function template_preprocess_maintenance_page(&$variables) {
   $variables['breadcrumb']        = '';
   $variables['feed_icons']        = '';
   $variables['help']              = '';
-  $variables['language']          = $language;
-  $variables['language']->dir     = $language->direction ? 'rtl' : 'ltr';
+  $variables['language']          = $context['language:ui'];
+  $variables['language']->dir     = $context['language:ui']->direction ? 'rtl' : 'ltr';
   $variables['logo']              = theme_get_setting('logo');
   $variables['messages']          = $variables['show_messages'] ? theme('status_messages') : '';
   $variables['main_menu']         = array();
diff --git a/modules/block/block.api.php b/modules/block/block.api.php
index d33f594..81479df 100644
--- a/modules/block/block.api.php
+++ b/modules/block/block.api.php
@@ -319,7 +319,8 @@ function hook_block_view_MODULE_DELTA_alter(&$data, $block) {
  *   An array of $blocks, keyed by the block ID.
  */
 function hook_block_list_alter(&$blocks) {
-  global $language, $theme_key;
+  $context = drupal_get_context();
+  global $theme_key;
 
   // This example shows how to achieve language specific visibility setting for
   // blocks.
@@ -343,7 +344,7 @@ function hook_block_list_alter(&$blocks) {
       continue;
     }
 
-    if (!isset($block_languages[$block->module][$block->delta][$language->language])) {
+    if (!isset($block_languages[$block->module][$block->delta][$context['language:ui']->language])) {
       // This block should not be displayed with the active language, remove
       // from the list.
       unset($blocks[$key]);
diff --git a/modules/book/book.module b/modules/book/book.module
index beb1721..293d152 100644
--- a/modules/book/book.module
+++ b/modules/book/book.module
@@ -1112,12 +1112,13 @@ function book_toc($bid, $depth_limit, $exclude = array()) {
  * @see book-export-html.tpl.php
  */
 function template_preprocess_book_export_html(&$variables) {
-  global $base_url, $language;
+  $context = drupal_get_context();
+  global $base_url;
 
   $variables['title'] = check_plain($variables['title']);
   $variables['base_url'] = $base_url;
-  $variables['language'] = $language;
-  $variables['language_rtl'] = ($language->direction == LANGUAGE_RTL);
+  $variables['language'] = $context['language:ui'];
+  $variables['language_rtl'] = ($context['language:ui']->direction == LANGUAGE_RTL);
   $variables['head'] = drupal_get_html_head();
 }
 
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 393318a..2bd3819 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -914,8 +914,9 @@ function comment_prepare_thread(&$comments) {
  *   An array as expected by drupal_render().
  */
 function comment_view($comment, $node, $view_mode = 'full', $langcode = NULL) {
+  $context = drupal_get_context();
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $langcode = $context['language:content']->language;
   }
 
   // Populate $comment->content with a render() array.
@@ -981,8 +982,9 @@ function comment_view($comment, $node, $view_mode = 'full', $langcode = NULL) {
  *   content language of the current request.
  */
 function comment_build_content($comment, $node, $view_mode = 'full', $langcode = NULL) {
+  $context = drupal_get_context();
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $langcode = $context['language:content']->language;
   }
 
   // Remove previously built content, if exists.
diff --git a/modules/comment/comment.test b/modules/comment/comment.test
index c9478f4..369b555 100644
--- a/modules/comment/comment.test
+++ b/modules/comment/comment.test
@@ -1718,10 +1718,10 @@ class CommentTokenReplaceTestCase extends CommentHelperCase {
    * Creates a comment, then tests the tokens generated from it.
    */
   function testCommentTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
     $url_options = array(
       'absolute' => TRUE,
-      'language' => $language,
+      'language' => $context['language:ui'],
     );
 
     $this->drupalLogin($this->admin_user);
@@ -1754,8 +1754,8 @@ class CommentTokenReplaceTestCase extends CommentHelperCase {
     $tests['[comment:body]'] = _text_sanitize($instance, LANGUAGE_NONE, $comment->comment_body[LANGUAGE_NONE][0], 'value');
     $tests['[comment:url]'] = url('comment/' . $comment->cid, $url_options + array('fragment' => 'comment-' . $comment->cid));
     $tests['[comment:edit-url]'] = url('comment/' . $comment->cid . '/edit', $url_options);
-    $tests['[comment:created:since]'] = format_interval(REQUEST_TIME - $comment->created, 2, $language->language);
-    $tests['[comment:changed:since]'] = format_interval(REQUEST_TIME - $comment->changed, 2, $language->language);
+    $tests['[comment:created:since]'] = format_interval(REQUEST_TIME - $comment->created, 2, $context['language:ui']->language);
+    $tests['[comment:changed:since]'] = format_interval(REQUEST_TIME - $comment->changed, 2, $context['langiage:ui']->language);
     $tests['[comment:parent:cid]'] = $comment->pid;
     $tests['[comment:parent:title]'] = check_plain($parent_comment->subject);
     $tests['[comment:node:nid]'] = $comment->nid;
@@ -1767,7 +1767,7 @@ class CommentTokenReplaceTestCase extends CommentHelperCase {
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('comment' => $comment), array('language' => $language));
+      $output = token_replace($input, array('comment' => $comment), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized comment token %token replaced.', array('%token' => $input)));
     }
 
@@ -1783,7 +1783,7 @@ class CommentTokenReplaceTestCase extends CommentHelperCase {
     $tests['[comment:author:name]'] = $this->admin_user->name;
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('comment' => $comment), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array('comment' => $comment), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized comment token %token replaced.', array('%token' => $input)));
     }
 
@@ -1796,7 +1796,7 @@ class CommentTokenReplaceTestCase extends CommentHelperCase {
     $tests['[node:comment-count-new]'] = 2;
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('node' => $node), array('language' => $language));
+      $output = token_replace($input, array('node' => $node), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Node comment token %token replaced.', array('%token' => $input)));
     }
   }
diff --git a/modules/contact/contact.pages.inc b/modules/contact/contact.pages.inc
index 30b2825..b09c135 100644
--- a/modules/contact/contact.pages.inc
+++ b/modules/contact/contact.pages.inc
@@ -126,7 +126,8 @@ function contact_site_form_validate($form, &$form_state) {
  * Form submission handler for contact_site_form().
  */
 function contact_site_form_submit($form, &$form_state) {
-  global $user, $language;
+  $context = drupal_get_context();
+  global $user;
 
   $values = $form_state['values'];
   $values['sender'] = $user;
@@ -148,12 +149,12 @@ function contact_site_form_submit($form, &$form_state) {
 
   // If the user requests it, send a copy using the current language.
   if ($values['copy']) {
-    drupal_mail('contact', 'page_copy', $from, $language, $values, $from);
+    drupal_mail('contact', 'page_copy', $from, $context['language:ui'], $values, $from);
   }
 
   // Send an auto-reply if necessary using the current language.
   if ($values['category']['reply']) {
-    drupal_mail('contact', 'page_autoreply', $from, $language, $values, $to);
+    drupal_mail('contact', 'page_autoreply', $from, $context['language:ui'], $values, $to);
   }
 
   flood_register_event('contact', variable_get('contact_threshold_window', 3600));
@@ -258,7 +259,8 @@ function contact_personal_form_validate($form, &$form_state) {
  * @see contact_personal_form()
  */
 function contact_personal_form_submit($form, &$form_state) {
-  global $user, $language;
+  $context = drupal_get_context();
+  global $user;
 
   $values = $form_state['values'];
   $values['sender'] = $user;
@@ -279,7 +281,7 @@ function contact_personal_form_submit($form, &$form_state) {
 
   // Send a copy if requested, using current page language.
   if ($values['copy']) {
-    drupal_mail('contact', 'user_copy', $from, $language, $values, $from);
+    drupal_mail('contact', 'user_copy', $from, $context['language:ui'], $values, $from);
   }
 
   flood_register_event('contact', variable_get('contact_threshold_window', 3600));
diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc
index 6b172dd..857cbe9 100644
--- a/modules/field/field.info.inc
+++ b/modules/field/field.info.inc
@@ -66,12 +66,12 @@ function field_info_cache_clear() {
  *     as well as module, giving the module that exposes the entity type.
  */
 function _field_info_collate_types($reset = FALSE) {
-  global $language;
+  $context = drupal_get_context();
   static $info;
 
   // The _info() hooks invoked below include translated strings, so each
   // language is cached separately.
-  $langcode = $language->language;
+  $langcode = $context['language:ui']->language;
 
   if ($reset) {
     $info = NULL;
diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc
index 5373d97..8c7d83a 100644
--- a/modules/field/field.multilingual.inc
+++ b/modules/field/field.multilingual.inc
@@ -223,12 +223,12 @@ function field_has_translation_handler($entity_type, $handler = NULL) {
  *   A valid language code.
  */
 function field_valid_language($langcode, $default = TRUE) {
+  $context = drupal_get_context();
   $enabled_languages = field_content_languages();
   if (in_array($langcode, $enabled_languages)) {
     return $langcode;
   }
-  global $language_content;
-  return $default ? language_default('language') : $language_content->language;
+  return $default ? language_default('language') : $context['language:content']->language;
 }
 
 /**
diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test
index 32de9dc..00371ec 100644
--- a/modules/file/tests/file.test
+++ b/modules/file/tests/file.test
@@ -996,10 +996,10 @@ class FileTokenReplaceTestCase extends FileFieldTestCase {
    * Creates a file, then tests the tokens generated from it.
    */
   function testFileTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
     $url_options = array(
       'absolute' => TRUE,
-      'language' => $language,
+      'language' => $context['language:ui'],
     );
 
     // Create file field.
@@ -1026,8 +1026,8 @@ class FileTokenReplaceTestCase extends FileFieldTestCase {
     $tests['[file:mime]'] = check_plain($file->filemime);
     $tests['[file:size]'] = format_size($file->filesize);
     $tests['[file:url]'] = check_plain(file_create_url($file->uri));
-    $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $language->language);
-    $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $language->language);
+    $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $context['language:ui']->language);
+    $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $context['language:ui']->language);
     $tests['[file:owner]'] = check_plain(format_username($this->admin_user));
     $tests['[file:owner:uid]'] = $file->uid;
 
@@ -1035,7 +1035,7 @@ class FileTokenReplaceTestCase extends FileFieldTestCase {
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('file' => $file), array('language' => $language));
+      $output = token_replace($input, array('file' => $file), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized file token %token replaced.', array('%token' => $input)));
     }
 
@@ -1046,7 +1046,7 @@ class FileTokenReplaceTestCase extends FileFieldTestCase {
     $tests['[file:size]'] = format_size($file->filesize);
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('file' => $file), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array('file' => $file), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized file token %token replaced.', array('%token' => $input)));
     }
   }
diff --git a/modules/filter/filter.module b/modules/filter/filter.module
index 9e14812..a423deb 100644
--- a/modules/filter/filter.module
+++ b/modules/filter/filter.module
@@ -390,12 +390,13 @@ function filter_modules_disabled($modules) {
  * @see filter_formats_reset()
  */
 function filter_formats($account = NULL) {
-  global $language;
+  $context = drupal_get_context();
   $formats = &drupal_static(__FUNCTION__, array());
+  $langcode = $context['language:ui']->language;
 
   // All available formats are cached for performance.
   if (!isset($formats['all'])) {
-    if ($cache = cache_get("filter_formats:{$language->language}")) {
+    if ($cache = cache_get("filter_formats:{$langcode}")) {
       $formats['all'] = $cache->data;
     }
     else {
@@ -407,7 +408,7 @@ function filter_formats($account = NULL) {
         ->execute()
         ->fetchAllAssoc('format');
 
-      cache_set("filter_formats:{$language->language}", $formats['all']);
+      cache_set("filter_formats:{$langcode}", $formats['all']);
     }
   }
 
diff --git a/modules/image/image.module b/modules/image/image.module
index 008a365..5370f3a 100644
--- a/modules/image/image.module
+++ b/modules/image/image.module
@@ -943,11 +943,11 @@ function image_default_style_revert($style) {
  * @see image_effect_definition_load()
  */
 function image_effect_definitions() {
-  global $language;
+  $context = drupal_get_context();
 
   // hook_image_effect_info() includes translated strings, so each language is
   // cached separately.
-  $langcode = $language->language;
+  $langcode = $context['language:ui']->language;
 
   $effects = &drupal_static(__FUNCTION__);
 
diff --git a/modules/locale/locale.api.php b/modules/locale/locale.api.php
index e327401..9c19226 100644
--- a/modules/locale/locale.api.php
+++ b/modules/locale/locale.api.php
@@ -24,9 +24,10 @@
  * did not happen yet and thus they cannot rely on translated variables.
  */
 function hook_language_init() {
-  global $language, $conf;
+  $context = drupal_get_context();
+  global $conf;
 
-  switch ($language->language) {
+  switch ($context['language:ui']->language) {
     case 'it':
       $conf['site_name'] = 'Il mio sito Drupal';
       break;
@@ -52,10 +53,11 @@ function hook_language_init() {
  *   The current path.
  */
 function hook_language_switch_links_alter(array &$links, $type, $path) {
-  global $language;
+  $context = drupal_get_context();
+  $langcode = $context['language:ui']->language;
 
-  if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language->language])) {
-    foreach ($links[$language->language] as $link) {
+  if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$langcode])) {
+    foreach ($links[$langcode] as $link) {
       $link['attributes']['class'][] = 'active-language';
     }
   }
diff --git a/modules/locale/locale.module b/modules/locale/locale.module
index da8ad13..3a861e3 100644
--- a/modules/locale/locale.module
+++ b/modules/locale/locale.module
@@ -218,12 +218,20 @@ function locale_menu() {
 }
 
 /**
+ * Implements hook_context_init().
+ */
+function locale_context_init(DrupalContextInterface $context) {
+
+}
+
+/**
  * Implements hook_init().
  *
  * Initialize date formats according to the user's current locale.
  */
 function locale_init() {
-  global $conf, $language;
+  global $conf;
+  $context = drupal_get_context();
   include_once DRUPAL_ROOT . '/includes/locale.inc';
 
   // For each date type (e.g. long, short), get the localized date format
@@ -232,7 +240,7 @@ function locale_init() {
   // settings page, where we want to display the site default and not the
   // localized version.
   if (strpos($_GET['q'], 'admin/config/regional/date-time/formats') !== 0) {
-    $languages = array($language->language);
+    $languages = array($context['language:ui']->language);
 
     // Setup appropriate date formats for this locale.
     $formats = locale_get_localized_date_format($languages);
@@ -263,12 +271,12 @@ function locale_permission() {
  * @see locale_form_alter()
  */
 function locale_language_selector_form(&$form, &$form_state, $user) {
-  global $language;
+  $context = drupal_get_context();
   $languages = language_list('enabled');
   $languages = $languages[1];
 
   // If the user is being created, we set the user language to the page language.
-  $user_preferred_language = $user->uid ? user_preferred_language($user) : $language;
+  $user_preferred_language = $user->uid ? user_preferred_language($user) : $context['language:ui'];
 
   $names = array();
   foreach ($languages as $langcode => $item) {
@@ -618,7 +626,7 @@ function locale_modules_disabled($modules) {
  *   Language code to use for the lookup.
  */
 function locale($string = NULL, $context = NULL, $langcode = NULL) {
-  global $language;
+  $context = drupal_get_context();
   $locale_t = &drupal_static(__FUNCTION__);
 
   if (!isset($string)) {
@@ -626,11 +634,11 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) {
     return $locale_t;
   }
 
-  $langcode = isset($langcode) ? $langcode : $language->language;
+  $langcode = isset($langcode) ? $langcode : $context['language:ui']->language;
 
   // Store database cached translations in a static variable. Only build the
   // cache after $language has been set to avoid an unnecessary cache rebuild.
-  if (!isset($locale_t[$langcode]) && isset($language)) {
+  if (!isset($locale_t[$langcode]) && isset($context['language:ui'])) {
     $locale_t[$langcode] = array();
     // Disabling the usage of string caching allows a module to watch for
     // the exact list of strings used on a page. From a performance
@@ -717,11 +725,11 @@ function locale_reset() {
  *   what is used to display the page.
  */
 function locale_get_plural($count, $langcode = NULL) {
-  global $language;
+  $context = drupal_get_context();
   $locale_formula = &drupal_static(__FUNCTION__, array());
   $plurals = &drupal_static(__FUNCTION__ . ':plurals', array());
 
-  $langcode = $langcode ? $langcode : $language->language;
+  $langcode = $langcode ? $langcode : $context['language:ui']->language;
 
   if (!isset($plurals[$langcode][$count])) {
     if (empty($locale_formula)) {
@@ -820,7 +828,7 @@ function locale_system_update($components) {
  * file if necessary, and adds it to the page.
  */
 function locale_js_alter(&$javascript) {
-  global $language;
+  $context = drupal_get_context();
 
   $dir = 'public://' . variable_get('locale_js_directory', 'languages');
   $parsed = variable_get('javascript_parsed', array());
@@ -852,11 +860,11 @@ function locale_js_alter(&$javascript) {
   }
 
   // If necessary, rebuild the translation file for the current language.
-  if (!empty($parsed['refresh:' . $language->language])) {
+  if (!empty($parsed['refresh:' . $context['language:ui']->language])) {
     // Don't clear the refresh flag on failure, so that another try will
     // be performed later.
     if (_locale_rebuild_js()) {
-      unset($parsed['refresh:' . $language->language]);
+      unset($parsed['refresh:' . $context['language:ui']->language]);
     }
     // Store any changes after refresh was attempted.
     variable_set('javascript_parsed', $parsed);
@@ -868,9 +876,9 @@ function locale_js_alter(&$javascript) {
   }
 
   // Add the translation JavaScript file to the page.
-  if ($files && !empty($language->javascript)) {
+  if ($files && !empty($context['language:ui']->javascript)) {
     // Add the translation JavaScript file to the page.
-    $file = $dir . '/' . $language->language . '_' . $language->javascript . '.js';
+    $file = $dir . '/' . $context['language:ui']->language . '_' . $context['language:ui']->javascript . '.js';
     $javascript[$file] = drupal_js_defaults($file);
   }
 }
@@ -882,10 +890,10 @@ function locale_js_alter(&$javascript) {
  * and checks to see if a related right to left CSS file should be included.
  */
 function locale_css_alter(&$css) {
-  global $language;
+  $context = drupal_get_context();
 
   // If the current language is RTL, add the CSS file with the RTL overrides.
-  if ($language->direction == LANGUAGE_RTL) {
+  if ($context['language:ui']->direction == LANGUAGE_RTL) {
     foreach ($css as $data => $item) {
       // Only provide RTL overrides for files.
       if ($item['type'] == 'file') {
@@ -908,14 +916,14 @@ function locale_css_alter(&$css) {
  * Provides the language support for the jQuery UI Date Picker.
  */
 function locale_library_alter(&$libraries, $module) {
-  global $language;
+  $context = drupal_get_context();
   if ($module == 'system' && isset($libraries['system']['ui.datepicker'])) {
     $datepicker = drupal_get_path('module', 'locale') . '/locale.datepicker.js';
     $libraries['system']['ui.datepicker']['js'][$datepicker] = array('group' => JS_THEME);
     $libraries['system']['ui.datepicker']['js'][] = array(
       'data' => array(
         'jqueryuidatepicker' => array(
-          'rtl' => $language->direction == LANGUAGE_RTL,
+          'rtl' => $context['language:ui']->direction == LANGUAGE_RTL,
           'firstDay' => variable_get('date_first_day', 0),
         ),
       ),
@@ -1020,7 +1028,7 @@ function locale_form_comment_form_alter(&$form, &$form_state, $form_id) {
   // If a content type has multilingual support we set the content language as
   // comment language.
   if ($form['language']['#value'] == LANGUAGE_NONE && locale_multilingual_node_type($form['#node']->type)) {
-    global $language_content;
-    $form['language']['#value'] = $language_content->language;
+    $context = drupal_get_context();
+    $form['language']['#value'] = $context['language:content']->language;
   }
 }
diff --git a/modules/locale/locale.test b/modules/locale/locale.test
index 6dad7e0..7ff4d80 100644
--- a/modules/locale/locale.test
+++ b/modules/locale/locale.test
@@ -1094,6 +1094,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
    * Check if the values of the Locale variables are correct after uninstall.
    */
   function testUninstallProcess() {
+    $context = drupal_get_context();
     $locale_module = array('locale');
 
     // Add a new language and optionally set it as default.
@@ -1101,9 +1102,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
     locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language == 'fr');
 
     // Check the UI language.
-    drupal_language_initialize();
-    global $language;
-    $this->assertEqual($language->language, $this->language, t('Current language: %lang', array('%lang' => $language->language)));
+    $this->assertEqual($context['language:ui']->language, $this->language, t('Current language: %lang', array('%lang' => $context['language:ui']->language)));
 
     // Enable multilingual workflow option for articles.
     variable_set('language_content_type_article', 1);
@@ -1148,7 +1147,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
 
     // Check the init language logic.
     drupal_language_initialize();
-    $this->assertEqual($language->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language->language)));
+    $this->assertEqual($context['language:ui']->language, 'en', t('Language after uninstall: %lang', array('%lang' => $context['language:ui']->language)));
 
     // Check JavaScript files deletion.
     $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
diff --git a/modules/node/node.module b/modules/node/node.module
index 1ecc093..35d1eac 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -668,7 +668,8 @@ function node_type_update_nodes($old_type, $type) {
  *   type object by $type->disabled being set to TRUE.
  */
 function _node_types_build($rebuild = FALSE) {
-  $cid = 'node_types:' . $GLOBALS['language']->language;
+  $context = drupal_get_context();
+  $cid = 'node_types:' . $context['language:ui']->language;
 
   if (!$rebuild) {
     $_node_types = &drupal_static(__FUNCTION__);
@@ -1278,7 +1279,8 @@ function node_revision_delete($revision_id) {
  */
 function node_view($node, $view_mode = 'full', $langcode = NULL) {
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $context = drupal_get_context();
+    $langcode = $context['language:content']->language;
   }
 
   // Populate $node->content with a render() array.
@@ -1339,8 +1341,9 @@ function node_view($node, $view_mode = 'full', $langcode = NULL) {
  *   content language of the current request.
  */
 function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
+  $context = drupal_get_context();
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $langcode = $context['language:content']->language;
   }
 
   // Remove previously built content, if exists.
@@ -2446,7 +2449,8 @@ function node_block_list_alter(&$blocks) {
  *   The link should be an absolute URL.
  */
 function node_feed($nids = FALSE, $channel = array()) {
-  global $base_url, $language_content;
+  $context = drupal_get_context();
+  global $base_url;
 
   if ($nids === FALSE) {
     $nids = db_select('node', 'n')
@@ -2501,7 +2505,7 @@ function node_feed($nids = FALSE, $channel = array()) {
     'title'       => variable_get('site_name', 'Drupal'),
     'link'        => $base_url,
     'description' => variable_get('feed_description', ''),
-    'language'    => $language_content->language
+    'language'    => $context['language:content']->language
   );
   $channel_extras = array_diff_key($channel, $channel_defaults);
   $channel = array_merge($channel_defaults, $channel);
diff --git a/modules/node/node.test b/modules/node/node.test
index 56a2d34..53fb849 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -2237,10 +2237,10 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
    * Creates a node, then tests the tokens generated from it.
    */
   function testNodeTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
     $url_options = array(
       'absolute' => TRUE,
-      'language' => $language,
+      'language' => $context['language:ui'],
     );
 
     // Create a user and a node.
@@ -2272,14 +2272,14 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
     $tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options);
     $tests['[node:author:uid]'] = $node->uid;
     $tests['[node:author:name]'] = check_plain(format_username($account));
-    $tests['[node:created:since]'] = format_interval(REQUEST_TIME - $node->created, 2, $language->language);
-    $tests['[node:changed:since]'] = format_interval(REQUEST_TIME - $node->changed, 2, $language->language);
+    $tests['[node:created:since]'] = format_interval(REQUEST_TIME - $node->created, 2, $context['language:ui']->language);
+    $tests['[node:changed:since]'] = format_interval(REQUEST_TIME - $node->changed, 2, $context['language:ui']->language);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('node' => $node), array('language' => $language));
+      $output = token_replace($input, array('node' => $node), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized node token %token replaced.', array('%token' => $input)));
     }
 
@@ -2291,7 +2291,7 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
     $tests['[node:author:name]'] = format_username($account);
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('node' => $node), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array('node' => $node), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized node token %token replaced.', array('%token' => $input)));
     }
   }
diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc
index 6945f34..00b00bf 100644
--- a/modules/openid/openid.inc
+++ b/modules/openid/openid.inc
@@ -88,10 +88,10 @@ function openid_redirect_http($url, $message) {
  * Creates a js auto-submit redirect for (for the 2.x protocol)
  */
 function openid_redirect($url, $message) {
-  global $language;
+  $context = drupal_get_context();
   
   $output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n";
-  $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $language->language . '" lang="' . $language->language . '">' . "\n";
+  $output .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $context['language:ui']->language . '" lang="' . $context['language:ui']->language . '">' . "\n";
   $output .= "<head>\n";
   $output .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
   $output .= "<title>" . t('OpenID redirect') . "</title>\n";
diff --git a/modules/path/path.test b/modules/path/path.test
index f42ec81..52e291d 100644
--- a/modules/path/path.test
+++ b/modules/path/path.test
@@ -455,7 +455,6 @@ class PathMonolingualTestCase extends DrupalWebTestCase {
   }
 
   function setUp() {
-    global $language;
     parent::setUp('path', 'locale', 'translation');
 
     // Create and login user.
@@ -482,9 +481,6 @@ class PathMonolingualTestCase extends DrupalWebTestCase {
     // Set language detection to URL.
     $edit = array('language[enabled][locale-url]' => TRUE);
     $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
-
-    // Force languages to be initialized.
-    drupal_language_initialize();
   }
 
   /**
diff --git a/modules/poll/poll.test b/modules/poll/poll.test
index d6c4f40..5cb8fb6 100644
--- a/modules/poll/poll.test
+++ b/modules/poll/poll.test
@@ -615,7 +615,7 @@ class PollTokenReplaceTestCase extends PollTestCase {
    * Creates a poll, then tests the tokens generated from it.
    */
   function testPollTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
 
     // Craete a poll with three choices.
     $title = $this->randomName();
@@ -664,13 +664,13 @@ class PollTokenReplaceTestCase extends PollTestCase {
     $tests['[node:poll-winner]'] = filter_xss($poll->choice[1]['chtext']);
     $tests['[node:poll-winner-votes]'] = 2;
     $tests['[node:poll-winner-percent]'] = 50;
-    $tests['[node:poll-duration]'] = format_interval($poll->runtime, 1, $language->language);
+    $tests['[node:poll-duration]'] = format_interval($poll->runtime, 1, $context['language:ui']->language);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('node' => $poll), array('language' => $language));
+      $output = token_replace($input, array('node' => $poll), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized poll token %token replaced.', array('%token' => $input)));
     }
 
@@ -678,7 +678,7 @@ class PollTokenReplaceTestCase extends PollTestCase {
     $tests['[node:poll-winner]'] = $poll->choice[1]['chtext'];
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('node' => $poll), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array('node' => $poll), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized poll token %token replaced.', array('%token' => $input)));
     }
   }
diff --git a/modules/search/search.pages.inc b/modules/search/search.pages.inc
index 833ea8b..f1465bf 100644
--- a/modules/search/search.pages.inc
+++ b/modules/search/search.pages.inc
@@ -104,12 +104,12 @@ function template_preprocess_search_results(&$variables) {
  * @see search-result.tpl.php
  */
 function template_preprocess_search_result(&$variables) {
-  global $language;
+  $context = drupal_get_context();
 
   $result = $variables['result'];
   $variables['url'] = check_url($result['link']);
   $variables['title'] = check_plain($result['title']);
-  if (isset($result['language']) && $result['language'] != $language->language && $result['language'] != LANGUAGE_NONE) {
+  if (isset($result['language']) && $result['language'] != $context['language:ui']->language && $result['language'] != LANGUAGE_NONE) {
     $variables['title_attributes_array']['xml:lang'] = $result['language'];
     $variables['content_attributes_array']['xml:lang'] = $result['language'];
   }
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index 5c39cfc..7f25b51 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -1229,7 +1229,9 @@ class DrupalWebTestCase extends DrupalTestCase {
    *   either a single array or a variable number of string arguments.
    */
   protected function setUp() {
-    global $user, $language, $conf;
+    global $user, $conf;
+    $context = drupal_get_context();
+    $language = $context['language:ui'];
 
     // Generate a temporary prefixed database to ensure that tests have a clean starting point.
     $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000);
@@ -1444,7 +1446,9 @@ class DrupalWebTestCase extends DrupalTestCase {
    * and reset the database prefix.
    */
   protected function tearDown() {
-    global $user, $language;
+    global $user;
+    $context = drupal_get_context();
+    $language = $context['language:ui'];
 
     // In case a fatal error occured that was not in the test process read the
     // log to pick up any fatal errors.
diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test
index 5f69673..a74b374 100644
--- a/modules/simpletest/tests/common.test
+++ b/modules/simpletest/tests/common.test
@@ -776,9 +776,9 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase {
    * Tests Locale module's CSS Alter to include RTL overrides.
    */
   function testAlter() {
+    $context = drupal_get_context();
     // Switch the language to a right to left language and add system.base.css.
-    global $language;
-    $language->direction = LANGUAGE_RTL;
+    $context['language:ui']->direction = LANGUAGE_RTL;
     $path = drupal_get_path('module', 'system');
     drupal_add_css($path . '/system.base.css');
 
@@ -787,7 +787,7 @@ class CascadingStylesheetsTestCase extends DrupalWebTestCase {
     $this->assert(strpos($styles, $path . '/system.base-rtl.css') !== FALSE, t('CSS is alterable as right to left overrides are added.'));
 
     // Change the language back to left to right.
-    $language->direction = LANGUAGE_LTR;
+    $context['language:ui']->direction = LANGUAGE_LTR;
   }
 
   /**
@@ -2215,7 +2215,8 @@ class FormatDateUnitTest extends DrupalWebTestCase {
    * Tests for the format_date() function.
    */
   function testFormatDate() {
-    global $user, $language;
+    $context = drupal_get_context();
+    global $user;
 
     $timestamp = strtotime('2007-03-26T00:00:00+00:00');
     $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', t('Test all parameters.'));
@@ -2248,8 +2249,8 @@ class FormatDateUnitTest extends DrupalWebTestCase {
     // Save the original user and language and then replace it with the test user and language.
     $real_user = $user;
     $user = user_load($test_user->uid, TRUE);
-    $real_language = $language->language;
-    $language->language = $user->language;
+    $real_language = $context['language:ui']->language;
+    $context['language:ui']->language = $user->language;
     // Simulate a Drupal bootstrap with the logged-in user.
     date_default_timezone_set(drupal_get_user_timezone());
 
@@ -2263,7 +2264,7 @@ class FormatDateUnitTest extends DrupalWebTestCase {
 
     // Restore the original user and language, and enable session saving.
     $user = $real_user;
-    $language->language = $real_language;
+    $context['language:ui']->language = $real_language;
     // Restore default time zone.
     date_default_timezone_set(drupal_get_user_timezone());
     drupal_save_session(TRUE);
diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test
index a6c7b40..9212843 100644
--- a/modules/simpletest/tests/mail.test
+++ b/modules/simpletest/tests/mail.test
@@ -32,10 +32,10 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface {
    * Assert that the pluggable mail system is functional.
    */
   function testPluggableFramework() {
-    global $language;
+    $context = drupal_get_context();
 
     // Use MailTestCase for sending a message.
-    $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language);
+    $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $context['language:ui']);
 
     // Assert whether the message was sent through the send function.
     $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.'));
diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test
index 1ef8525..94859d7 100644
--- a/modules/simpletest/tests/upgrade/upgrade.test
+++ b/modules/simpletest/tests/upgrade/upgrade.test
@@ -31,7 +31,8 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
    * Override of DrupalWebTestCase::setUp() specialized for upgrade testing.
    */
   protected function setUp() {
-    global $user, $language, $conf;
+    $context = drupal_get_context();
+    global $user, $conf;
 
     // Load the Update API.
     require_once DRUPAL_ROOT . '/includes/update.inc';
@@ -60,7 +61,7 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
     Database::addConnectionInfo('default', 'default', $connection_info['default']);
 
     // Store necessary current values before switching to prefixed database.
-    $this->originalLanguage = $language;
+    $this->originalLanguage = $context['language:ui'];
     $this->originalLanguageDefault = variable_get('language_default');
     $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
     $this->originalProfile = drupal_get_profile();
@@ -135,7 +136,9 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
    * Override of DrupalWebTestCase::tearDown() specialized for upgrade testing.
    */
   protected function tearDown() {
-    global $user, $language;
+    global $user;
+    $context = drupal_get_context();
+    $language = $context['language:ui'];
 
     // In case a fatal error occured that was not in the test process read the
     // log to pick up any fatal errors.
diff --git a/modules/statistics/statistics.test b/modules/statistics/statistics.test
index 126828f..f7bfcd5 100644
--- a/modules/statistics/statistics.test
+++ b/modules/statistics/statistics.test
@@ -415,7 +415,7 @@ class StatisticsTokenReplaceTestCase extends StatisticsTestCase {
    * Creates a node, then tests the statistics tokens generated from it.
    */
   function testStatisticsTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
 
     // Create user and node.
     $user = $this->drupalCreateUser(array('create page content'));
@@ -437,7 +437,7 @@ class StatisticsTokenReplaceTestCase extends StatisticsTestCase {
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('node' => $node), array('language' => $language));
+      $output = token_replace($input, array('node' => $node), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Statistics token %token replaced.', array('%token' => $input)));
     }
   }
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index ab3e0fc..293c6f6 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -2258,7 +2258,8 @@ function hook_xmlrpc_alter(&$methods) {
  *   - message: The text of the message to be logged.
  */
 function hook_watchdog(array $log_entry) {
-  global $base_url, $language;
+  $context = drupal_get_context();
+  global $base_url;
 
   $severity_list = array(
     WATCHDOG_EMERGENCY     => t('Emergency'),
@@ -2304,7 +2305,7 @@ function hook_watchdog(array $log_entry) {
     '@message'       => strip_tags($log_entry['message']),
   ));
 
-  drupal_mail('emaillog', 'entry', $to, $language, $params);
+  drupal_mail('emaillog', 'entry', $to, $context['language:ui'], $params);
 }
 
 /**
diff --git a/modules/system/system.module b/modules/system/system.module
index b427372..d53484b 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -1920,6 +1920,7 @@ function system_context_init(DrupalContextInterface $butler) {
   $butler->registerHandler('http:get', 'ContextHandlerHttp', array('query' => 'GET'));
   $butler->registerHandler('http:post', 'ContextHandlerHttp', array('query' => 'POST'));
   $butler->registerHandler('http:header', 'ContextHandlerHeader');
+  $butler->registerHandler('language', 'ContextHandlerLanguage');
 }
 
 
diff --git a/modules/system/system.test b/modules/system/system.test
index 0e293bc..dece9c4 100644
--- a/modules/system/system.test
+++ b/modules/system/system.test
@@ -1808,11 +1808,12 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
    * Creates a user and a node, then tests the tokens generated from them.
    */
   function testTokenReplacement() {
+    $context = drupal_get_context();
     // Create the initial objects.
     $account = $this->drupalCreateUser();
     $node = $this->drupalCreateNode(array('uid' => $account->uid));
     $node->title = '<blink>Blinking Text</blink>';
-    global $user, $language;
+    global $user;
 
     $source  = '[node:title]';         // Title of the node we passed in
     $source .= '[node:author:name]';   // Node author's name
@@ -1824,18 +1825,18 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
 
     $target  = check_plain($node->title);
     $target .= check_plain($account->name);
-    $target .= format_interval(REQUEST_TIME - $node->created, 2, $language->language);
+    $target .= format_interval(REQUEST_TIME - $node->created, 2, $context['language:ui']->language);
     $target .= check_plain($user->name);
-    $target .= format_date(REQUEST_TIME, 'short', '', NULL, $language->language);
+    $target .= format_date(REQUEST_TIME, 'short', '', NULL, $context['language:ui']->language);
 
     // Test that the clear parameter cleans out non-existent tokens.
-    $result = token_replace($source, array('node' => $node), array('language' => $language, 'clear' => TRUE));
+    $result = token_replace($source, array('node' => $node), array('language' => $context['language:ui'], 'clear' => TRUE));
     $result = $this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens cleared out.');
 
     // Test without using the clear parameter (non-existant token untouched).
     $target .= '[user:name]';
     $target .= '[bogus:token]';
-    $result = token_replace($source, array('node' => $node), array('language' => $language));
+    $result = token_replace($source, array('node' => $node), array('language' => $context['language:ui']));
     $this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens ignored.');
 
     // Check that the results of token_generate are sanitized properly. This does NOT
@@ -1854,7 +1855,7 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
    * Test whether token-replacement works in various contexts.
    */
   function testSystemTokenRecognition() {
-    global $language;
+    $context = drupal_get_context();
 
     // Generate prefixes and suffixes for the token context.
     $tests = array(
@@ -1874,7 +1875,7 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
     foreach ($tests as $test) {
       $input = $test['prefix'] . '[site:name]' . $test['suffix'];
       $expected = $test['prefix'] . 'Drupal' . $test['suffix'];
-      $output = token_replace($input, array(), array('language' => $language));
+      $output = token_replace($input, array(), array('language' => $context['language:ui']));
       $this->assertTrue($output == $expected, t('Token recognized in string %string', array('%string' => $input)));
     }
   }
@@ -1883,10 +1884,10 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
    * Tests the generation of all system site information tokens.
    */
   function testSystemSiteTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
     $url_options = array(
       'absolute' => TRUE,
-      'language' => $language,
+      'language' => $context['language:ui'],
     );
 
     // Set a few site variables.
@@ -1906,7 +1907,7 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array(), array('language' => $language));
+      $output = token_replace($input, array(), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized system site information token %token replaced.', array('%token' => $input)));
     }
 
@@ -1915,7 +1916,7 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
     $tests['[site:slogan]'] = variable_get('site_slogan', '');
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array(), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array(), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized system site information token %token replaced.', array('%token' => $input)));
     }
   }
@@ -1924,25 +1925,25 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
    * Tests the generation of all system date tokens.
    */
   function testSystemDateTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
 
     // Set time to one hour before request.
     $date = REQUEST_TIME - 3600;
 
     // Generate and test tokens.
     $tests = array();
-    $tests['[date:short]'] = format_date($date, 'short', '', NULL, $language->language);
-    $tests['[date:medium]'] = format_date($date, 'medium', '', NULL, $language->language);
-    $tests['[date:long]'] = format_date($date, 'long', '', NULL, $language->language);
-    $tests['[date:custom:m/j/Y]'] = format_date($date, 'custom', 'm/j/Y', NULL, $language->language);
-    $tests['[date:since]'] = format_interval((REQUEST_TIME - $date), 2, $language->language);
+    $tests['[date:short]'] = format_date($date, 'short', '', NULL, $context['language:ui']->language);
+    $tests['[date:medium]'] = format_date($date, 'medium', '', NULL, $context['language:ui']->language);
+    $tests['[date:long]'] = format_date($date, 'long', '', NULL, $context['language:ui']->language);
+    $tests['[date:custom:m/j/Y]'] = format_date($date, 'custom', 'm/j/Y', NULL, $context['language:ui']->language);
+    $tests['[date:since]'] = format_interval((REQUEST_TIME - $date), 2, $context['language:ui']->language);
     $tests['[date:raw]'] = filter_xss($date);
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('date' => $date), array('language' => $language));
+      $output = token_replace($input, array('date' => $date), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Date token %token replaced.', array('%token' => $input)));
     }
   }
diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module
index eb81870..574e0b2 100644
--- a/modules/taxonomy/taxonomy.module
+++ b/modules/taxonomy/taxonomy.module
@@ -723,8 +723,9 @@ function taxonomy_term_delete($tid) {
  *   An array as expected by drupal_render().
  */
 function taxonomy_term_view($term, $view_mode = 'full', $langcode = NULL) {
+  $context = drupal_get_context();
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $langcode = $context['language:content']->language;
   }
 
   field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode, $langcode);
diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test
index aa7cc2e..002e410 100644
--- a/modules/taxonomy/taxonomy.test
+++ b/modules/taxonomy/taxonomy.test
@@ -1147,7 +1147,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
    * Creates some terms and a node, then tests the tokens generated from them.
    */
   function testTaxonomyTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
 
     // Create two taxonomy terms.
     $term1 = $this->createTerm($this->vocabulary);
@@ -1176,7 +1176,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
     $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name);
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('term' => $term1), array('language' => $language));
+      $output = token_replace($input, array('term' => $term1), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
 
@@ -1196,7 +1196,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('term' => $term2), array('language' => $language));
+      $output = token_replace($input, array('term' => $term2), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
 
@@ -1207,7 +1207,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
     $tests['[term:vocabulary:name]'] = $this->vocabulary->name;
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('term' => $term2), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array('term' => $term2), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized taxonomy term token %token replaced.', array('%token' => $input)));
     }
 
@@ -1223,7 +1223,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language));
+      $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
     }
 
@@ -1232,7 +1232,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase {
     $tests['[vocabulary:description]'] = $this->vocabulary->description;
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input)));
     }
   }
diff --git a/modules/user/user.module b/modules/user/user.module
index db0591f..d52e74b 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -2515,8 +2515,9 @@ function user_view_page($account) {
  *   An array as expected by drupal_render().
  */
 function user_view($account, $view_mode = 'full', $langcode = NULL) {
+  $context = drupal_get_context();
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $langcode = $context['language:content']->language;
   }
 
   // Retrieve all profile fields and attach to $account->content.
@@ -2552,8 +2553,9 @@ function user_view($account, $view_mode = 'full', $langcode = NULL) {
  *   content language of the current request.
  */
 function user_build_content($account, $view_mode = 'full', $langcode = NULL) {
+  $context = drupal_get_context();
   if (!isset($langcode)) {
-    $langcode = $GLOBALS['language_content']->language;
+    $langcode = $context['language:content']->language;
   }
 
   // Remove previously built content, if exists.
diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc
index 09bf33b..a7a4380 100644
--- a/modules/user/user.pages.inc
+++ b/modules/user/user.pages.inc
@@ -72,11 +72,11 @@ function user_pass_validate($form, &$form_state) {
 }
 
 function user_pass_submit($form, &$form_state) {
-  global $language;
+  $context = drupal_get_context();
 
   $account = $form_state['values']['account'];
   // Mail one time login URL and instructions using current language.
-  _user_mail_notify('password_reset', $account, $language);
+  _user_mail_notify('password_reset', $account, $context['language:ui']);
   watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail));
   drupal_set_message(t('Further instructions have been sent to your e-mail address.'));
 
diff --git a/modules/user/user.test b/modules/user/user.test
index 6ecbfac..1b30786 100644
--- a/modules/user/user.test
+++ b/modules/user/user.test
@@ -1895,10 +1895,10 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
    * Creates a user, then tests the tokens generated from it.
    */
   function testUserTokenReplacement() {
-    global $language;
+    $context = drupal_get_context();
     $url_options = array(
       'absolute' => TRUE,
-      'language' => $language,
+      'language' => $context['language:ui'],
     );
 
     // Create two users and log them in one after another.
@@ -1918,17 +1918,17 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
     $tests['[user:mail]'] = check_plain($account->mail);
     $tests['[user:url]'] = url("user/$account->uid", $url_options);
     $tests['[user:edit-url]'] = url("user/$account->uid/edit", $url_options);
-    $tests['[user:last-login]'] = format_date($account->login, 'medium', '', NULL, $language->language);
-    $tests['[user:last-login:short]'] = format_date($account->login, 'short', '', NULL, $language->language);
-    $tests['[user:created]'] = format_date($account->created, 'medium', '', NULL, $language->language);
-    $tests['[user:created:short]'] = format_date($account->created, 'short', '', NULL, $language->language);
+    $tests['[user:last-login]'] = format_date($account->login, 'medium', '', NULL, $context['language:ui']->language);
+    $tests['[user:last-login:short]'] = format_date($account->login, 'short', '', NULL, $context['language:ui']->language);
+    $tests['[user:created]'] = format_date($account->created, 'medium', '', NULL, $context['language:ui']->language);
+    $tests['[user:created:short]'] = format_date($account->created, 'short', '', NULL, $context['language:ui']->language);
     $tests['[current-user:name]'] = check_plain(format_username($global_account));
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('user' => $account), array('language' => $language));
+      $output = token_replace($input, array('user' => $account), array('language' => $context['language:ui']));
       $this->assertEqual($output, $expected, t('Sanitized user token %token replaced.', array('%token' => $input)));
     }
 
@@ -1938,7 +1938,7 @@ class UserTokenReplaceTestCase extends DrupalWebTestCase {
     $tests['[current-user:name]'] = format_username($global_account);
 
     foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
+      $output = token_replace($input, array('user' => $account), array('language' => $context['language:ui'], 'sanitize' => FALSE));
       $this->assertEqual($output, $expected, t('Unsanitized user token %token replaced.', array('%token' => $input)));
     }
   }
