Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.340
diff -u -p -r1.340 bootstrap.inc
--- includes/bootstrap.inc	7 Jan 2010 04:54:18 -0000	1.340
+++ includes/bootstrap.inc	14 Jan 2010 02:52:16 -0000
@@ -189,12 +189,12 @@ define('LANGUAGE_NONE', 'und');
 /**
  * The type of language used to define the content language.
  */
-define('LANGUAGE_TYPE_CONTENT', 'language');
+define('LANGUAGE_TYPE_CONTENT', 'language_content');
 
 /**
  * The type of language used to select the user interface.
  */
-define('LANGUAGE_TYPE_INTERFACE', 'language_interface');
+define('LANGUAGE_TYPE_INTERFACE', 'language');
 
 /**
  * The type of language used for URLs.
@@ -1786,8 +1786,8 @@ function drupal_language_initialize() {
  */
 function drupal_language_types() {
   return array(
-    LANGUAGE_TYPE_CONTENT => TRUE,
     LANGUAGE_TYPE_INTERFACE => TRUE,
+    LANGUAGE_TYPE_CONTENT => FALSE,
     LANGUAGE_TYPE_URL => FALSE,
   );
 }
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1088
diff -u -p -r1.1088 common.inc
--- includes/common.inc	13 Jan 2010 13:03:26 -0000	1.1088
+++ includes/common.inc	14 Jan 2010 02:56:44 -0000
@@ -1514,12 +1514,12 @@ function fix_gpc_magic() {
  *   The translated string.
  */
 function t($string, array $args = array(), array $options = array()) {
-  global $language_interface;
+  global $language;
   static $custom_strings;
 
   // Merge in default.
   if (empty($options['langcode'])) {
-    $options['langcode'] = isset($language_interface->language) ? $language_interface->language : 'en';
+    $options['langcode'] = isset($language->language) ? $language->language : 'en';
   }
   if (empty($options['context'])) {
     $options['context'] = '';
Index: includes/language.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/language.inc,v
retrieving revision 1.24
diff -u -p -r1.24 language.inc
--- includes/language.inc	2 Dec 2009 19:26:21 -0000	1.24
+++ includes/language.inc	14 Jan 2010 03:35:12 -0000
@@ -37,17 +37,34 @@ function language_types_info() {
  * whose negotiation values are unchangable and defined while defining the
  * language type itself.
  *
+ * @param $stored
+ *   Optional. By default retrieves values from the 'language_types' variable to
+ *   avoid unnecessary hook invocations.
+ *   If set to FALSE retrieves values from the actual language type definitions.
+ *   This allows to react to alterations performed on the definitions by modules
+ *   installed after the 'language_types' variable is set.
+ *
  * @return
  *   An array of language type names.
  */
-function language_types_configurable() {
+function language_types_configurable($stored = TRUE) {
   $configurable = &drupal_static(__FUNCTION__);
 
-  if (!isset($configurable)) {
+  if ($stored && !isset($configurable)) {
     $types = variable_get('language_types', drupal_language_types());
     $configurable = array_keys(array_filter($types));
   }
 
+  if (!$stored) {
+    $result = array();
+    foreach (language_types_info() as $type => $info) {
+      if (!isset($info['fixed'])) {
+        $result[] = $type;
+      }
+    }
+    return $result;
+  }
+
   return $configurable;
 }
 
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.242
diff -u -p -r1.242 locale.inc
--- includes/locale.inc	13 Jan 2010 23:19:54 -0000	1.242
+++ includes/locale.inc	14 Jan 2010 02:54:52 -0000
@@ -41,12 +41,12 @@ define('LOCALE_LANGUAGE_NEGOTIATION_URL_
  */
 
 /**
- * Identify the language from the current content language.
+ * Identifies the language from the current interface language.
  *
  * @return
- *   The current content language code.
+ *   The current interface language code.
  */
-function locale_language_from_content() {
+function locale_language_from_interface() {
   global $language;
   return isset($language->language) ? $language->language : FALSE;
 }
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.374
diff -u -p -r1.374 menu.inc
--- includes/menu.inc	12 Jan 2010 06:09:04 -0000	1.374
+++ includes/menu.inc	14 Jan 2010 03:08:04 -0000
@@ -969,7 +969,7 @@ function menu_tree_all_data($menu_name, 
   // 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-cid:' . $mlid . ':' . $GLOBALS['language_interface']->language . ':' . (int)$max_depth;
+  $cid = 'links:' . $menu_name . ':all-cid:' . $mlid . ':' . $GLOBALS['language']->language . ':' . (int)$max_depth;
 
   if (!isset($tree[$cid])) {
     // If the static variable doesn't have the data, check {cache_menu}.
@@ -1081,7 +1081,7 @@ function menu_tree_page_data($menu_name,
       $max_depth = min($max_depth, MENU_MAX_DEPTH);
     }
     // Generate a cache ID (cid) specific for this page.
-    $cid = 'links:' . $menu_name . ':page-cid:' . $item['href'] . ':' . $GLOBALS['language_interface']->language . ':' . (int)$item['access'] . ':' . (int)$max_depth;
+    $cid = 'links:' . $menu_name . ':page-cid:' . $item['href'] . ':' . $GLOBALS['language']->language . ':' . (int)$item['access'] . ':' . (int)$max_depth;
 
     if (!isset($tree[$cid])) {
       // If the static variable doesn't have the data, check {cache_menu}.
@@ -1229,7 +1229,7 @@ function menu_tree_page_data($menu_name,
  * Helper function - compute the real cache ID for menu tree data.
  */
 function _menu_tree_cid($menu_name, $data) {
-  return 'links:' . $menu_name . ':tree-data:' . $GLOBALS['language_interface']->language . ':' . md5(serialize($data));
+  return 'links:' . $menu_name . ':tree-data:' . $GLOBALS['language']->language . ':' . md5(serialize($data));
 }
 
 /**
Index: includes/path.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/path.inc,v
retrieving revision 1.55
diff -u -p -r1.55 path.inc
--- includes/path.inc	7 Jan 2010 04:54:18 -0000	1.55
+++ includes/path.inc	14 Jan 2010 03:45:21 -0000
@@ -44,7 +44,7 @@ function drupal_path_initialize() {
  *   found.
  */
 function drupal_lookup_path($action, $path = '', $path_language = NULL) {
-  global $language;
+  global $language_content;
   // Use the advanced drupal_static() pattern, since this is called very often.
   static $drupal_static_fast;
   if (!isset($drupal_static_fast)) {
@@ -71,7 +71,7 @@ function drupal_lookup_path($action, $pa
     }
   }
 
-  $path_language = $path_language ? $path_language : $language->language;
+  $path_language = $path_language ? $path_language : $language_content->language;
 
   if ($action == 'wipe') {
     $cache = array();
Index: modules/field/field.multilingual.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.multilingual.inc,v
retrieving revision 1.6
diff -u -p -r1.6 field.multilingual.inc
--- modules/field/field.multilingual.inc	4 Dec 2009 16:49:46 -0000	1.6
+++ modules/field/field.multilingual.inc	14 Jan 2010 03:22:41 -0000
@@ -132,8 +132,8 @@ function field_multilingual_valid_langua
   if (in_array($langcode, $enabled_languages)) {
     return $langcode;
   }
-  global $language;
-  $langcode = $default ? language_default('language') : $language->language;
+  global $language_content;
+  $langcode = $default ? language_default('language') : $language_content->language;
   if (in_array($langcode, $enabled_languages)) {
     return $langcode;
   }
Index: modules/locale/locale.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.admin.inc,v
retrieving revision 1.1
diff -u -p -r1.1 locale.admin.inc
--- modules/locale/locale.admin.inc	8 Jan 2010 13:32:43 -0000	1.1
+++ modules/locale/locale.admin.inc	14 Jan 2010 01:56:04 -0000
@@ -479,7 +479,7 @@ function locale_languages_configure_form
   $form = array(
     '#submit' => array('locale_languages_configure_form_submit'),
     '#theme' => 'locale_languages_configure_form',
-    '#language_types' => language_types_configurable(),
+    '#language_types' => language_types_configurable(FALSE),
     '#language_types_info' => language_types_info(),
     '#language_providers' => language_negotiation_info(),
   );
Index: modules/locale/locale.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.api.php,v
retrieving revision 1.8
diff -u -p -r1.8 locale.api.php
--- modules/locale/locale.api.php	6 Jan 2010 03:53:40 -0000	1.8
+++ modules/locale/locale.api.php	14 Jan 2010 03:36:30 -0000
@@ -57,6 +57,9 @@ function hook_language_switch_links_alte
  *   the following key-value pairs:
  *   - "name": The human-readable language type identifier.
  *   - "description": A description of the language type.
+ *   - "fixed": An array of language providers keyed by provider id and having
+ *     weights as values. Defining this key makes the language type
+ *     non-configurable.
  */
 function hook_language_types_info() {
   return array(
@@ -64,6 +67,9 @@ function hook_language_types_info() {
       'name' => t('Custom language'),
       'description' => t('A custom language type.'),
     ),
+    'fixed_custom_language_type' => array(
+      'fixed' => array('custom_language_provider'),
+    ),
   );
 }
 
Index: modules/locale/locale.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.install,v
retrieving revision 1.55
diff -u -p -r1.55 locale.install
--- modules/locale/locale.install	9 Jan 2010 22:07:56 -0000	1.55
+++ modules/locale/locale.install	14 Jan 2010 01:41:44 -0000
@@ -70,10 +70,9 @@ function locale_update_7001() {
       break;
   }
 
-  // Save new language negotiation options: UI language is tied to content
-  // language as this was Drupal 6 behavior.
-  language_negotiation_set(LANGUAGE_TYPE_CONTENT, array_flip($negotiation));
-  language_negotiation_set(LANGUAGE_TYPE_INTERFACE, array(LOCALE_LANGUAGE_NEGOTIATION_CONTENT => 0));
+  // Save new language negotiation options.
+  language_negotiation_set(LANGUAGE_TYPE_INTERFACE, array_flip($negotiation));
+  language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE => 0));
   language_negotiation_set(LANGUAGE_TYPE_URL, array(LOCALE_LANGUAGE_NEGOTIATION_URL => 0));
 
   // Unset the old language negotiation system variable.
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.281
diff -u -p -r1.281 locale.module
--- modules/locale/locale.module	10 Jan 2010 19:06:47 -0000	1.281
+++ modules/locale/locale.module	14 Jan 2010 02:38:31 -0000
@@ -24,9 +24,9 @@ define('LOCALE_LANGUAGE_NEGOTIATION_URL'
 define('LOCALE_LANGUAGE_NEGOTIATION_BROWSER', 'locale-browser');
 
 /**
- * The language is determined using the current content language.
+ * The language is determined using the current interface language.
  */
-define('LOCALE_LANGUAGE_NEGOTIATION_CONTENT', 'locale-content');
+define('LOCALE_LANGUAGE_NEGOTIATION_INTERFACE', 'locale-interface');
 
 /**
  * The language is set based on the user language settings.
@@ -69,7 +69,7 @@ function locale_help($path, $arg) {
     case 'admin/config/regional/language/add':
       return '<p>' . t('Add all languages to be supported by your site. If your desired language is not available in the <em>Language name</em> drop-down, click <em>Custom language</em> and provide a language code and other details manually. When providing a language code manually, be sure to enter a standardized language code, since this code may be used by browsers to determine an appropriate display language.') . '</p>';
     case 'admin/config/regional/language/configure':
-      $output = '<p>' . t("Set which languages to use for content and for the administrative interface. Drag the detection methods into the order they should test for languages. The first method that gets a result will set the language for the relevant part of the site. <strong>Changing these settings may break all incoming URLs, use with caution in a production environment.</strong>") . '</p>';
+      $output = '<p>' . t("Set how system languages should be determined. Drag the detection methods into the order they should test for languages. The first method that gets a result will set the language for the relevant part of the site. <strong>Changing these settings may break all incoming URLs, use with caution in a production environment.</strong>") . '</p>';
       return $output;
     case 'admin/config/regional/language/configure/url':
       $output = '<p>' . t('Determine the language by examining the URL. Example: "http://example.com/de/contact" sets language to German based on the use of "de" as the path prefix. "http://de.example.com/contact" sets presentation language to German based on the use of "http://de.example.com" in the domain.') . '</p>';
@@ -479,17 +479,26 @@ function locale_entity_info_alter(&$enti
 
 /**
  * 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 provider. It is used by l() as the default language if none is
+ *   specified.
  */
 function locale_language_types_info() {
   return array(
-    LANGUAGE_TYPE_CONTENT => array(
-      'name' => t('Content'),
-      'description' => t('If a piece of content is available in multiple languages, the one matching the <em>content</em> language will be used.'),
-    ),
     LANGUAGE_TYPE_INTERFACE => array(
       'name' => t('Interface'),
       'description' => t('The interface labels will be displayed in the <em>interface</em> language.'),
     ),
+    LANGUAGE_TYPE_CONTENT => array(
+      'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_INTERFACE),
+    ),
     LANGUAGE_TYPE_URL => array(
       'fixed' => array(LOCALE_LANGUAGE_NEGOTIATION_URL),
     ),
@@ -547,13 +556,13 @@ function locale_language_negotiation_inf
     'description' => t('The language is determined from the browser\'s language settings.'),
   );
 
-  $providers[LOCALE_LANGUAGE_NEGOTIATION_CONTENT] = array(
-    'types' => array(LANGUAGE_TYPE_INTERFACE),
-    'callbacks' => array('language' => 'locale_language_from_content'),
+  $providers[LOCALE_LANGUAGE_NEGOTIATION_INTERFACE] = array(
+    'types' => array(LANGUAGE_TYPE_CONTENT),
+    'callbacks' => array('language' => 'locale_language_from_interface'),
     'file' => $file,
     'weight' => 8,
-    'name' => t('Content'),
-    'description' => t('The interface language is the same as the negotiated content language.'),
+    'name' => t('Interface'),
+    'description' => t('The content language is the same as the negotiated interface language.'),
   );
 
   return $providers;
Index: modules/locale/locale.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v
retrieving revision 1.60
diff -u -p -r1.60 locale.test
--- modules/locale/locale.test	10 Jan 2010 22:56:51 -0000	1.60
+++ modules/locale/locale.test	14 Jan 2010 03:27:08 -0000
@@ -964,11 +964,11 @@ class LocaleUninstallFunctionalTest exte
   /**
    * The default language set for the UI before uninstall.
    */
-  protected $language_interface;
+  protected $language;
 
   function setUp() {
     parent::setUp('locale');
-    $this->language_interface = 'en';
+    $this->language = 'en';
   }
 
   /**
@@ -979,12 +979,12 @@ class LocaleUninstallFunctionalTest exte
 
     // Add a new language and optionally set it as default.
     require_once DRUPAL_ROOT . '/includes/locale.inc';
-    locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language_interface == 'fr');
+    locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language == 'fr');
 
     // Check the UI language.
     drupal_language_initialize();
-    global $language_interface;
-    $this->assertEqual($language_interface->language, $this->language_interface, t('Current language: %lang', array('%lang' => $language_interface->language)));
+    global $language;
+    $this->assertEqual($language->language, $this->language, t('Current language: %lang', array('%lang' => $language->language)));
 
     // Enable multilingual workflow option for articles.
     variable_set('language_content_type_article', 1);
@@ -1030,7 +1030,7 @@ class LocaleUninstallFunctionalTest exte
 
     // Check the init language logic.
     drupal_language_initialize();
-    $this->assertEqual($language_interface->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language_interface->language)));
+    $this->assertEqual($language->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language->language)));
 
     // Check JavaScript files deletion.
     $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
@@ -1090,7 +1090,7 @@ class LocaleUninstallFrenchFunctionalTes
 
   function setUp() {
     parent::setUp();
-    $this->language_interface = 'fr';
+    $this->language = 'fr';
   }
 }
 
@@ -1121,8 +1121,9 @@ class LanguageSwitchingFunctionalTest ex
    */
   function testLanguageBlock() {
     // Enable the language switching block.
+    $language_type = LANGUAGE_TYPE_INTERFACE;
     $edit = array(
-      'locale_language[region]' => 'sidebar_first',
+      "locale_{$language_type}[region]" => 'sidebar_first',
     );
     $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
 
@@ -1135,14 +1136,14 @@ class LanguageSwitchingFunctionalTest ex
     // Set language negotiation.
     drupal_load('module', 'locale');
     include_once DRUPAL_ROOT . '/includes/language.inc';
-    language_negotiation_set(LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info());
+    language_negotiation_set($language_type, locale_language_negotiation_info());
 
     // Assert that the language switching block is displayed on the frontpage.
     $this->drupalGet('');
     $this->assertText(t('Languages'), t('Language switcher block found.'));
 
     // Assert that only the current language is marked as active.
-    list($language_switcher) = $this->xpath('//div[@id="block-locale-language"]/div[@class="content"]');
+    list($language_switcher) = $this->xpath("//div[@id=\"block-locale-{$language_type}\"]/div[@class=\"content\"]");
     $links = array(
       'active' => array(),
       'inactive' => array(),
@@ -1814,9 +1815,9 @@ class LocalizeDateFormatsFunctionalTest 
     $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
 
     // Set language negotiation.
+    $language_type = LANGUAGE_TYPE_INTERFACE;
     $edit = array(
-      'language[enabled][locale-url]' => TRUE,
-      'language_interface[enabled][locale-url]' => TRUE,
+      "{$language_type}[enabled][locale-url]" => TRUE,
     );
     $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
 
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1212
diff -u -p -r1.1212 node.module
--- modules/node/node.module	13 Jan 2010 12:58:47 -0000	1.1212
+++ modules/node/node.module	14 Jan 2010 01:36:28 -0000
@@ -1250,52 +1250,6 @@ function node_build_content($node, $view
 }
 
 /**
- * Implements hook_language_negotiation_info().
- */
-function node_language_negotiation_info() {
-  $providers = array();
-
-  $providers['node-language'] = array(
-    'types' => array(LANGUAGE_TYPE_CONTENT),
-    'callbacks' => array('language' => 'node_language_provider'),
-    'file' => drupal_get_path('module', 'node') . '/node.module',
-    'name' => t('Node'),
-    'description' => t('The current node language is used.'),
-  );
-
-  return $providers;
-}
-
-/**
- * Return the language of the current node.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on succes, FALSE otherwise.
- */
-function node_language_provider($languages) {
-  require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc');
-
-  $path = isset($_GET['q']) ? $_GET['q'] : '';
-  list($language, $path) = language_url_split_prefix($path, $languages);
-  $language = $language ? $language : language_default();
-  $path = drupal_get_normal_path($path, $language->language);
-
-  // We cannot use args now.
-  $path = explode('/', $path);
-  // Act only if we are in a node page.
-  if (isset($path[0]) && isset($path[1]) && $path[0] == 'node' && $nid = intval($path[1])) {
-    // We cannot perform a node load here.
-    $result = db_query('SELECT n.language FROM {node} n WHERE n.nid = :nid', array(':nid' => $nid))->fetchAssoc();
-    return $result['language'];
-  }
-
-  return FALSE;
-}
-
-/**
  * Generate an array which displays a node detail page.
  *
  * @param $node
