From efbd1db550a62a282faa5a7d8ca733dea68dc046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Hojtsy?= <gabor@hojtsy.hu> Date: Mon, 26 Nov 2007 22:34:09 +0000 Subject: [PATCH] #193320 by JirkaRybka: _locale_rebuild_js() was invoked on every page view, now optimized --- includes/common.inc | 4 +- includes/locale.inc | 208 ++++++++++++++++++----------------- modules/locale/locale.module | 68 ++++++++++++ 3 files changed, 175 insertions(+), 105 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index 6bc3b328f052..b101ddc56232 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1871,8 +1871,8 @@ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer */ function drupal_get_js($scope = 'header', $javascript = NULL) { global $update_mode; - if (empty($update_mode) && function_exists('locale_inc_callback')) { - locale_inc_callback('_locale_update_js_files'); + if (empty($update_mode) && function_exists('locale_update_js_files')) { + locale_update_js_files(); } if (!isset($javascript)) { diff --git a/includes/locale.inc b/includes/locale.inc index fd81a499ad78..d59a00228c08 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -408,8 +408,13 @@ function locale_languages_delete_form(&$form_state, $langcode) { function locale_languages_delete_form_submit($form, &$form_state) { $languages = language_list(); if (isset($languages[$form_state['values']['langcode']])) { - db_query("DELETE FROM {languages} WHERE language = '%s'", $form_state['values']['langcode']); + // Remove translations first. db_query("DELETE FROM {locales_target} WHERE language = '%s'", $form_state['values']['langcode']); + cache_clear_all('locale:'. $form_state['values']['langcode'], 'cache'); + // With no translations, this removes existing JavaScript translations file. + _locale_rebuild_js($form_state['values']['langcode']); + // Remove the language. + db_query("DELETE FROM {languages} WHERE language = '%s'", $form_state['values']['langcode']); db_query("UPDATE {node} SET language = '' WHERE language = '%s'", $form_state['values']['langcode']); $variables = array('%locale' => $languages[$form_state['values']['langcode']]->name); drupal_set_message(t('The language %locale has been removed.', $variables)); @@ -833,8 +838,8 @@ function locale_translate_edit_form_submit($form, &$form_state) { db_query("DELETE FROM {locales_target} WHERE lid = %d AND language = '%s'", $lid, $key); } - // Refresh the JS file for this language. - _locale_rebuild_js($key); + // Force JavaScript translation file recreation for this language. + _locale_invalidate_js($key); } drupal_set_message(t('The string has been saved.')); @@ -858,12 +863,10 @@ function locale_translate_edit_form_submit($form, &$form_state) { * Delete a language string. */ function locale_translate_delete($lid) { - $langcode = db_result(db_query('SELECT language FROM {locales_target} WHERE lid = %d', $lid)); db_query('DELETE FROM {locales_source} WHERE lid = %d', $lid); db_query('DELETE FROM {locales_target} WHERE lid = %d', $lid); - if ($langcode) { - _locale_rebuild_js($langcode); - } + // Force JavaScript translation file recreation for all languages. + _locale_invalidate_js(); cache_clear_all('locale:', 'cache', TRUE); drupal_set_message(t('The string has been removed.')); drupal_goto('admin/build/translate/search'); @@ -925,6 +928,9 @@ function locale_add_language($langcode, $name = NULL, $native = NULL, $direction variable_set('language_count', variable_get('language_count', 1) + 1); } + // 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)); } /** @@ -974,8 +980,8 @@ function _locale_import_po($file, $langcode, $mode, $group = NULL) { drupal_set_message(t('The translation file %filename appears to have a missing or malformed header.', array('%filename' => $file->filename)), 'error'); } - // Clear cache and refresh JavaScript translations. - _locale_rebuild_js($langcode); + // Clear cache and force refresh of JavaScript translations. + _locale_invalidate_js($langcode); cache_clear_all('locale:', 'cache', TRUE); // Rebuild the menu, strings may have changed. @@ -1597,46 +1603,6 @@ function _locale_import_parse_quoted($string) { * @} End of "locale-api-import" */ -/** - * This function checks all JavaScript files currently added via drupal_add_js() - * and invokes parsing if they have not yet been parsed for Drupal.t() calls. - */ -function _locale_update_js_files() { - global $language; - - $parsed = variable_get('javascript_parsed', array()); - - // The first three parameters are NULL in order to get an array with all - // scopes. This is necessary to prevent recreation of JS translation files - // when new files are added for example in the footer. - $javascript = drupal_add_js(NULL, NULL, NULL); - $files = FALSE; - - foreach ($javascript as $scope) { - foreach ($scope as $type => $data) { - if ($type != 'setting' && $type != 'inline') { - foreach ($data as $filepath => $info) { - $files = TRUE; - if (!in_array($filepath, $parsed)) { - _locale_parse_js_file($filepath); - watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath)); - $parsed[] = $filepath; - } - } - } - } - } - - // Update the parsed files storage array. - variable_set('javascript_parsed', $parsed); - _locale_rebuild_js(); - - // Add the translation JavaScript file to the page. - if ($files && !empty($language->javascript)) { - drupal_add_js(file_create_path(variable_get('locale_js_directory', 'languages') .'/'. $language->language .'_'. $language->javascript .'.js'), 'core'); - } -} - /** * Parses a JavaScript file, extracts strings wrapped in Drupal.t() and * Drupal.formatPlural() and inserts them into the database. @@ -2043,6 +2009,39 @@ function _locale_translate_seek_query() { return $query; } +/** + * Force the JavaScript translation file(s) to be refreshed. + * + * This function sets a refresh flag for a specified language, or all + * languages except English, if none specified. JavaScript translation + * files are rebuilt (with locale_update_js_files()) the next time a + * request is served in that language. + * + * @param $langcode + * The language code for which the file needs to be refreshed. + * @return + * New content of the 'javascript_parsed' variable. + */ +function _locale_invalidate_js($langcode = NULL) { + $parsed = variable_get('javascript_parsed', array()); + + if (empty($langcode)) { + // Invalidate all languages. + $languages = language_list(); + unset($languages['en']); + foreach ($languages as $lcode => $data) { + $parsed['refresh:'. $lcode] = 'waiting'; + } + } + else { + // Invalidate single language. + $parsed['refresh:'. $langcode] = 'waiting'; + } + + variable_set('javascript_parsed', $parsed); + return $parsed; +} + /** * (Re-)Creates the JavaScript translation file for a language. * @@ -2091,9 +2090,10 @@ function _locale_rebuild_js($langcode = NULL) { } } - // Only operate when there are translations. + // Construct the JavaScript file, if there are translations. + $data = $status = ''; if (!empty($translations)) { - // Construct the JavaScript file. + $data = "Drupal.locale = { "; if (!empty($language->formula)) { @@ -2101,67 +2101,69 @@ function _locale_rebuild_js($langcode = NULL) { } $data .= "'strings': ". drupal_to_js($translations) ." };"; - - // Construct the directory where JS translation files are stored. - // There is (on purpose) no front end to edit that variable. - $dir = file_create_path(variable_get('locale_js_directory', 'languages')); - - // Only create a new file if the content has changed. $data_hash = md5($data); - if ($language->javascript != $data_hash) { - if (!empty($language->javascript)) { - // We are recreating the new file, so delete the old one. - file_delete(file_create_path($dir .'/'. $language->language .'_'. $language->javascript .'.js')); - $language->javascript = ''; - } - else { - // The directory might not exist when there has not been a translation - // file before, so create it. - file_check_directory($dir, TRUE); - } + } - // The file name of the new JavaScript translation file. - $dest = $dir .'/'. $language->language .'_'. $data_hash .'.js'; - $filepath = file_save_data($data, $dest); + // Construct the filepath where JS translation files are stored. + // There is (on purpose) no front end to edit that variable. + $dir = file_create_path(variable_get('locale_js_directory', 'languages')); - // Update the global $language object. - $language->javascript = $filepath ? $data_hash : ''; + // Delete old file, if we have no translations anymore, or a different file to be saved. + if (!empty($language->javascript) && (!$data || $language->javascript != $data_hash)) { + file_delete(file_create_path($dir .'/'. $language->language .'_'. $language->javascript .'.js')); + $language->javascript = ''; + $status = 'deleted'; + } - // Save the new JavaScript hash. - db_query("UPDATE {languages} SET javascript = '%s' WHERE language = '%s'", $language->javascript, $language->language); + // Only create a new file if the content has changed. + if ($data && $language->javascript != $data_hash) { + // Ensure that the directory exists and is writable, if possible. + file_check_directory($dir, TRUE); - // Update the default language variable if the default language has been altered. - // This is necessary to keep the variable consistent with the database - // version of the language and to prevent checking against an outdated hash. - $default_langcode = language_default('language'); - if ($default_langcode == $language->language) { - $default = db_fetch_object(db_query("SELECT * FROM {languages} WHERE language = '%s'", $default_langcode)); - variable_set('language_default', $default); - } + // Save the file. + $dest = $dir .'/'. $language->language .'_'. $data_hash .'.js'; + if (file_save_data($data, $dest)) { + $language->javascript = $data_hash; + $status = ($status == 'deleted') ? 'updated' : 'created'; + } + else { + $language->javascript = ''; + $status = 'error'; + } + } - // Only report updated or creation when there is actually a file created. - if ($filepath) { - // There has already been a translation file before. - if (!empty($language->javascript)) { - watchdog('locale', 'Updated JavaScript translation file for the language %language.', array('%language' => t($language->name))); - } - else { - watchdog('locale', 'Created JavaScript translation file for the language %language.', array('%language' => t($language->name))); - } - } - else { - watchdog('locale', 'An error occurred during creation of the JavaScript translation file for the language %language.', array('%language' => t($language->name))); - } + // Save the new JavaScript hash (or an empty value if the file + // just got deleted). Act only if some operation was executed. + if ($status) { + db_query("UPDATE {languages} SET javascript = '%s' WHERE language = '%s'", $language->javascript, $language->language); + + // Update the default language variable if the default language has been altered. + // This is necessary to keep the variable consistent with the database + // version of the language and to prevent checking against an outdated hash. + $default_langcode = language_default('language'); + if ($default_langcode == $language->language) { + $default = db_fetch_object(db_query("SELECT * FROM {languages} WHERE language = '%s'", $default_langcode)); + variable_set('language_default', $default); } } - // There are no strings for JavaScript translation. However, if there is a file, - // delete it and reset the database. - elseif (!empty($language->javascript)) { - // Delete the old JavaScript file - file_delete(file_create_path(variable_get('locale_js_directory', 'languages') .'/'. $language->language .'_'. $language->javascript .'.js')); - db_query("UPDATE {languages} SET javascript = '' WHERE language = '%s'", $language->language); - watchdog('locale', 'Deleted JavaScript translation file for the locale %language.', array('%language' => t($language->name))); + // Log the operation and return success flag. + switch ($status) { + case 'updated': + watchdog('locale', 'Updated JavaScript translation file for the language %language.', array('%language' => t($language->name))); + return TRUE; + case 'created': + watchdog('locale', 'Created JavaScript translation file for the language %language.', array('%language' => t($language->name))); + return TRUE; + case 'deleted': + watchdog('locale', 'Removed JavaScript translation file for the language %language, because no translations currently exist for that language.', array('%language' => t($language->name))); + return TRUE; + case 'error': + watchdog('locale', 'An error occurred during creation of the JavaScript translation file for the language %language.', array('%language' => t($language->name)), WATCHDOG_ERROR); + return FALSE; + default: + // No operation needed. + return TRUE; } } diff --git a/modules/locale/locale.module b/modules/locale/locale.module index a07e5afddda7..d6a94a0d6ef7 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -458,6 +458,74 @@ function locale_system_update($components) { } } +/** + * Update JavaScript translation file, if required, and add it to the page. + * + * This function checks all JavaScript files currently added via drupal_add_js() + * and invokes parsing if they have not yet been parsed for Drupal.t() + * and Drupal.formatPlural() calls. Also refreshes the JavaScript translation + * file if necessary, and adds it to the page. + */ +function locale_update_js_files() { + global $language; + + $dir = file_create_path(variable_get('locale_js_directory', 'languages')); + $parsed = variable_get('javascript_parsed', array()); + + // The first three parameters are NULL in order to get an array with all + // scopes. This is necessary to prevent recreation of JS translation files + // when new files are added for example in the footer. + $javascript = drupal_add_js(NULL, NULL, NULL); + $files = $new_files = FALSE; + + foreach ($javascript as $scope) { + foreach ($scope as $type => $data) { + if ($type != 'setting' && $type != 'inline') { + foreach ($data as $filepath => $info) { + $files = TRUE; + if (!in_array($filepath, $parsed)) { + // Don't parse our own translations files. + if (substr($filepath, 0, strlen($dir)) != $dir) { + locale_inc_callback('_locale_parse_js_file', $filepath); + watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath)); + $parsed[] = $filepath; + $new_files = TRUE; + } + } + } + } + } + } + + // If there are any new source files we parsed, invalidate existing + // JavaScript translation files for all languages, adding the refresh + // flags into the existing array. + if ($new_files) { + $parsed += locale_inc_callback('_locale_invalidate_js'); + } + + // If necessary, rebuild the translation file for the current language. + if (!empty($parsed['refresh:'. $language->language])) { + // Don't clear the refresh flag on failure, so that another try will + // be performed later. + if (locale_inc_callback('_locale_rebuild_js')) { + unset($parsed['refresh:'. $language->language]); + } + // Store any changes after refresh was attempted. + variable_set('javascript_parsed', $parsed); + } + // If no refresh was attempted, but we have new source files, we need + // to store them too. This occurs if current page is in English. + else if ($new_files) { + variable_set('javascript_parsed', $parsed); + } + + // Add the translation JavaScript file to the page. + if ($files && !empty($language->javascript)) { + drupal_add_js($dir .'/'. $language->language .'_'. $language->javascript .'.js', 'core'); + } +} + // --------------------------------------------------------------------------------- // Language switcher block -- GitLab