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 10 Aug 2009 14:47:14 -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: includes/locale.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/locale.inc,v retrieving revision 1.222 diff -u -r1.222 locale.inc --- includes/locale.inc 5 Aug 2009 15:58:34 -0000 1.222 +++ includes/locale.inc 10 Aug 2009 14:47:16 -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( '' . drupal_render($form['name'][$key]) . '', 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 help screen.', 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/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: modules/locale/locale.test =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.test,v retrieving revision 1.34 diff -u -r1.34 locale.test --- modules/locale/locale.test 3 Aug 2009 22:18:59 -0000 1.34 +++ modules/locale/locale.test 10 Aug 2009 14:47:17 -0000 @@ -68,6 +68,7 @@ 'native' => $native, 'prefix' => $prefix, 'direction' => '0', + 'type' => LANGUAGE_TYPE_ANY, ); $this->drupalPost('admin/international/language/add', $edit, t('Add custom language')); $this->assertEqual($this->getUrl(), url('admin/international/language', array('absolute' => TRUE)), t('Correct page redirection.')); @@ -151,9 +152,30 @@ $this->assertEqual($this->getUrl(), url('admin/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/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/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/international/language/add', $edit, t('Add custom language')); // Add string. @@ -632,6 +657,11 @@ ); $this->drupalPost('admin/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/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/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/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/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/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/international/language/add', $edit, t('Add custom language')); // Disable second custom language. Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.246 diff -u -r1.246 locale.module --- modules/locale/locale.module 2 Aug 2009 15:44:08 -0000 1.246 +++ modules/locale/locale.module 10 Aug 2009 14:47:16 -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/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 10 Aug 2009 14:47:16 -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/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.57 diff -u -r1.57 common.test --- modules/simpletest/tests/common.test 30 Jul 2009 19:57:10 -0000 1.57 +++ modules/simpletest/tests/common.test 10 Aug 2009 14:47:18 -0000 @@ -1111,6 +1111,7 @@ 'native' => self::LANGCODE, 'direction' => LANGUAGE_LTR, 'prefix' => self::LANGCODE, + 'type' => LANGUAGE_TYPE_ANY, ); $this->drupalPost('admin/international/language/add', $edit, t('Add custom language'));