From 129de63e067006bac296f08a5d15f8b65bd295c5 Mon Sep 17 00:00:00 2001 From: Nathaniel <catch@35733.no-reply.drupal.org> Date: Fri, 25 Nov 2011 15:22:29 +0900 Subject: [PATCH] =?UTF-8?q?Issue=20#1260716=20by=20good=5Fman,=20G=C3=A1bo?= =?UTF-8?q?r=20Hojtsy,=20emarchak,=20David=5FRothstein,=20ezra-g:=20Improv?= =?UTF-8?q?e=20language=20onboarding=20user=20experience.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/includes/install.core.inc | 185 +++++++++++++++------------- core/includes/install.inc | 39 +++--- core/modules/locale/locale.bulk.inc | 13 +- core/modules/system/system.api.php | 14 ++- 4 files changed, 145 insertions(+), 106 deletions(-) diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 8aa1e385e8fb..f9d8062f98e3 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -115,7 +115,7 @@ function install_drupal($settings = array()) { * Non-interactive Drupal installations can override some of these default * settings by passing in an array to the installation script, most notably * 'parameters' (which contains one-time parameters such as 'profile' and - * 'locale' that are normally passed in via the URL) and 'forms' (which can + * 'langcode' that are normally passed in via the URL) and 'forms' (which can * be used to programmatically submit forms during the installation; the keys * of each element indicate the name of the installation task that the form * submission is for, and the values are used as the $form_state['values'] @@ -148,12 +148,12 @@ function install_state_defaults() { // Whether or not this installation is interactive. By default this will // be set to FALSE if settings are passed in to install_drupal(). 'interactive' => TRUE, - // An array of available languages for the installation. - 'locales' => array(), + // An array of available translation files for the installation. + 'translations' => array(), // An array of parameters for the installation, pre-populated by the URL // or by the settings passed in to install_drupal(). This is primarily // used to store 'profile' (the name of the chosen installation profile) - // and 'locale' (the name of the chosen installation language), since + // and 'langcode' (the code of the chosen installation language), since // these settings need to persist from page request to page request before // the database is available for storage. 'parameters' => array(), @@ -213,12 +213,17 @@ function install_begin_request(&$install_state) { // Add any installation parameters passed in via the URL. $install_state['parameters'] += $_GET; + // @todo: remove this testbot compatibility layer once the testbot is fixed. + if (isset($_GET['locale'])) { + $install_state['parameters']['langcode'] = $_GET['locale']; + } + // Validate certain core settings that are used throughout the installation. if (!empty($install_state['parameters']['profile'])) { $install_state['parameters']['profile'] = preg_replace('/[^a-zA-Z_0-9]/', '', $install_state['parameters']['profile']); } - if (!empty($install_state['parameters']['locale'])) { - $install_state['parameters']['locale'] = preg_replace('/[^a-zA-Z_0-9\-]/', '', $install_state['parameters']['locale']); + if (!empty($install_state['parameters']['langcode'])) { + $install_state['parameters']['langcode'] = preg_replace('/[^a-zA-Z_0-9\-]/', '', $install_state['parameters']['langcode']); } // Allow command line scripts to override server variables used by Drupal. @@ -309,6 +314,9 @@ function install_begin_request(&$install_state) { // Modify the installation state as appropriate. $install_state['completed_task'] = $task; $install_state['database_tables_exist'] = !empty($task); + + // Add the list of available profiles to the installation state. + $install_state['profiles'] += install_find_profiles(); } /** @@ -523,20 +531,20 @@ function install_tasks_to_perform($install_state) { */ function install_tasks($install_state) { // Determine whether translation import tasks will need to be performed. - $needs_translations = count($install_state['locales']) > 1 && !empty($install_state['parameters']['locale']) && $install_state['parameters']['locale'] != 'en'; + $needs_translations = count($install_state['translations']) > 1 && !empty($install_state['parameters']['langcode']) && $install_state['parameters']['langcode'] != 'en'; - // Start with the core installation tasks that run before handing control - // to the install profile. + // The first two installation tasks are (by default) language selection and + // profile selection. $tasks = array( + 'install_select_language' => array( + 'display_name' => st('Choose language'), + 'run' => INSTALL_TASK_RUN_IF_REACHED, + ), 'install_select_profile' => array( 'display_name' => st('Choose profile'), 'display' => count($install_state['profiles']) != 1, 'run' => INSTALL_TASK_RUN_IF_REACHED, ), - 'install_select_locale' => array( - 'display_name' => st('Choose language'), - 'run' => INSTALL_TASK_RUN_IF_REACHED, - ), 'install_load_profile' => array( 'run' => INSTALL_TASK_RUN_IF_REACHED, ), @@ -557,7 +565,7 @@ function install_tasks($install_state) { 'display_name' => count($install_state['profiles']) == 1 ? st('Install site') : st('Install profile'), 'type' => 'batch', ), - 'install_import_locales' => array( + 'install_import_translations' => array( 'display_name' => st('Set up translations'), 'display' => $needs_translations, 'type' => 'batch', @@ -588,7 +596,7 @@ function install_tasks($install_state) { // Finish by adding the remaining core tasks. $tasks += array( - 'install_import_locales_remaining' => array( + 'install_import_translations_remaining' => array( 'display_name' => st('Finish translations'), 'display' => $needs_translations, 'type' => 'batch', @@ -856,7 +864,6 @@ function install_verify_pdo() { function install_settings_form($form, &$form_state, &$install_state) { global $databases; $profile = $install_state['parameters']['profile']; - $install_locale = $install_state['parameters']['locale']; drupal_static_reset('conf_path'); $conf_path = './' . conf_path(FALSE); @@ -1015,7 +1022,6 @@ function install_find_profiles() { * thrown if a profile cannot be chosen automatically. */ function install_select_profile(&$install_state) { - $install_state['profiles'] += install_find_profiles(); if (empty($install_state['parameters']['profile'])) { // Try to find a profile. $profile = _install_select_profile($install_state['profiles']); @@ -1133,8 +1139,8 @@ function install_select_profile_form($form, &$form_state, $profile_files) { /** * Find all .po files useful for the installer. */ -function install_find_locales() { - $files = install_find_locale_files(); +function install_find_translations() { + $files = install_find_translation_files(); // English does not need a translation file. array_unshift($files, (object) array('name' => 'en')); foreach ($files as $key => $file) { @@ -1152,94 +1158,91 @@ function install_find_locales() { /** * Find installer translations either for a specific langcode or all languages. */ -function install_find_locale_files($langcode = NULL) { +function install_find_translation_files($langcode = NULL) { $directory = variable_get('locale_translate_file_directory', conf_path() . '/files/translations'); $files = file_scan_directory($directory, '!install\.' . (!empty($langcode) ? '\.' . preg_quote($langcode, '!') : '[^\.]+') . '\.po$!', array('recurse' => FALSE)); return $files; } /** - * Installation task; select which locale to use for the current profile. + * Installation task; select which language to use. * * @param $install_state * An array of information about the current installation state. The chosen - * locale will be added here, if it was not already selected previously, as - * will a list of all available locales. + * langcode will be added here, if it was not already selected previously, as + * will a list of all available languages. * * @return * For interactive installations, a form or other page output allowing the - * locale to be selected or providing information about locale selection, if - * a locale has not been chosen. Otherwise, an exception is thrown if a - * locale cannot be chosen automatically. + * language to be selected or providing information about language selection, + * if a language has not been chosen. Otherwise, an exception is thrown if a + * language cannot be chosen automatically. */ -function install_select_locale(&$install_state) { - // Find all available locales. - $profilename = $install_state['parameters']['profile']; - $locales = install_find_locales($profilename); - $install_state['locales'] += $locales; - - if (!empty($_POST['locale'])) { - foreach ($locales as $locale) { - if ($_POST['locale'] == $locale->langcode) { - $install_state['parameters']['locale'] = $locale->langcode; +function install_select_language(&$install_state) { + // Find all available translations. + $files = install_find_translations(); + $install_state['translations'] += $files; + + if (!empty($_POST['langcode'])) { + foreach ($files as $file) { + if ($_POST['langcode'] == $file->langcode) { + $install_state['parameters']['langcode'] = $file->langcode; return; } } } - if (empty($install_state['parameters']['locale'])) { + if (empty($install_state['parameters']['langcode'])) { // If only the built-in (English) language is available, and we are // performing an interactive installation, inform the user that the - // installer can be localized. Otherwise we assume the user knows what he + // installer can be translated. Otherwise we assume the user knows what he // is doing. - if (count($locales) == 1) { + if (count($files) == 1) { if ($install_state['interactive']) { + $directory = variable_get('locale_translate_file_directory', conf_path() . '/files/translations'); + drupal_set_title(st('Choose language')); - if (!empty($install_state['parameters']['localize'])) { + if (!empty($install_state['parameters']['translate'])) { $output = '<p>Follow these steps to translate Drupal into your language:</p>'; $output .= '<ol>'; $output .= '<li>Download a translation from the <a href="http://localize.drupal.org/download" target="_blank">translation server</a>.</li>'; - $output .= '<li>Place it into the following directory: -<pre> -/profiles/' . $profilename . '/translations/ -</pre></li>'; + $output .= '<li>Place it into the following directory:<pre>' . $directory . '</pre></li>'; $output .= '</ol>'; $output .= '<p>For more information on installing Drupal in different languages, visit the <a href="http://drupal.org/localize" target="_blank">drupal.org handbook page</a>.</p>'; $output .= '<p>How should the installation continue?</p>'; $output .= '<ul>'; - $output .= '<li><a href="install.php?profile=' . $profilename . '">Reload the language selection page after adding translations</a></li>'; - $output .= '<li><a href="install.php?profile=' . $profilename . '&locale=en">Continue installation in English</a></li>'; + $output .= '<li><a href="' . check_url(drupal_current_script_url(array('translate' => NULL))) . '">Reload the language selection page after adding translations</a></li>'; + $output .= '<li><a href="' . check_url(drupal_current_script_url(array('langcode' => 'en', 'translate' => NULL))) . '">Continue installation in English</a></li>'; $output .= '</ul>'; } else { include_once DRUPAL_ROOT . '/core/includes/form.inc'; - $elements = drupal_get_form('install_select_locale_form', $locales, $profilename); + $elements = drupal_get_form('install_select_language_form', $files); $output = drupal_render($elements); } return $output; } // One language, but not an interactive installation. Assume the user // knows what he is doing. - $locale = current($locales); - $install_state['parameters']['locale'] = $locale->name; + $langcode = current($files); + $install_state['parameters']['langcode'] = $file->langcode; return; } else { // Allow profile to pre-select the language, skipping the selection. - $function = $profilename . '_profile_details'; - if (function_exists($function)) { - $details = $function(); - if (isset($details['language'])) { - foreach ($locales as $locale) { - if ($details['language'] == $locale->name) { - $install_state['parameters']['locale'] = $locale->name; + if (isset($install_state['parameters']['profile'])) { + $info = install_profile_info($install_state['parameters']['profile']); + if (isset($info['langcode'])) { + foreach ($files as $file) { + if ($info['langcode'] == $file->langcode) { + $install_state['parameters']['langcode'] = $file->langcode; return; } } } } - // We still don't have a locale, so display a form for selecting one. + // We still don't have a langcode, so display a form for selecting one. // Only do this in the case of interactive installations, since this is // not a real form with submit handlers (the database isn't even set up // yet), rather just a convenience method for setting parameters in the @@ -1247,7 +1250,7 @@ function install_select_locale(&$install_state) { if ($install_state['interactive']) { drupal_set_title(st('Choose language')); include_once DRUPAL_ROOT . '/core/includes/form.inc'; - $elements = drupal_get_form('install_select_locale_form', $locales, $profilename); + $elements = drupal_get_form('install_select_language_form', $files); return drupal_render($elements); } else { @@ -1260,25 +1263,41 @@ function install_select_locale(&$install_state) { /** * Form API array definition for language selection. */ -function install_select_locale_form($form, &$form_state, $locales, $profilename) { +function install_select_language_form($form, &$form_state, $files) { include_once DRUPAL_ROOT . '/core/includes/standard.inc'; - $languages = standard_language_list(); - foreach ($locales as $locale) { - $name = $locale->langcode; - if (isset($languages[$name])) { - $name = $languages[$name][0] . (isset($languages[$name][1]) ? ' ' . st('(@language)', array('@language' => $languages[$name][1])) : ''); + include_once DRUPAL_ROOT . '/core/includes/locale.inc'; + + $standard_languages = standard_language_list(); + $select_options = array(); + $languages = array(); + + foreach ($files as $file) { + if (isset($standard_languages[$file->langcode])) { + // Build a list of select list options based on files we found. + $select_options[$file->langcode] = $standard_languages[$file->langcode][1]; } - $form['locale'][$locale->langcode] = array( - '#type' => 'radio', - '#return_value' => $locale->langcode, - '#default_value' => $locale->langcode == 'en' ? 'en' : '', - '#title' => $name . ($locale->langcode == 'en' ? ' ' . st('(built-in)') : ''), - '#parents' => array('locale') + else { + // If language not found in standard.inc, display it's langcode + $select_options[$file->langcode] = $file->langcode; + } + // Build a list of languages simulated for browser detection. + $languages[$file->langcode] = (object) array( + 'language' => $file->langcode, ); } - if (count($locales) == 1) { + + $browser_langcode = locale_language_from_browser($languages); + $form['langcode'] = array( + '#type' => 'select', + '#options' => $select_options, + // Use the browser detected language as default or English if nothing found. + '#default_value' => !empty($browser_langcode) ? $browser_langcode : 'en', + '#size' => min(count($select_options), 10), + ); + + if (count($files) == 1) { $form['help'] = array( - '#markup' => '<p><a href="install.php?profile=' . $profilename . '&localize=true">' . st('Learn how to install Drupal in other languages') . '</a></p>', + '#markup' => '<p><a href="' . check_url(drupal_current_script_url(array('translate' => 'true'))) . '">' . st('Learn how to install Drupal in other languages') . '</a></p>', ); } $form['actions'] = array('#type' => 'actions'); @@ -1319,7 +1338,7 @@ function install_load_profile(&$install_state) { $profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile'; if (file_exists($profile_file)) { include_once $profile_file; - $install_state['profile_info'] = install_profile_info($install_state['parameters']['profile'], $install_state['parameters']['locale']); + $install_state['profile_info'] = install_profile_info($install_state['parameters']['profile'], $install_state['parameters']['langcode']); } else { throw new Exception(st('Sorry, the profile you have chosen cannot be loaded.')); @@ -1398,19 +1417,19 @@ function install_profile_modules(&$install_state) { * @return * The batch definition, if there are language files to import. */ -function install_import_locales(&$install_state) { +function install_import_translations(&$install_state) { include_once DRUPAL_ROOT . '/core/includes/locale.inc'; include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc'; - $install_locale = $install_state['parameters']['locale']; + $langcode = $install_state['parameters']['langcode']; include_once DRUPAL_ROOT . '/core/includes/standard.inc'; - $predefined = standard_language_list(); - if (!isset($predefined[$install_locale])) { + $standard_languages = standard_language_list(); + if (!isset($standard_languages[$langcode])) { // Drupal does not know about this language, so we prefill its values with // our best guess. The user will be able to edit afterwards. $language = (object) array( - 'language' => $install_locale, - 'name' => $install_locale, + 'language' => $langcode, + 'name' => $langcode, 'default' => TRUE, ); locale_language_save($language); @@ -1418,14 +1437,14 @@ function install_import_locales(&$install_state) { else { // A known predefined language, details will be filled in properly. $language = (object) array( - 'language' => $install_locale, + 'language' => $langcode, 'default' => TRUE, ); locale_language_save($language); } // Collect files to import for this language. - $batch = locale_translate_batch_import_files($install_locale); + $batch = locale_translate_batch_import_files($langcode); if (!empty($batch)) { return $batch; } @@ -1499,9 +1518,9 @@ function install_configure_form($form, &$form_state, &$install_state) { * once we have l10n_update functionality integrated. See * http://drupal.org/node/1191488. */ -function install_import_locales_remaining(&$install_state) { +function install_import_translations_remaining(&$install_state) { include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc'; - return locale_translate_batch_import_files($install_state['parameters']['locale']); + return locale_translate_batch_import_files($install_state['parameters']['langcode']); } /** diff --git a/core/includes/install.inc b/core/includes/install.inc index 514d89c3554c..470586c4b7c2 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -663,7 +663,7 @@ function drupal_rewrite_settings($settings = array()) { */ function drupal_verify_profile($install_state) { $profile = $install_state['parameters']['profile']; - $locale = $install_state['parameters']['locale']; + $langcode = $install_state['parameters']['langcode']; include_once DRUPAL_ROOT . '/core/includes/file.inc'; include_once DRUPAL_ROOT . '/core/includes/common.inc'; @@ -1092,27 +1092,27 @@ function drupal_requirements_url($severity) { * @ingroup sanitization */ function st($string, array $args = array(), array $options = array()) { - static $locale_strings = NULL; + static $strings = NULL; global $install_state; if (empty($options['context'])) { $options['context'] = ''; } - if (!isset($locale_strings)) { - $locale_strings = array(); - if (isset($install_state['parameters']['profile']) && isset($install_state['parameters']['locale'])) { - // If the given locale was selected, there should be at least one .po file - // with its name ending in install.{$install_state['parameters']['locale']}.po + if (!isset($strings)) { + $strings = array(); + if (isset($install_state['parameters']['profile']) && isset($install_state['parameters']['langcode'])) { + // If the given langcode was selected, there should be at least one .po file + // with its name ending in install.{$install_state['parameters']['langcode']}.po // This might or might not be the entire filename. It is also possible // that multiple files end with the same extension, even if unlikely. - $locale_files = install_find_locale_files($install_state['parameters']['locale']); - if (!empty($locale_files)) { + $files = install_find_translation_files($install_state['parameters']['langcode']); + if (!empty($files)) { require_once DRUPAL_ROOT . '/core/includes/gettext.inc'; - foreach ($locale_files as $locale_file) { - _locale_import_read_po('mem-store', $locale_file); + foreach ($files as $file) { + _locale_import_read_po('mem-store', $file); } - $locale_strings = _locale_import_one_string('mem-report'); + $strings = _locale_import_one_string('mem-report'); } } } @@ -1134,7 +1134,7 @@ function st($string, array $args = array(), array $options = array()) { case '!': } } - return strtr((!empty($locale_strings[$options['context']][$string]) ? $locale_strings[$options['context']][$string] : $string), $args); + return strtr((!empty($strings[$options['context']][$string]) ? $strings[$options['context']][$string] : $string), $args); } /** @@ -1246,13 +1246,13 @@ function drupal_check_module($module) { * * @param $profile * Name of profile. - * @param $locale - * Name of locale used (if any). + * @param $langcode + * Language code (if any). * * @return * The info array. */ -function install_profile_info($profile, $locale = 'en') { +function install_profile_info($profile, $langcode = 'en') { $cache = &drupal_static(__FUNCTION__, array()); if (!isset($cache[$profile])) { @@ -1264,12 +1264,17 @@ function install_profile_info($profile, $locale = 'en') { 'version' => NULL, 'hidden' => FALSE, 'php' => DRUPAL_MINIMUM_PHP, + 'langcode' => NULL, + 'custom_language_selection' => FALSE, ); $info = drupal_parse_info_file("profiles/$profile/$profile.info") + $defaults; + if (isset($info['langcode'])) { + $info['custom_language_selection'] = TRUE; + } $info['dependencies'] = array_unique(array_merge( drupal_required_modules(), $info['dependencies'], - ($locale != 'en' && !empty($locale) ? array('locale') : array())) + ($langcode != 'en' && !empty($langcode) ? array('locale') : array())) ); // drupal_required_modules() includes the current profile as a dependency. diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 45a31bdcb719..4051674798d3 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -181,7 +181,18 @@ function locale_translate_export_po_form_submit($form, &$form_state) { */ function locale_translate_batch_import_files($langcode = NULL, $finish_feedback = FALSE) { $directory = variable_get('locale_translate_file_directory', conf_path() . '/files/translations'); - $files = file_scan_directory($directory, '!' . (!empty($langcode) ? '\.' . preg_quote($langcode, '!') : '') . '\.po$!', array('recurse' => FALSE)); + $files = array(); + if (!empty($langcode)) { + $langcodes = array($langcode); + } + else { + // If langcode was not provided, make sure to only import files for the + // languages we have enabled. + $langcodes = array_keys(language_list()); + } + foreach ($langcodes as $langcode) { + $files = array_merge($files, file_scan_directory($directory, '!' . (!empty($langcode) ? '\.' . preg_quote($langcode, '!') : '') . '\.po$!', array('recurse' => FALSE))); + } return locale_translate_batch_build($files, $finish_feedback); } diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 6325a5420319..51f0ecb71057 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -3252,18 +3252,22 @@ function hook_html_head_alter(&$head_elements) { /** * Alter the full list of installation tasks. * + * You can use this hook to change or replace any part of the Drupal + * installation process that occurs after the installation profile is selected. + * * @param $tasks * An array of all available installation tasks, including those provided by - * Drupal core. You can modify this array to change or replace any part of - * the Drupal installation process that occurs after the installation profile - * is selected. + * Drupal core. You can modify this array to change or replace individual + * steps within the installation process. * @param $install_state * An array of information about the current installation state. + * + * @see install_profile_info() */ function hook_install_tasks_alter(&$tasks, $install_state) { - // Replace the "Choose language" installation task provided by Drupal core + // Replace the entire site configuration form provided by Drupal core // with a custom callback function defined by this installation profile. - $tasks['install_select_locale']['function'] = 'myprofile_locale_selection'; + $tasks['install_configure_form']['function'] = 'myprofile_install_configure_form'; } /** -- GitLab