Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.223
diff -u -r1.223 locale.inc
--- includes/locale.inc	11 Aug 2009 11:52:45 -0000	1.223
+++ includes/locale.inc	12 Aug 2009 01:45:33 -0000
@@ -33,7 +33,7 @@
  */
 function locale_languages_overview_form() {
   drupal_static_reset('language');
-  $languages = language_list('language');
+  $languages = language_list('language', LANGUAGE_TYPE_ANY);
 
   $options = array();
   $form['weight'] = array('#tree' => TRUE);
@@ -51,6 +51,7 @@
     $form['name'][$langcode] = array('#markup' => check_plain($language->name));
     $form['native'][$langcode] = array('#markup' => check_plain($language->native));
     $form['direction'][$langcode] = array('#markup' => ($language->direction == LANGUAGE_RTL ? t('Right to left') : t('Left to right')));
+    $form['type'][$langcode] = array('#markup' => _locale_language_type_label($language->type));
   }
   $form['enabled'] = array('#type' => 'checkboxes',
     '#options' => $options,
@@ -73,6 +74,7 @@
  */
 function theme_locale_languages_overview_form($form) {
   $default = language_default();
+  $languages = language_list();
   foreach ($form['name'] as $key => $element) {
     // Do not take form control structures.
     if (is_array($element) && element_child($key)) {
@@ -80,11 +82,16 @@
       if ($key == $default->language) {
         $form['enabled'][$key]['#attributes']['disabled'] = 'disabled';
       }
+      // Disable the default radio button for content languages.
+      if (!isset($languages[$key])) {
+        $form['site_default'][$key]['#attributes']['disabled'] = 'disabled';
+      }
       $rows[] = array(
         'data' => array(
           '<strong>' . drupal_render($form['name'][$key]) . '</strong>',
           drupal_render($form['native'][$key]),
           check_plain($key),
+          drupal_render($form['type'][$key]),
           drupal_render($form['direction'][$key]),
           array('data' => drupal_render($form['enabled'][$key]), 'align' => 'center'),
           drupal_render($form['site_default'][$key]),
@@ -95,7 +102,7 @@
       );
     }
   }
-  $header = array(array('data' => t('English name')), array('data' => t('Native name')), array('data' => t('Code')), array('data' => t('Direction')), array('data' => t('Enabled')), array('data' => t('Default')), array('data' => t('Weight')), array('data' => t('Operations')));
+  $header = array(array('data' => t('English name')), array('data' => t('Native name')), array('data' => t('Code')), array('data' => t('Type')), array('data' => t('Direction')), array('data' => t('Enabled')), array('data' => t('Default')), array('data' => t('Weight')), array('data' => t('Operations')));
   $output = theme('table', $header, $rows, array('id' => 'language-order'));
   $output .= drupal_render_children($form);
 
@@ -294,6 +301,13 @@
     '#default_value' => @$language->direction,
     '#options' => array(LANGUAGE_LTR => t('Left to right'), LANGUAGE_RTL => t('Right to left'))
   );
+  $form['type'] = array('#type' => 'radios',
+    '#title' => t('Type'),
+    '#required' => TRUE,
+    '#description' => t('The type of language being configured.'),
+    '#default_value' => @$language->type,
+    '#options' => _locale_language_type_label(),
+  );
   return $form;
 }
 
@@ -328,7 +342,7 @@
   $langcode = $form_state['values']['langcode'];
   if (isset($form_state['values']['name'])) {
     // Custom language form.
-    locale_add_language($langcode, $form_state['values']['name'], $form_state['values']['native'], $form_state['values']['direction'], $form_state['values']['domain'], $form_state['values']['prefix']);
+    locale_add_language($langcode, $form_state['values']['name'], $form_state['values']['native'], $form_state['values']['direction'], $form_state['values']['domain'], $form_state['values']['prefix'], TRUE, FALSE, $form_state['values']['type']);
     drupal_set_message(t('The language %language has been created and can now be used. More information is available on the <a href="@locale-help">help screen</a>.', array('%language' => t($form_state['values']['name']), '@locale-help' => url('admin/help/locale'))));
   }
   else {
@@ -353,18 +367,22 @@
  * Validate the language editing form. Reused for custom language addition too.
  */
 function locale_languages_edit_form_validate($form, &$form_state) {
+  $default = language_default('language');
   if (!empty($form_state['values']['domain']) && !empty($form_state['values']['prefix'])) {
     form_set_error('prefix', t('Domain and path prefix values should not be set at the same time.'));
   }
   if (!empty($form_state['values']['domain']) && $duplicate = db_query("SELECT language FROM {languages} WHERE domain = :domain AND language <> :language", array(':domain' => $form_state['values']['domain'], ':language' => $form_state['values']['langcode']))->fetchField()) {
     form_set_error('domain', t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate->language)));
   }
-  if (empty($form_state['values']['prefix']) && language_default('language') != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) {
+  if (empty($form_state['values']['prefix']) && $default != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) {
     form_set_error('prefix', t('Only the default language can have both the domain and prefix empty.'));
   }
   if (!empty($form_state['values']['prefix']) && $duplicate = db_query("SELECT language FROM {languages} WHERE prefix = :prefix AND language <> :language", array(':prefix' => $form_state['values']['prefix'], ':language' => $form_state['values']['langcode']))->fetchField()) {
     form_set_error('prefix', t('The prefix (%prefix) is already tied to a language (%language).', array('%prefix' => $form_state['values']['prefix'], '%language' => $duplicate->language)));
   }
+  if ($form_state['values']['type'] == LANGUAGE_TYPE_CONTENT && $default == $form_state['values']['langcode']) {
+    form_set_error('type', t('The default site language type cannot be set to "Content".'));
+  }
 }
 
 /**
@@ -378,6 +396,7 @@
       'domain' => $form_state['values']['domain'],
       'prefix' => $form_state['values']['prefix'],
       'direction' => $form_state['values']['direction'],
+      'type' => $form_state['values']['type'],
     ))
     ->condition('language',  $form_state['values']['langcode'])
     ->execute();
@@ -391,6 +410,9 @@
     }
     variable_set('language_default', $default);
   }
+  if ($form_state['values']['type'] & LANGUAGE_TYPE_UI) {
+    _locale_invalidate_js($form_state['values']['langcode']);
+  }
   $form_state['redirect'] = 'admin/config/international/language';
   return;
 }
@@ -420,7 +442,7 @@
   }
 
   // For other languages, warn user that data loss is ahead.
-  $languages = language_list();
+  $languages = language_list('language', LANGUAGE_TYPE_ANY);
 
   if (!isset($languages[$langcode])) {
     drupal_not_found();
@@ -435,7 +457,7 @@
  * Process language deletion submissions.
  */
 function locale_languages_delete_form_submit($form, &$form_state) {
-  $languages = language_list();
+  $languages = language_list('language', LANGUAGE_TYPE_ANY);
   if (isset($languages[$form_state['values']['langcode']])) {
     // Remove translations first.
     db_delete('locales_target')
@@ -542,8 +564,10 @@
   // Languages with at least one record in the locale table.
   $translations = db_query("SELECT COUNT(*) AS translation, t.language, s.textgroup FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid GROUP BY textgroup, language");
   foreach ($translations as $data) {
-    $ratio = (!empty($groupsums[$data->textgroup]) && $data->translation > 0) ? round(($data->translation/$groupsums[$data->textgroup]) * 100.0, 2) : 0;
-    $rows[$data->language][$data->textgroup] = $data->translation . '/' . $groupsums[$data->textgroup] . " ($ratio%)";
+    if (isset($languages[$data->language])) {
+      $ratio = (!empty($groupsums[$data->textgroup]) && $data->translation > 0) ? round(($data->translation/$groupsums[$data->textgroup]) * 100.0, 2) : 0;
+      $rows[$data->language][$data->textgroup] = $data->translation . '/' . $groupsums[$data->textgroup] . " ($ratio%)";
+    }
   }
 
   return theme('table', $headers, $rows);
@@ -1103,7 +1127,7 @@
  * @param $default
  *   Optionally set this language to be the default.
  */
-function locale_add_language($langcode, $name = NULL, $native = NULL, $direction = LANGUAGE_LTR, $domain = '', $prefix = '', $enabled = TRUE, $default = FALSE) {
+function locale_add_language($langcode, $name = NULL, $native = NULL, $direction = LANGUAGE_LTR, $domain = '', $prefix = '', $enabled = TRUE, $default = FALSE, $type = LANGUAGE_TYPE_ANY) {
   // Default prefix on language code.
   if (empty($prefix)) {
     $prefix = $langcode;
@@ -1127,6 +1151,7 @@
       'domain' => $domain,
       'prefix' => $prefix,
       'enabled' => $enabled,
+      'type' => $type,
     ))
     ->execute();
 
@@ -1140,8 +1165,10 @@
     variable_set('language_count', variable_get('language_count', 1) + 1);
   }
 
-  // Force JavaScript translation file creation for the newly added language.
-  _locale_invalidate_js($langcode);
+  if ($type & LANGUAGE_TYPE_UI) {
+    // Force JavaScript translation file creation for the newly added language.
+    _locale_invalidate_js($langcode);
+  }
 
   watchdog('locale', 'The %language language (%code) has been created.', array('%language' => $name, '%code' => $langcode));
 }
@@ -2383,7 +2410,7 @@
   }
   else {
     // Get information about the locale.
-    $languages = language_list();
+    $languages = language_list('language', LANGUAGE_TYPE_ANY);
     $language = $languages[$langcode];
   }
 
@@ -2533,7 +2560,7 @@
  */
 function _locale_prepare_predefined_list() {
   include_once DRUPAL_ROOT . '/includes/iso.inc';
-  $languages = language_list();
+  $languages = language_list('language', LANGUAGE_TYPE_ANY);
   $predefined = _locale_get_predefined_list();
   foreach ($predefined as $key => $value) {
     if (isset($languages[$key])) {
@@ -2709,6 +2736,18 @@
 }
 
 /**
+ * Helper function to provide language type labels.
+ */
+function _locale_language_type_label($type = NULL) {
+  $labels = array(
+    LANGUAGE_TYPE_UI => t('Interface'),
+    LANGUAGE_TYPE_CONTENT => t('Content'),
+    LANGUAGE_TYPE_ANY => t('Interface and content')
+  );
+  return empty($type) ? $labels : $labels[$type];
+}
+
+/**
  * @} End of "locale-autoimport"
  */
 
Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.293
diff -u -r1.293 bootstrap.inc
--- includes/bootstrap.inc	4 Aug 2009 04:02:25 -0000	1.293
+++ includes/bootstrap.inc	12 Aug 2009 01:45:31 -0000
@@ -202,6 +202,21 @@
 define('LANGUAGE_RTL', 1);
 
 /**
+ * The type of language used to select the user interface.
+ */
+define('LANGUAGE_TYPE_UI', 0x1);
+
+/**
+ * The type of language used to define the content language.
+ */
+define('LANGUAGE_TYPE_CONTENT', 0x2);
+
+/**
+ * Any type of language.
+ */
+define('LANGUAGE_TYPE_ANY', LANGUAGE_TYPE_UI | LANGUAGE_TYPE_CONTENT);
+
+/**
  * For convenience, define a short form of the request time global.
  */
 define('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
@@ -1607,36 +1622,43 @@
 /**
  * Get a list of languages set up indexed by the specified key
  *
- * @param $field The field to index the list with.
+ * @param $field
+ *   The field to index the list with.
+ * @param $type
+ *   The type of the languages to be returned.
  */
-function language_list($field = 'language') {
+function language_list($field = 'language', $type = LANGUAGE_TYPE_UI) {
   $languages = &drupal_static(__FUNCTION__);
   // Init language list
-  if (!isset($languages)) {
+  if (!isset($languages[$type])) {
     if (variable_get('language_count', 1) > 1 || module_exists('locale')) {
-      $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language');
+      $types = array($type, LANGUAGE_TYPE_ANY);
+      $where = $type == LANGUAGE_TYPE_ANY ? '' : 'WHERE type IN (%d, %d) ';
+      $languages[$type]['language'] = db_query('SELECT * FROM {languages} ' . $where . ' ORDER BY weight ASC, name ASC', $types)
+        ->fetchAllAssoc('language');
     }
     else {
       // No locale module, so use the default language only.
       $default = language_default();
-      $languages['language'][$default->language] = $default;
+      $languages[$type]['language'][$default->language] = $default;
     }
   }
 
   // Return the array indexed by the right field
-  if (!isset($languages[$field])) {
-    $languages[$field] = array();
-    foreach ($languages['language'] as $lang) {
+  if (!isset($languages[$type][$field])) {
+    $languages[$type][$field] = array();
+    foreach ($languages[$type]['language'] as $lang) {
       // Some values should be collected into an array
       if (in_array($field, array('enabled', 'weight'))) {
-        $languages[$field][$lang->$field][$lang->language] = $lang;
+        $languages[$type][$field][$lang->$field][$lang->language] = $lang;
       }
       else {
-        $languages[$field][$lang->$field] = $lang;
+        $languages[$type][$field][$lang->$field] = $lang;
       }
     }
   }
-  return $languages[$field];
+
+  return $languages[$type][$field];
 }
 
 /**
Index: modules/locale/locale.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v
retrieving revision 1.35
diff -u -r1.35 locale.test
--- modules/locale/locale.test	11 Aug 2009 11:52:46 -0000	1.35
+++ modules/locale/locale.test	12 Aug 2009 01:45:35 -0000
@@ -68,6 +68,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
     $this->assertEqual($this->getUrl(), url('admin/config/international/language', array('absolute' => TRUE)), t('Correct page redirection.'));
@@ -151,9 +152,30 @@
     $this->assertEqual($this->getUrl(), url('admin/config/international/language', array('absolute' => TRUE)), t('Correct page redirection.'));
     $this->assertText(t('The English language cannot be deleted.'), t('Failed to delete English language.'));
 
+    // Test language type settings.
+    drupal_static_reset('language_list');
+    require_once DRUPAL_ROOT . '/includes/locale.inc';
+    $langtypes = array(LANGUAGE_TYPE_UI => 'ui', LANGUAGE_TYPE_CONTENT => 'ct');
+    foreach ($langtypes as $type => $langcode) {
+      $edit = array(
+        'langcode' => $langcode,
+        'name' => $this->randomName(16),
+        'native' => $this->randomName(16),
+        'prefix' => $langcode,
+        'direction' => LANGUAGE_LTR,
+        'type' => $type,
+      );
+      $this->drupalPost('admin/international/language/add', $edit, t('Add custom language'));
+      $languages = language_list('language', $type);
+      $this->assertTrue(isset($languages[$langcode]), t('%type language correctly added', array('%type' => _locale_language_type_label($type))));
+      $language_type = isset($languages[$langcode]) ? $languages[$langcode]->type : -1;
+      $this->assertTrue($language_type == $type, t('Language type correctly set'));
+      $type = $type == LANGUAGE_TYPE_UI ? LANGUAGE_TYPE_CONTENT : LANGUAGE_TYPE_UI;
+      $this->assertFalse(isset($languages[$langtypes[$type]]), t('%type language not listed', array('%type' => _locale_language_type_label($type))));
+    }
+
     $this->drupalLogout();
   }
-
 }
 
 /**
@@ -204,6 +226,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
     // Add string.
@@ -336,6 +359,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
     // Add string.
@@ -397,6 +421,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
     // Add string.
@@ -632,6 +657,11 @@
     );
     $this->drupalPost('admin/config/international/translate/translate', $search, t('Filter'));
     $this->assertNoText(t('No strings found for your search.'), t('String overwritten by imported string.'));
+
+    // Content languages cannot be listed in the language selector.
+    locale_inc_callback('locale_add_language', 'de', 'German', 'Deutsch', LANGUAGE_LTR, '', 'de', TRUE, FALSE, LANGUAGE_TYPE_CONTENT);
+    $this->drupalGet('admin/international/translate/import');
+    $this->assertNoRaw('Deutsch', t('Content language not listed in the language selector'));
   }
 
   /**
@@ -655,6 +685,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
 
@@ -1146,6 +1177,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
 
@@ -1164,6 +1196,7 @@
       'native' => $native_disabled,
       'prefix' => $prefix_disabled,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
     // Disable the language.
@@ -1239,6 +1272,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
 
@@ -1325,6 +1359,7 @@
       'native' => $native,
       'prefix' => $prefix,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
 
@@ -1343,6 +1378,7 @@
       'native' => $native_disabled,
       'prefix' => $prefix_disabled,
       'direction' => '0',
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
     // Disable second custom language.
Index: modules/locale/locale.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.install,v
retrieving revision 1.46
diff -u -r1.46 locale.install
--- modules/locale/locale.install	14 Jul 2009 10:22:17 -0000	1.46
+++ modules/locale/locale.install	12 Aug 2009 01:45:33 -0000
@@ -42,6 +42,7 @@
   $ret = array();
   db_drop_index($ret, 'locales_source', 'source');
   db_add_index($ret, 'locales_source', 'source_context', array(array('source', 30), 'context'));
+  db_add_column($ret, 'languages', 'type', 'int', array('not null' => TRUE, 'default' => LANGUAGE_TYPE_ANY));
   return $ret;
 }
 
@@ -167,6 +168,12 @@
         'default' => '',
         'description' => 'Location of JavaScript translation file.',
       ),
+      'type' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => LANGUAGE_TYPE_ANY,
+        'description' => 'Language type (UI, Content, Any).',
+      ),
     ),
     'primary key' => array('language'),
     'indexes' => array(
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.247
diff -u -r1.247 locale.module
--- modules/locale/locale.module	11 Aug 2009 11:52:45 -0000	1.247
+++ modules/locale/locale.module	12 Aug 2009 01:45:34 -0000
@@ -301,7 +301,7 @@
         '#type' => 'select',
         '#title' => t('Language'),
         '#default_value' => (isset($form['#node']->language) ? $form['#node']->language : ''),
-        '#options' => array('' => t('Language neutral')) + locale_language_list('name'),
+        '#options' => array('' => t('Language neutral')) + locale_language_list('name', FALSE, LANGUAGE_TYPE_CONTENT),
       );
     }
     // Node type without language selector: assign the default for new nodes
@@ -489,12 +489,12 @@
  * @param $all
  *   Boolean to return all languages or only enabled ones
  */
-function locale_language_list($field = 'name', $all = FALSE) {
+function locale_language_list($field = 'name', $all = FALSE, $type = LANGUAGE_TYPE_UI) {
   if ($all) {
-    $languages = language_list();
+    $languages = language_list('language', $type);
   }
   else {
-    $languages = language_list('enabled');
+    $languages = language_list('enabled', $type);
     $languages = $languages[1];
   }
   $list = array();
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.58
diff -u -r1.58 common.test
--- modules/simpletest/tests/common.test	11 Aug 2009 11:52:46 -0000	1.58
+++ modules/simpletest/tests/common.test	12 Aug 2009 01:45:36 -0000
@@ -1111,6 +1111,7 @@
       'native' => self::LANGCODE,
       'direction' => LANGUAGE_LTR,
       'prefix' => self::LANGCODE,
+      'type' => LANGUAGE_TYPE_ANY,
     );
     $this->drupalPost('admin/config/international/language/add', $edit, t('Add custom language'));
 
