diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 8b377de568293fd38b337e60d795c6c66a1fac89..29e5c3aa3a645a016b0cba274729a5226802d16b 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -158,6 +158,13 @@ */ define('DRUPAL_KILOBYTE', 1024); +/** + * System language (only applicable to UI). + * + * Refers to the language used in Drupal and module/theme source code. + */ +define('LANGUAGE_SYSTEM', 'system'); + /** * The language code used when no language is explicitly assigned. * @@ -1476,7 +1483,7 @@ function t($string, array $args = array(), array $options = array()) { // Merge in default. if (empty($options['langcode'])) { - $options['langcode'] = isset($language->language) ? $language->language : 'en'; + $options['langcode'] = isset($language->language) ? $language->language : LANGUAGE_SYSTEM; } if (empty($options['context'])) { $options['context'] = ''; @@ -1494,7 +1501,7 @@ function t($string, array $args = array(), array $options = array()) { $string = $custom_strings[$options['langcode']][$options['context']][$string]; } // Translate with locale module if enabled. - elseif ($options['langcode'] != 'en' && function_exists('locale')) { + elseif ($options['langcode'] != LANGUAGE_SYSTEM && ($options['langcode'] != 'en' || variable_get('locale_translate_english', FALSE)) && function_exists('locale')) { $string = locale($string, $options['context'], $options['langcode']); } if (empty($args)) { @@ -2632,12 +2639,6 @@ function language_list($field = 'language') { $default = language_default(); if (drupal_multilingual() || module_exists('locale')) { $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language'); - // Users cannot uninstall the native English language. However, we allow - // it to be hidden from the installed languages. Therefore, at least one - // other language must be enabled then. - if (!$languages['language']['en']->enabled && !variable_get('language_native_enabled', TRUE)) { - unset($languages['language']['en']); - } } else { // No locale module, so use the default language only. diff --git a/includes/common.inc b/includes/common.inc index 5f7cdb85687d36b0879fa5c33a972cdd442daded..c851715022cec25b60ddf8d4dd930d48ce24040e 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1875,7 +1875,7 @@ 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 = isset($language->language) ? $language->language : LANGUAGE_SYSTEM; } switch ($type) { diff --git a/includes/locale.inc b/includes/locale.inc index 5a0138c79bf26c327e3323f6413e0bbf742503e8..01c35fabd0b2149076bbfe4d8d07a9297c1146db 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -703,7 +703,9 @@ function _locale_invalidate_js($langcode = NULL) { if (empty($langcode)) { // Invalidate all languages. $languages = language_list(); - unset($languages['en']); + if (!locale_translate_english()) { + unset($languages['en']); + } foreach ($languages as $lcode => $data) { $parsed['refresh:' . $lcode] = 'waiting'; } diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index 42c54e0474e14e2307c4e50bf58640a02c5d6b7a..6abe48b1dbff37b3305a73a56cfb915e58e7bb56 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -79,7 +79,7 @@ function locale_language_overview_form($form, &$form_state) { '#type' => 'link', '#title' => t('delete'), '#href' => 'admin/config/regional/language/delete/' . $langcode, - '#access' => $langcode != 'en' && $langcode != $default->language, + '#access' => $langcode != $default->language, ); } @@ -416,11 +416,6 @@ function locale_languages_edit_form_submit($form, &$form_state) { */ function locale_languages_delete_form($form, &$form_state, $language) { $langcode = $language->language; - // Do not allow deletion of English locale. - if ($langcode == 'en') { - drupal_set_message(t('The English language cannot be deleted.')); - drupal_goto('admin/config/regional/language'); - } if (language_default()->language == $langcode) { drupal_set_message(t('The default language cannot be deleted.')); diff --git a/modules/locale/locale.bulk.inc b/modules/locale/locale.bulk.inc index 51f80a635d7ab26f31c6a91e988b678bc6025966..ce10f05cba51dcd24728df91f2199237026cf53f 100644 --- a/modules/locale/locale.bulk.inc +++ b/modules/locale/locale.bulk.inc @@ -14,7 +14,9 @@ function locale_translate_import_form($form) { // Get all languages, except English drupal_static_reset('language_list'); $names = locale_language_list('name'); - unset($names['en']); + if (!locale_translate_english()) { + unset($names['en']); + } if (!count($names)) { $languages = _locale_prepare_predefined_list(); @@ -101,7 +103,9 @@ function locale_translate_export_screen() { // Get all languages, except English drupal_static_reset('language_list'); $names = locale_language_list('name'); - unset($names['en']); + if (!locale_translate_english()) { + unset($names['en']); + } $output = ''; // Offer translation export if any language is set up. if (count($names)) { @@ -215,7 +219,9 @@ function locale_batch_by_language($langcode, $finished = NULL, $skip = array()) function locale_batch_by_component($components, $finished = '_locale_batch_system_finished') { $files = array(); $languages = language_list('enabled'); - unset($languages[1]['en']); + if (!locale_translate_english()) { + unset($languages[1]['en']); + } if (count($languages[1])) { $language_list = join('|', array_keys($languages[1])); // Collect all files to import for all $components. diff --git a/modules/locale/locale.install b/modules/locale/locale.install index edd22c3fd07654f58ca11e1690dd801a539e2879..6cca4c42512193d756fd8fff48cb036918323889 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -9,20 +9,9 @@ * Implements hook_install(). */ function locale_install() { - // locales_source.source and locales_target.target are not used as binary - // fields; non-MySQL database servers need to ensure the field type is text - // and that LIKE produces a case-sensitive comparison. - - db_insert('languages') - ->fields(array( - 'language' => 'en', - 'name' => 'English', - 'direction' => 0, - 'enabled' => 1, - 'weight' => 0, - 'javascript' => '', - )) - ->execute(); + // Add the default language to the database too. + include_once DRUPAL_ROOT . '/includes/locale.inc'; + locale_language_save(language_default()); } /** diff --git a/modules/locale/locale.module b/modules/locale/locale.module index 03757ac0cec458539d2e523a84a47bd80b0db945..9eab4295d7f7c26ba01e176fa5537ae880b39c7a 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -1100,7 +1100,7 @@ function locale_form_locale_language_overview_form_alter(&$form, &$form_state) { 'translated' => 0, 'ratio' => 0, ); - if ($langcode != 'en') { + if ($langcode != 'en' || locale_translate_english()) { $form['languages'][$langcode]['locale_statistics'] = array( '#type' => 'link', '#title' => t('@translated/@total (@ratio%)', array( @@ -1113,8 +1113,36 @@ function locale_form_locale_language_overview_form_alter(&$form, &$form_state) { } else { $form['languages'][$langcode]['locale_statistics'] = array( - '#markup' => t('built-in'), + '#markup' => t('not applicable'), ); } } } + +/** + * Implements hook_form_FORM_ID_alter() for locale_languages_edit_form(). + */ +function locale_form_locale_languages_edit_form_alter(&$form, &$form_state) { + if ($form['langcode']['#type'] == 'value' && $form['langcode']['#value'] == 'en') { + $form['locale_translate_english'] = array( + '#title' => t('Enable interface translation to English'), + '#type' => 'checkbox', + '#default_value' => locale_translate_english(), + ); + $form['#submit'][] = 'locale_form_locale_languages_edit_form_alter_submit'; + } +} + +/** + * Submission handler to record our custom setting. + */ +function locale_form_locale_languages_edit_form_alter_submit($form, $form_state) { + variable_set('locale_translate_english', $form_state['values']['locale_translate_english']); +} + +/** + * Utility function to tell if locale translates to English. + */ +function locale_translate_english() { + return variable_get('locale_translate_english', FALSE); +} diff --git a/modules/locale/locale.pages.inc b/modules/locale/locale.pages.inc index 09cc1844b7f6959b9ca9ee630e03343c7a178441..ef843ec1fabbf20fdc538dff76a7f1cc1a2641d8 100644 --- a/modules/locale/locale.pages.inc +++ b/modules/locale/locale.pages.inc @@ -55,8 +55,8 @@ function _locale_translate_seek() { default: $condition = db_or() ->condition('s.source', '%' . db_like($query['string']) . '%', 'LIKE'); - if ($query['language'] != 'en') { - // Only search in translations if the language is not forced to English. + if ($query['language'] != LANGUAGE_SYSTEM) { + // Only search in translations if the language is not forced to system language. $condition->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE'); } $sql_query->condition($condition); @@ -64,7 +64,7 @@ function _locale_translate_seek() { } $limit_language = NULL; - if ($query['language'] != 'en' && $query['language'] != 'all') { + if ($query['language'] != LANGUAGE_SYSTEM && $query['language'] != 'all') { $sql_query->condition('language', $query['language']); $limit_language = $query['language']; } @@ -114,7 +114,9 @@ function _locale_translate_language_list($translation, $limit_language) { drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); $languages = language_list(); - unset($languages['en']); + if (!locale_translate_english()) { + unset($languages['en']); + } $output = ''; foreach ($languages as $langcode => $language) { if (!$limit_language || $limit_language == $langcode) { @@ -151,7 +153,9 @@ function locale_translation_filters() { // Get all languages, except English drupal_static_reset('language_list'); $languages = locale_language_list('name'); - unset($languages['en']); + if (!locale_translate_english()) { + unset($languages['en']); + } $filters['string'] = array( 'title' => t('String contains'), @@ -160,7 +164,7 @@ function locale_translation_filters() { $filters['language'] = array( 'title' => t('Language'), - 'options' => array_merge(array('all' => t('All languages'), 'en' => t('English (provided by Drupal)')), $languages), + 'options' => array_merge(array('all' => t('All languages'), LANGUAGE_SYSTEM => t('System (English)')), $languages), ); $filters['translation'] = array( @@ -297,7 +301,9 @@ function locale_translate_edit_form($form, &$form_state, $lid) { // Include default form controls with empty values for all languages. // This ensures that the languages are always in the same order in forms. $languages = language_list(); - unset($languages['en']); + if (!locale_translate_english()) { + unset($languages['en']); + } $form['translations'] = array('#tree' => TRUE); // Approximate the number of rows to use in the default textarea. $rows = min(ceil(str_word_count($source->source) / 12), 10); diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 4af9c546cc89054e9c1400eb5bc68068efebd930..fa19eb30ccae8c0deff42e1a28a76bf20bc8a56b 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -163,11 +163,36 @@ class LocaleConfigurationTest extends DrupalWebTestCase { // Make sure the "language_count" variable has not changed. $this->assertEqual(variable_get('language_count', 1), count($enabled[1]), t('Language count is correct.')); + // Ensure we can delete the English language. Right now English is the only + // language so we must add a new language and make it the default before + // deleting English. + $langcode = 'xx'; + $name = $this->randomName(16); + $edit = array( + 'predefined_langcode' => 'custom', + 'langcode' => $langcode, + 'name' => $name, + 'direction' => '0', + ); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); + $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.')); + $this->assertText($name, t('Name found.')); - // Ensure we can't delete the English language. - $this->drupalGet('admin/config/regional/language/delete/en'); + // Check if we can change the default language. + $path = 'admin/config/regional/language'; + $this->drupalGet($path); + $this->assertFieldChecked('edit-site-default-en', t('English is the default language.')); + // Change the default language. + $edit = array( + 'site_default' => $langcode, + ); + $this->drupalPost(NULL, $edit, t('Save configuration')); + $this->assertNoFieldChecked('edit-site-default-en', t('Default language updated.')); $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.')); - $this->assertText(t('The English language cannot be deleted.'), t('Failed to delete English language.')); + + $this->drupalPost('admin/config/regional/language/delete/en', array(), t('Delete')); + // We need raw here because %locale will add HTML. + $this->assertRaw(t('The language %locale has been removed.', array('%locale' => 'English')), t('The English language has been removed.')); } } @@ -296,6 +321,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { $language_indicator = "<em class=\"locale-untranslated\">$langcode</em> "; // This will be the translation of $name. $translation = $this->randomName(16); + $translation_to_en = $this->randomName(16); // Add custom language. $this->drupalLogin($admin_user); @@ -335,13 +361,39 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { $lid = $matches[1]; // No t() here, it's surely not translated yet. $this->assertText($name, t('name found on edit screen.')); + $this->assertNoText('English', t('No way to translate the string to English.')); + $this->drupalLogout(); + $this->drupalLogin($admin_user); + $this->drupalPost('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language')); + $this->drupalLogout(); + $this->drupalLogin($translate_user); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + // assertText() seems to remove the input field where $name always could be + // found, so this is not a false assert. See how assertNoText succeeds + // later. + $this->assertText($name, t('Search found the name.')); + $this->assertRaw($language_indicator, t('Name is untranslated.')); + // Assume this is the only result, given the random name. + $this->clickLink(t('edit')); + $string_edit_url = $this->getUrl(); $edit = array( "translations[$langcode]" => $translation, + 'translations[en]' => $translation_to_en, ); $this->drupalPost(NULL, $edit, t('Save translations')); $this->assertText(t('The string has been saved.'), t('The string has been saved.')); $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.')); - $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works.')); + $this->drupalGet($string_edit_url); + $this->assertRaw($translation, t('Non-English translation properly saved.')); + $this->assertRaw($translation_to_en, t('English translation properly saved.')); + $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works for non-English.')); + // Refresh the locale() cache to get fresh data from t() below. We are in + // the same HTTP request and therefore t() is not refreshed by saving the + // translation above. + locale_reset(); + // Now we should get the proper fresh translation from t(). + $this->assertTrue($name != $translation_to_en && t($name, array(), array('langcode' => 'en')) == $translation_to_en, t('t() works for English.')); + $this->assertTrue(t($name, array(), array('langcode' => LANGUAGE_SYSTEM)) == $name, t('t() works for LANGUAGE_SYSTEM.')); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); // The indicator should not be here. $this->assertNoRaw($language_indicator, t('String is translated.')); @@ -627,10 +679,10 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); $this->assertNoText(t('No strings available.'), t('Search found the translation.')); - // Ensure translated string doesn't appear if searching on English. + // Ensure translated string doesn't appear if searching in System (English). $search = array( 'string' => $translation, - 'language' => 'en', + 'language' => LANGUAGE_SYSTEM, 'translation' => 'all', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));