diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 24ceaf2..3db56a7 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1329,85 +1329,160 @@ function install_find_translation_files($langcode = NULL) { * language cannot be chosen automatically. */ function install_select_language(&$install_state) { +// @todo Can we refactor this code, its getting messy. + $files = array(); + + // Check if the localization server can be reached. + $install_state['localization_online'] = install_check_localization_server(); + // Find all available translations. $files = install_find_translations(); $install_state['translations'] += $files; + // If a valid language code is set, continue with the next installation step. + if (!empty($_POST['langcode'])) { - foreach ($files as $file) { - if ($_POST['langcode'] == $file->langcode) { - $install_state['parameters']['langcode'] = $file->langcode; + $langcode = $_POST['langcode']; + if ($install_state['localization_online']) { + // @todo Should we check if the translation is available at l.d.o? + include_once DRUPAL_ROOT . '/core/includes/standard.inc'; + $standard_languages = standard_language_list(); + if (isset($standard_languages[$langcode])) { + $install_state['parameters']['langcode'] = $langcode; return; } } + else { + foreach ($files as $file) { + if ($langcode == $file->langcode) { + $install_state['parameters']['langcode'] = $langcode; + return; + } + } + } } 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 translated. Otherwise we assume the user knows what he - // is doing. - 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']['translate'])) { - $output = '

Follow these steps to translate Drupal into your language:

'; - $output .= '
    '; - $output .= '
  1. Download a translation from the translation server.
  2. '; - $output .= '
  3. Place it into the following directory:
    ' . $directory . '
  4. '; - $output .= '
'; - $output .= '

For more information on installing Drupal in different languages, visit the drupal.org handbook page.

'; - $output .= '

How should the installation continue?

'; - $output .= ''; - } - else { - include_once DRUPAL_ROOT . '/core/includes/form.inc'; - $elements = drupal_get_form('install_select_language_form', $files); - $output = drupal_render($elements); + if (!$install_state['localization_online']) { + + // If only the built-in (English) language is available, and we are + // performing an interactive installation, inform the user that the + // installer can be translated. Otherwise we assume the user knows what he + // is doing. + 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']['translate'])) { + $output = '

Follow these steps to translate Drupal into your language:

'; + $output .= '
    '; + $output .= '
  1. Download a translation from the translation server.
  2. '; + $output .= '
  3. Place it into the following directory:
    ' . $directory . '
  4. '; + $output .= '
'; + $output .= '

For more information on installing Drupal in different languages, visit the drupal.org handbook page.

'; + $output .= '

How should the installation continue?

'; + $output .= ''; + } + else { + include_once DRUPAL_ROOT . '/core/includes/form.inc'; + $elements = drupal_get_form('install_select_language_form', $files); + $output = drupal_render($elements); + } + return $output; } - return $output; + // One language, but not an interactive installation. Assume the user + // knows what he is doing. + $file = current($files); + $install_state['parameters']['langcode'] = $file->langcode; + return; } - // One language, but not an interactive installation. Assume the user - // knows what he is doing. - $file = current($files); - $install_state['parameters']['langcode'] = $file->langcode; - return; + } + // We don't have a langcode, so display a form for selecting one. When the + // localization server is online, the form shows all available languages. + // When offline the form show the langagues of the available language files. + // 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 + // URL. + if ($install_state['interactive']) { + drupal_set_title(st('Choose language')); + include_once DRUPAL_ROOT . '/core/includes/form.inc'; + $elements = drupal_get_form('install_select_language_form', $install_state['localization_online'] ? array() : $files); + return drupal_render($elements); } else { - // 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 - // URL. - if ($install_state['interactive']) { - drupal_set_title(st('Choose language')); - include_once DRUPAL_ROOT . '/core/includes/form.inc'; - $elements = drupal_get_form('install_select_language_form', $files); - return drupal_render($elements); - } - else { - throw new Exception(st('Sorry, you must select a language to continue the installation.')); - } + throw new Exception(st('Sorry, you must select a language to continue the installation.')); + } + } +} + +/** + * Checks if the localization server can be contacted. + * + * @return boolean + * TRUE if the localization server can be reached. FALSE if not. + */ +function install_check_localization_server() { + $elements = parse_url(config('locale.settings')->get('translation.default_server_pattern')); + if (isset($elements['scheme']) && isset($elements['host'])) { + $url = $elements['scheme'] . '://' . $elements['host']; + $result = drupal_http_request($url, array('method' => 'HEAD')); + if (!isset($result->error) && $result->code == 200) { + return TRUE; + } + } + return FALSE; +} + +/** + * Gets the core release data for localization. + * + * In case of a dev release we fall back to the latest stable release. e.g. + * 8.2-dev falls back to 8.1. 8.0-dev falls back to 7.0. + * + * @return array + * Associative array containing 'core' and 'version' of the release. + */ +function install_get_localization_release() { + if (strpos(VERSION, 'dev')) { + list($version, ) = explode('-', VERSION); + list($major, $minor) = explode('.', $version); + + // Calculate the major and minor release number to fall back to. + // e.g. 8.0-dev falls back to 7.0 and 8.2-dev falls back to 8.1. + if ($minor == 0) { + $major--; + } + else { + $minor--; } + $release = "$major.$minor"; } + else { + $release = VERSION; + } + + return array( + 'core' => "$major.x", + 'version' => $release, + ); } /** * Form constructor for the language selection form. * - * @param $files - * An associative array of file information objects keyed by file URIs as - * returned by file_scan_directory(). + * @param array $files + * (option) An associative array of file information objects keyed by file URIs as + * returned by file_scan_directory(). Defaults to all standard languages. * * @see file_scan_directory() * @ingroup forms */ -function install_select_language_form($form, &$form_state, $files) { +function install_select_language_form($form, &$form_state, $files = array()) { include_once DRUPAL_ROOT . '/core/includes/standard.inc'; include_once DRUPAL_ROOT . '/core/modules/language/language.module'; include_once DRUPAL_ROOT . '/core/modules/language/language.negotiation.inc'; @@ -1416,18 +1491,29 @@ function install_select_language_form($form, &$form_state, $files) { $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]; - } - else { - // If the language was not found in standard.inc, display its langcode. - $select_options[$file->langcode] = $file->langcode; + if ($files) { + 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]; + } + else { + // If the language was not found in standard.inc, display its langcode. + $select_options[$file->langcode] = $file->langcode; + } + // Build a list of languages simulated for browser detection. + $languages[$file->langcode] = new Language(array( + 'langcode' => $file->langcode, + )); } + } + else { + foreach ($standard_languages as $langcode => $language_names) + // Build a list of select list options based on files we found. + $select_options[$langcode] = $language_names[1]; // Build a list of languages simulated for browser detection. - $languages[$file->langcode] = new Language(array( - 'langcode' => $file->langcode, + $languages[$langcode] = new Language(array( + 'langcode' => $langcode, )); } @@ -1599,14 +1685,49 @@ function install_import_translations(&$install_state) { language_delete('en'); } - // Collect files to import for this language. - $batch = locale_translate_batch_import_files(array('langcode' => $langcode)); + if ($install_state['localization_online']) { + // Download and import translations for the newly added language. + module_load_include('fetch.inc', 'locale'); + _install_set_locale_project(); + $options = _locale_translation_default_update_options(); + $batch = locale_translation_batch_update_build(array('drupal'), array($langcode), $options); + + } + else { + // Collect files to import for this language. + $batch = locale_translate_batch_import_files(array('drupal'), array('langcode' => $langcode), TRUE); + } + if (!empty($batch)) { return $batch; } } /** + * Tells the translation import process which modules are installed. + * + * Locale module depends on Update module to fetch project data. When update + * module is not yet enabled, we fill the locale_project table with data of + * the core project create a starting point for the translation import process. + * + * Follow up issue to solve this dependency: http://drupal.org/node/1832946 + * Create a small project API to be used by Update and Locale. + */ +function _install_set_locale_project() { + $release = install_get_localization_release(); + db_insert('locale_project') + ->fields(array( + 'name' => 'drupal', + 'project_type' => 'module', + 'core' => $release['core'], + 'version' => $release['version'], + 'server_pattern' => config('locale.settings')->get('translation.default_server_pattern'), + 'status' => 1, + )) + ->execute(); +} + +/** * Form constructor for a form to configure the new site. * * @param $install_state @@ -1661,15 +1782,17 @@ function install_configure_form($form, &$form_state, &$install_state) { * * @return * The batch definition, if there are language files to import. - * - * @todo - * This currently does the same as the first import step. Need to revisit - * once we have l10n_update functionality integrated. See - * http://drupal.org/node/1191488. */ function install_import_translations_remaining(&$install_state) { - include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc'; - return locale_translate_batch_import_files(array('langcode' => $install_state['parameters']['langcode'])); +// include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc'; +// return locale_translate_batch_import_files(array('langcode' => $install_state['parameters']['langcode'])); + // Download and import translations for the newly added language. + + module_load_include('fetch.inc', 'locale'); + module_load_include('compare.inc', 'locale'); + locale_translation_build_projects(); + $options = _locale_translation_default_update_options(); + return locale_translation_batch_update_build(array(), array($install_state['parameters']['langcode']), $options); } /** diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 46c5453..8dffadc 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -266,6 +266,65 @@ function locale_translate_export_form_submit($form, &$form_state) { } /** + * Prepare a batch to import all translations. + * + * @param array $options + * An array with options that can have the following elements: + * - 'langcode': The language code. Optional, defaults to NULL, which means + * that the language will be detected from the name of the files. + * - 'overwrite_options': Overwrite options array as defined in + * Drupal\locale\PoDatabaseWriter. Optional, defaults to an empty array. + * - 'customized': Flag indicating whether the strings imported from $file + * are customized translations or come from a community source. Use + * LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED. Optional, defaults to + * LOCALE_NOT_CUSTOMIZED. + * - 'finish_feedback': Whether or not to give feedback to the user when the + * batch is finished. Optional, defaults to TRUE. + * + * @param $force + * (optional) Import all available files, even if they were imported before. + * + * @todo + * Integrate with update status to identify projects needed and integrate + * l10n_update functionality to feed in translation files alike. + * See http://drupal.org/node/1191488. + */ +function locale_translate_batch_import_files($options, $force = FALSE) { + $options += array( + 'overwrite_options' => array(), + 'customized' => LOCALE_NOT_CUSTOMIZED, + 'finish_feedback' => TRUE, + ); + $files = array(); + if (!empty($options['langcode'])) { + $langcodes = array($options['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, locale_translate_get_interface_translation_files(array(), array($langcode))); + } + if (!$force) { + $result = db_select('locale_file', 'lf') + ->fields('lf', array('langcode', 'uri', 'timestamp')) + ->condition('langcode', $langcodes) + ->execute() + ->fetchAllAssoc('uri'); + foreach ($result as $uri => $info) { + if (isset($files[$uri]) && filemtime($uri) <= $info->timestamp) { + // The file is already imported and not changed since the last import. + // Remove it from file list and don't import it again. + unset($files[$uri]); + } + } + } + return locale_translate_batch_build($files, $options); +} + +/** * Get interface translation files present in the translations directory. * * @param array $projects diff --git a/core/modules/locale/locale.fetch.inc b/core/modules/locale/locale.fetch.inc index b65aa4b..668e901 100755 --- a/core/modules/locale/locale.fetch.inc +++ b/core/modules/locale/locale.fetch.inc @@ -95,7 +95,6 @@ function _locale_translation_fetch_operations($projects, $langcodes, $options) { $config = config('locale.settings'); $operations[] = array('locale_translation_batch_fetch_sources', array($projects, $langcodes)); - foreach ($projects as $project) { foreach ($langcodes as $langcode) { $operations[] = array('locale_translation_batch_fetch_download', array($project, $langcode));