diff options
author | Gábor Hojtsy <gabor@hojtsy.hu> | 2007-06-08 12:51:59 +0000 |
---|---|---|
committer | Gábor Hojtsy <gabor@hojtsy.hu> | 2007-06-08 12:51:59 +0000 |
commit | 57c9a13e1f0ed4e8492467bda817b3385be33225 (patch) | |
tree | cad6dffe20ee380f923a1a78b38248420393f332 | |
parent | 9e0da3dc7c39786bf6d972e07819fe2880cdeaa3 (diff) | |
download | brdo-57c9a13e1f0ed4e8492467bda817b3385be33225.tar.gz brdo-57c9a13e1f0ed4e8492467bda817b3385be33225.tar.bz2 |
#118026 by kkaefer with fixes from myself: JavaScript translation support and script.js as a default theme JS file to use, if found
-rw-r--r-- | includes/common.inc | 21 | ||||
-rw-r--r-- | includes/form.inc | 1 | ||||
-rw-r--r-- | includes/locale.inc | 253 | ||||
-rw-r--r-- | includes/theme.inc | 21 | ||||
-rw-r--r-- | install.php | 2 | ||||
-rw-r--r-- | misc/autocomplete.js | 2 | ||||
-rw-r--r-- | misc/drupal.js | 158 | ||||
-rw-r--r-- | misc/progress.js | 2 | ||||
-rw-r--r-- | misc/tableselect.js | 9 | ||||
-rw-r--r-- | misc/teaser.js | 8 | ||||
-rw-r--r-- | misc/upload.js | 4 | ||||
-rw-r--r-- | modules/locale/locale.install | 19 | ||||
-rw-r--r-- | modules/locale/locale.schema | 21 | ||||
-rw-r--r-- | modules/system/system.js | 6 | ||||
-rw-r--r-- | modules/system/system.module | 6 |
15 files changed, 484 insertions, 49 deletions
diff --git a/includes/common.inc b/includes/common.inc index 1be683d2c..5bd1ac781 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1686,13 +1686,14 @@ function drupal_clear_css_cache() { * feature has been turned on under the performance section? * @return * If the first parameter is NULL, the JavaScript array that has been built so - * far for $scope is returned. + * far for $scope is returned. If the first three parameters are NULL, + * an array with all scopes is returned. */ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE, $preprocess = TRUE) { static $javascript = array(); // Add jquery.js and drupal.js the first time a Javascript file is added. - if ($data && empty($javascript)) { + if (isset($data) && empty($javascript)) { $javascript['header'] = array( 'core' => array( 'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE), @@ -1701,11 +1702,11 @@ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array(), ); } - if (!isset($javascript[$scope])) { + if (isset($scope) && !isset($javascript[$scope])) { $javascript[$scope] = array('core' => array(), 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array()); } - if (!isset($javascript[$scope][$type])) { + if (isset($type) && isset($scope) && !isset($javascript[$scope][$type])) { $javascript[$scope][$type] = array(); } @@ -1723,7 +1724,12 @@ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer } } - return $javascript[$scope]; + if (isset($scope)) { + return $javascript[$scope]; + } + else { + return $javascript; + } } /** @@ -1743,6 +1749,10 @@ function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer * All JavaScript code segments and includes for the scope as HTML tags. */ function drupal_get_js($scope = 'header', $javascript = NULL) { + if (function_exists('locale_inc_callback')) { + locale_inc_callback('_locale_update_js_files'); + } + if (!isset($javascript)) { $javascript = drupal_add_js(NULL, NULL, $scope); } @@ -2062,6 +2072,7 @@ function _packer_backreferences($match, $offset, $data) { */ function drupal_clear_js_cache() { file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE); + variable_set('javascript_parsed', ''); } /** diff --git a/includes/form.inc b/includes/form.inc index 6ad249cc1..5ffa15397 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -1564,7 +1564,6 @@ function theme_textarea($element) { if (!empty($element['#teaser'])) { drupal_add_js('misc/teaser.js'); // Note: arrays are merged in drupal_get_js(). - drupal_add_js(array('teaserButton' => array(t('Join summary'), t('Split summary at cursor'))), 'setting'); drupal_add_js(array('teaserCheckbox' => array($element['#id'] => $element['#teaser_checkbox'])), 'setting'); drupal_add_js(array('teaser' => array($element['#id'] => $element['#teaser'])), 'setting'); $class[] = 'teaser'; diff --git a/includes/locale.inc b/includes/locale.inc index 0e20c1683..6c5798588 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -6,6 +6,8 @@ * Administration functions for locale.module. */ +define('LOCALE_JS_STRING', '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+'); + /** * @defgroup locale-language-overview Language overview functionality * @{ @@ -792,7 +794,11 @@ function locale_translate_edit_form_submit($form, &$form_state) { else { db_query("INSERT INTO {locales_target} (lid, translation, language) VALUES (%d, '%s', '%s')", $lid, $value, $key); } + + // Refresh the JS file for this language. + _locale_rebuild_js($key); } + drupal_set_message(t('The string has been saved.')); // Refresh the locale cache. @@ -814,8 +820,12 @@ 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_source} 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); + } locale_refresh_cache(); drupal_set_message(t('The string has been removed.')); drupal_goto('admin/build/translate/search'); @@ -897,47 +907,48 @@ function locale_add_language($langcode, $name = NULL, $native = NULL, $direction * * @param $file * Drupal file object corresponding to the PO file to import - * @param $lang + * @param $langcode * Language code * @param $mode * Should existing translations be replaced ('overwrite' or 'keep') * @param $group * Text group to import PO file into (eg. 'default' for interface translations) */ -function _locale_import_po($file, $lang, $mode, $group = NULL) { - // If not in 'safe mode', increase the maximum execution time: +function _locale_import_po($file, $langcode, $mode, $group = NULL) { + // If not in 'safe mode', increase the maximum execution time. if (!ini_get('safe_mode')) { set_time_limit(240); } - // Check if we have the language already in the database - if (!db_fetch_object(db_query("SELECT language FROM {languages} WHERE language = '%s'", $lang))) { + // Check if we have the language already in the database. + if (!db_fetch_object(db_query("SELECT language FROM {languages} WHERE language = '%s'", $langcode))) { drupal_set_message(t('The language selected for import is not supported.'), 'error'); return FALSE; } // Get strings from file (returns on failure after a partial import, or on success) - $status = _locale_import_read_po('db-store', $file, $mode, $lang, $group); + $status = _locale_import_read_po('db-store', $file, $mode, $langcode, $group); if ($status === FALSE) { - // error messages are set in _locale_import_read_po + // Error messages are set in _locale_import_read_po(). return FALSE; } - // Get status information on import process + // Get status information on import process. list($headerdone, $additions, $updates) = _locale_import_one_string('db-report'); if (!$headerdone) { drupal_set_message(t('The translation file %filename appears to have a missing or malformed header.', array('%filename' => $file->filename)), 'error'); } - // rebuild locale cache - cache_clear_all("locale:$lang", 'cache'); + // Rebuild locale cache. + _locale_rebuild_js($langcode); + cache_clear_all("locale:$langcode", 'cache'); - // rebuild the menu, strings may have changed + // Rebuild the menu, strings may have changed. menu_rebuild(); drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings and %update strings were updated.', array('%number' => $additions, '%update' => $updates))); - watchdog('locale', 'Imported %file into %locale: %number new strings added and %update updated.', array('%file' => $file->filename, '%locale' => $lang, '%number' => $additions, '%update' => $updates)); + watchdog('locale', 'Imported %file into %locale: %number new strings added and %update updated.', array('%file' => $file->filename, '%locale' => $langcode, '%number' => $additions, '%update' => $updates)); return TRUE; } @@ -1553,6 +1564,102 @@ function _locale_import_parse_quoted($string) { */ /** + * 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; + $parsed = array(); + + 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. + */ +function _locale_parse_js_file($filepath) { + global $language; + + // Load the JavaScript file. + $file = file_get_contents($filepath); + + // Match all calls to Drupal.t() in an array. + // Note: \s also matches newlines with the 's' modifier. + preg_match_all('~[^\w]Drupal\s*\.\s*t\s*\(\s*('. LOCALE_JS_STRING .')\s*[,\)]~s', $file, $t_matches); + + // Match all Drupal.formatPlural() calls in another array. + preg_match_all('~[^\w]Drupal\s*\.\s*formatPlural\s*\(\s*.+?\s*,\s*('. LOCALE_JS_STRING .')\s*,\s*((?:(?:\'(?:\\\\\'|[^\'])*@count(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*@count(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+)\s*[,\)]~s', $file, $plural_matches); + + // Loop through all matches and process them. + $all_matches = array_merge($plural_matches[1], $t_matches[1]); + foreach ($all_matches as $key => $string) { + $strings = array($string); + + // If there is also a plural version of this string, add it to the strings array. + if (isset($plural_matches[2][$key])) { + $strings[] = $plural_matches[2][$key]; + } + + foreach ($strings as $key => $string) { + // Remove the quotes and string concatenations from the string. + $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($string, 1, -1))); + + $result = db_query("SELECT lid, location FROM {locales_source} WHERE source = '%s' AND textgroup = 'default'", $string); + if ($source = db_fetch_object($result)) { + // We already have this source string and now have to add the location + // to the location column, if this file is not yet present in there. + $locations = preg_split('~\s*;\s*~', $source->location); + + if (!in_array($filepath, $locations)) { + $locations[] = $filepath; + $locations = implode('; ', $locations); + + // Save the new locations string to the database. + db_query("UPDATE {locales_source} SET location = '%s' WHERE lid = %d", $locations, $source->lid); + } + } + else { + // We don't have the source string yet, thus we insert it into the database. + db_query("INSERT INTO {locales_source} (location, source, textgroup) VALUES ('%s', '%s', 'default')", $filepath, $string); + $lid = db_result(db_query("SELECT lid FROM {locales_source} WHERE source = '%s' AND textgroup = 'default'", $string)); + db_query("INSERT INTO {locales_target} (lid, language, translation) VALUES (%d, '%s', '')", $lid, $language->language); + } + } + } +} + +/** * @defgroup locale-api-export Translation (template) export API. * @{ */ @@ -1869,6 +1976,128 @@ function _locale_translate_seek_query() { } /** + * (Re-)Creates the JavaScript translation file for a language. + * + * @param $language + * The language, the translation file should be (re)created for. + */ +function _locale_rebuild_js($langcode = NULL) { + if (!isset($langcode)) { + global $language; + } + else { + // Get information about the locale. + $languages = language_list(); + $language = $languages[$langcode]; + } + + // Construct the array for JavaScript translations. + // We sort on plural so that we have all plural forms before singular forms. + $result = db_query("SELECT s.lid, s.source, t.plid, t.plural, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE t.language = '%s' AND s.location LIKE '%%.js%%' AND s.textgroup = 'default' ORDER BY t.plural DESC", $language->language); + + $translations = $plurals = array(); + while ($data = db_fetch_object($result)) { + // Only add this to the translations array when there is actually a translation. + if (!empty($data->translation)) { + if ($data->plural) { + // When the translation is a plural form, first add it to another array and + // wait for the singular (parent) translation. + if (!isset($plurals[$data->plid])) { + $plurals[$data->plid] = array($data->plural => $data->translation); + } + else { + $plurals[$data->plid] += array($data->plural => $data->translation); + } + } + elseif (isset($plurals[$data->lid])) { + // There are plural translations for this translation, so get them from + // the plurals array and add them to the final translations array. + $translations[$data->source] = array($data->plural => $data->translation) + $plurals[$data->lid]; + unset($plurals[$data->lid]); + } + else { + // There are no plural forms for this translation, so just add it to + // the translations array. + $translations[$data->source] = $data->translation; + } + } + } + + // Only operate when there are translations. + if (!empty($translations)) { + // Construct the JavaScript file. + $data = "Drupal.locale = { "; + + if (!empty($language->formula)) { + $data .= "'pluralFormula': function(\$n) { return Number({$language->formula}); }, "; + } + + $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); + + // Update the global $language object. + $language->javascript = $filepath ? $data_hash : ''; + + // Save the new JavaScript hash. + 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); + } + + // 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', t('Updated JavaScript translation file for the language %language.', array('%language' => t($language->name)))); + } + else { + watchdog('locale', t('Created JavaScript translation file for the language %language.', array('%language' => t($language->name)))); + } + } + else { + watchdog('locale', t('An error occured during creation of the JavaScript translation file for the language %language.', array('%language' => t($language->name)))); + } + } + } + + // 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', t('Deleted JavaScript translation file for the locale %language.', array('%language' => t($language->name)))); + } +} + +/** * List languages in search result table */ function _locale_translate_language_list($translation) { diff --git a/includes/theme.inc b/includes/theme.inc index eb73fda0c..367befade 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -115,6 +115,21 @@ function _init_theme($theme, $base_theme = array()) { } } + // Add the default script + if (!empty($theme->script)) { + drupal_add_js($theme->script, 'theme'); + } + else { + // If we don't have a script of our own, look for the first + // base to have one and use its. + foreach ($base_theme as $base) { + if (!empty($base->script)) { + drupal_add_js($base->script, 'theme'); + break; + } + } + } + $theme_engine = NULL; // Initialize the theme. @@ -341,9 +356,12 @@ function list_themes($refresh = FALSE) { while ($theme = db_fetch_object($result)) { if (file_exists($theme->filename)) { $theme->info = unserialize($theme->info); - if (isset($theme->info['stylesheet'])) { + if (!empty($theme->info['stylesheet']) && file_exists($theme->info['stylesheet'])) { $theme->stylesheet = $theme->info['stylesheet']; } + if (!empty($theme->info['script']) && file_exists($theme->info['script'])) { + $theme->script = $theme->info['script']; + } if (isset($theme->info['engine'])) { $theme->engine = $theme->info['engine']; } @@ -1164,7 +1182,6 @@ function theme_table($header, $rows, $attributes = array(), $caption = NULL) { * Returns a header cell for tables that have a select all functionality. */ function theme_table_select_header_cell() { - drupal_add_js(array('tableSelect' => array('selectAll' => t('Select all rows in this table'), 'selectNone' => t('Deselect all rows in this table'))), 'setting'); drupal_add_js('misc/tableselect.js'); return array('class' => 'select-all'); diff --git a/install.php b/install.php index ff7a26367..4ec0cb55a 100644 --- a/install.php +++ b/install.php @@ -898,6 +898,8 @@ function install_configure_form() { ); drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module'); + // We add these strings as settings because JavaScript translation does not + // work on install time. drupal_add_js(array('cleanURL' => array('success' => st('Your server has been successfully tested to support this feature.'), 'failure' => st('Your system configuration does not currently support this feature. The <a href="http://drupal.org/node/15365">handbook page on Clean URLs</a> has additional troubleshooting information.'), 'testing' => st('Testing clean URLs...'))), 'setting'); drupal_add_js(' // Global Killswitch diff --git a/misc/autocomplete.js b/misc/autocomplete.js index fc0e96b2c..2431d37e4 100644 --- a/misc/autocomplete.js +++ b/misc/autocomplete.js @@ -282,7 +282,7 @@ Drupal.ACDB.prototype.search = function (searchString) { } }, error: function (xmlhttp) { - alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ db.uri); + alert(Drupal.t("An HTTP error @status occured. \n@uri", { '@status': xmlhttp.status, '@uri': db.uri })); } }); }, this.delay); diff --git a/misc/drupal.js b/misc/drupal.js index dc527240d..98441c7ec 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -1,6 +1,6 @@ // $Id$ -var Drupal = Drupal || {}; +var Drupal = Drupal || { 'settings': {}, 'themes': {}, 'locale': {} }; /** * Set the variable that indicates if JavaScript behaviors should be applied @@ -22,6 +22,142 @@ Drupal.extend = function(obj) { }; /** + * Encode special characters in a plain-text string for display as HTML. + */ +Drupal.checkPlain = function(str) { + str = String(str); + var replace = { '&': '&', '"': '"', '<': '<', '>': '>' }; + for (var character in replace) { + str = str.replace(character, replace[character]); + } + return str; +}; + +/** + * Translate strings to the page language or a given language. + * + * See the documentation of the server-side t() function for further details. + * + * @param str + * A string containing the English string to translate. + * @param args + * An object of replacements pairs to make after translation. Incidences + * of any key in this array are replaced with the corresponding value. + * Based on the first character of the key, the value is escaped and/or themed: + * - !variable: inserted as is + * - @variable: escape plain text to HTML (Drupal.checkPlain) + * - %variable: escape text and theme as a placeholder for user-submitted + * content (checkPlain + Drupal.theme('placeholder')) + * @return + * The translated string. + */ +Drupal.t = function(str, args) { + // Fetch the localized version of the string. + if (Drupal.locale.strings && Drupal.locale.strings[str]) { + str = Drupal.locale.strings[str]; + } + + if (args) { + // Transform arguments before inserting them + for (var key in args) { + switch (key.charAt(0)) { + // Escaped only + case '@': + args[key] = Drupal.checkPlain(args[key]); + break; + // Pass-through + case '!': + break; + // Escaped and placeholder + case '%': + default: + args[key] = Drupal.theme('placeholder', args[key]); + break; + } + str = str.replace(key, args[key]); + } + } + return str; +}; + +/** + * Format a string containing a count of items. + * + * This function ensures that the string is pluralized correctly. Since Drupal.t() is + * called by this function, make sure not to pass already-localized strings to it. + * + * See the documentation of the server-side format_plural() function for further details. + * + * @param count + * The item count to display. + * @param singular + * The string for the singular case. Please make sure it is clear this is + * singular, to ease translation (e.g. use "1 new comment" instead of "1 new"). + * Do not use @count in the singular string. + * @param plural + * The string for the plural case. Please make sure it is clear this is plural, + * to ease translation. Use @count in place of the item count, as in "@count + * new comments". + * @param args + * An object of replacements pairs to make after translation. Incidences + * of any key in this array are replaced with the corresponding value. + * Based on the first character of the key, the value is escaped and/or themed: + * - !variable: inserted as is + * - @variable: escape plain text to HTML (Drupal.checkPlain) + * - %variable: escape text and theme as a placeholder for user-submitted + * content (checkPlain + Drupal.theme('placeholder')) + * Note that you do not need to include @count in this array. + * This replacement is done automatically for the plural case. + * @return + * A translated string. + */ +Drupal.formatPlural = function(count, singular, plural, args) { + var args = ars || {}; + args['@count'] = count; + // Determine the index of the plural form. + var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1); + + if (index == 0) { + return Drupal.t(singular, args); + } + else if (index == 1) { + return Drupal.t(plural, args); + } + else { + args['@count['+ index +']'] = args['@count']; + delete args['@count']; + return Drupal.t(plural.replace('@count', '@count['+ index +']')); + } +}; + +/** + * Generate the themed representation of a Drupal object. + * + * All requests for themed output must go through this function. It examines + * the request and routes it to the appropriate theme function. If the current + * theme does not provide an override function, the generic theme function is + * called. + * + * For example, to retrieve the HTML that is output by theme_placeholder(text), + * call Drupal.theme('placeholder', text). + * + * @param func + * The name of the theme function to call. + * @param ... + * Additional arguments to pass along to the theme function. + * @return + * Any data the theme function returns. This could be a plain HTML string, + * but also a complex object. + */ +Drupal.theme = function(func) { + for (var i = 1, args = []; i < arguments.length; i++) { + args.push(arguments[i]); + } + + return (Drupal.theme[func] || Drupal.theme.prototype[func]).apply(this, args); +}; + +/** * Redirects a button's form submission to a hidden iframe and displays the result * in a given wrapper. The iframe should contain a call to * window.parent.iframeHandler() after submission. @@ -126,7 +262,7 @@ Drupal.mousePosition = function(e) { */ Drupal.parseJson = function (data) { if ((data.substring(0, 1) != '{') && (data.substring(0, 1) != '[')) { - return { status: 0, data: data.length ? data : 'Unspecified error' }; + return { status: 0, data: data.length ? data : Drupal.t('Unspecified error') }; } return eval('(' + data + ');'); }; @@ -227,3 +363,21 @@ if (Drupal.jsEnabled) { // 'js enabled' cookie document.cookie = 'has_js=1'; } + +/** + * The default themes. + */ +Drupal.theme.prototype = { + + /** + * Formats text for emphasized display in a placeholder inside a sentence. + * + * @param str + * The text to format (plain-text). + * @return + * The formatted text (html). + */ + placeholder: function(str) { + return '<em>' + Drupal.checkPlain(str) + '</em>'; + } +}; diff --git a/misc/progress.js b/misc/progress.js index e9e07ee48..c693fafad 100644 --- a/misc/progress.js +++ b/misc/progress.js @@ -86,7 +86,7 @@ Drupal.progressBar.prototype.sendPing = function () { pb.timer = setTimeout(function() { pb.sendPing(); }, pb.delay); }, error: function (xmlhttp) { - pb.displayError('An HTTP error '+ xmlhttp.status +' occured.\n'+ pb.uri); + pb.displayError(Drupal.t("An HTTP error @status occured. \n@uri", { '@status': xmlhttp.status, '@uri': pb.uri })); } }); } diff --git a/misc/tableselect.js b/misc/tableselect.js index 5020f7931..5c0cfbb32 100644 --- a/misc/tableselect.js +++ b/misc/tableselect.js @@ -2,10 +2,11 @@ Drupal.tableSelect = function() { // Keep track of the table, which checkbox is checked and alias the settings. - var table = this, selectAll, checkboxes, lastChecked, settings = Drupal.settings.tableSelect; + var table = this, selectAll, checkboxes, lastChecked; + var strings = { 'selectAll': Drupal.t('Select all rows in this table'), 'selectNone': Drupal.t('Deselect all rows in this table') }; // Store the select all checkbox in a variable as we need it quite often. - selectAll = $('<input type="checkbox" class="form-checkbox" />').attr('title', settings.selectAll).click(function() { + selectAll = $('<input type="checkbox" class="form-checkbox" />').attr('title', strings.selectAll).click(function() { // Loop through all checkboxes and set their state to the select all checkbox' state. checkboxes.each(function() { this.checked = selectAll[0].checked; @@ -13,7 +14,7 @@ Drupal.tableSelect = function() { $(this).parents('tr:first')[ this.checked ? 'addClass' : 'removeClass' ]('selected'); }); // Update the title and the state of the check all box. - selectAll.attr('title', selectAll[0].checked ? settings.selectNone : settings.selectAll); + selectAll.attr('title', selectAll[0].checked ? strings.selectNone : strings.selectAll); }); // Find all <th> with class select-all, and insert the check all checkbox. @@ -35,7 +36,7 @@ Drupal.tableSelect = function() { // If all checkboxes are checked, make sure the select-all one is checked too, otherwise keep unchecked. selectAll[0].checked = (checkboxes.length == $(checkboxes).filter(':checked').length); // Set the title to the current action. - selectAll.attr('title', selectAll[0].checked ? settings.selectNone : settings.selectAll); + selectAll.attr('title', selectAll[0].checked ? strings.selectNone : strings.selectAll); // Keep track of the last checked checkbox. lastChecked = e.target; diff --git a/misc/teaser.js b/misc/teaser.js index eeb847305..d515d1f71 100644 --- a/misc/teaser.js +++ b/misc/teaser.js @@ -29,7 +29,7 @@ Drupal.teaserAttach = function() { $(teaser).attr('disabled', 'disabled'); $(teaser).parent().slideUp('fast'); // Change label - $(this).val(Drupal.settings.teaserButton[1]); + $(this).val(Drupal.t('Split summary at cursor')); // Show separate teaser checkbox $(checkbox).hide(); } @@ -48,7 +48,7 @@ Drupal.teaserAttach = function() { $(teaser).attr('disabled', ''); $(teaser).parent().slideDown('fast'); // Change label - $(this).val(Drupal.settings.teaserButton[0]); + $(this).val(Drupal.t('Join summary')); // Show separate teaser checkbox $(checkbox).show(); } @@ -64,11 +64,11 @@ Drupal.teaserAttach = function() { teaser[0].value = trim(text[0]); body[0].value = trim(text[1]); $(teaser).attr('disabled', ''); - $('input', button).val(Drupal.settings.teaserButton[0]).toggle(join_teaser, split_teaser); + $('input', button).val(Drupal.t('Join summary')).toggle(join_teaser, split_teaser); } else { $(teaser).hide(); - $('input', button).val(Drupal.settings.teaserButton[1]).toggle(split_teaser, join_teaser); + $('input', button).val(Drupal.t('Split summary at cursor')).toggle(split_teaser, join_teaser); $(checkbox).hide(); } diff --git a/misc/upload.js b/misc/upload.js index bcc1e6acc..900d979ec 100644 --- a/misc/upload.js +++ b/misc/upload.js @@ -33,7 +33,7 @@ Drupal.jsUpload = function(uri, button, wrapper, hide) { Drupal.jsUpload.prototype.onsubmit = function () { // Insert progressbar and stretch to take the same space. this.progress = new Drupal.progressBar('uploadprogress'); - this.progress.setProgress(-1, 'Uploading file'); + this.progress.setProgress(-1, Drupal.t('Uploading file')); var hide = this.hide; var el = this.progress.element; @@ -98,7 +98,7 @@ Drupal.jsUpload.prototype.oncomplete = function (data) { * Handler for the form redirection error. */ Drupal.jsUpload.prototype.onerror = function (error) { - alert('An error occurred:\n\n'+ error); + alert(Drupal.t('An error occurred:\n\n@error', { '@error': error })); // Remove progressbar $(this.progress.element).remove(); this.progress = null; diff --git a/modules/locale/locale.install b/modules/locale/locale.install index 1d87d6ccc..309e9d57f 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -12,7 +12,7 @@ function locale_install() { // Create tables. drupal_install_schema('locale'); - db_query("INSERT INTO {languages} (language, name, native, direction, enabled, weight) VALUES ('en', 'English', 'English', '0', '1', '0')"); + db_query("INSERT INTO {languages} (language, name, native, direction, enabled, weight, javascript) VALUES ('en', 'English', 'English', '0', '1', '0', '')"); } /** @@ -92,6 +92,15 @@ function locale_update_6002() { } /** + * Adds a column to store the filename of the JavaScript translation file. + */ +function locale_update_6003() { + $ret = array(); + db_add_field($rest, 'languages', 'javascript', array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => '')); + return $ret; +} + +/** * @} End of "defgroup updates-5.x-to-6.x" */ @@ -99,6 +108,14 @@ function locale_update_6002() { * Implementation of hook_uninstall(). */ function locale_uninstall() { + // Delete all JavaScript translation files + $files = db_query('SELECT javascript FROM {languages}'); + while ($file = db_fetch_object($files)) { + if (!empty($file)) { + file_delete(file_create_path($file->javascript)); + } + } + // Remove tables. drupal_uninstall_schema('locale'); } diff --git a/modules/locale/locale.schema b/modules/locale/locale.schema index f949c7878..6973aeead 100644 --- a/modules/locale/locale.schema +++ b/modules/locale/locale.schema @@ -4,16 +4,17 @@ function locale_schema() { $schema['languages'] = array( 'fields' => array( - 'language' => array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''), - 'name' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''), - 'native' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''), - 'direction' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), - 'enabled' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), - 'plurals' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), - 'formula' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), - 'domain' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), - 'prefix' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), - 'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0) + 'language' => array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''), + 'name' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''), + 'native' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''), + 'direction' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'enabled' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'plurals' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'formula' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), + 'domain' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), + 'prefix' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''), + 'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'javascript' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), ), 'primary key' => array('language'), ); diff --git a/modules/system/system.js b/modules/system/system.js index 900d11ac1..808dac7f5 100644 --- a/modules/system/system.js +++ b/modules/system/system.js @@ -9,18 +9,18 @@ */ Drupal.cleanURLsSettingsCheck = function() { var url = location.pathname +"admin/settings/clean-urls"; - $("#clean-url .description span").html('<div id="testing">'+ Drupal.settings.cleanURL.testing +"</div>"); + $("#clean-url .description span").html('<div id="testing">'+ Drupal.t('Testing clean URLs...') +"</div>"); $("#clean-url p").hide(); $.ajax({url: location.protocol +"//"+ location.host + url, type: "GET", data: " ", complete: function(response) { $("#testing").toggle(); if (response.status == 200) { // Check was successful. $("#clean-url input.form-radio").attr("disabled", ""); - $("#clean-url .description span").append('<div class="ok">'+ Drupal.settings.cleanURL.success +"</div>"); + $("#clean-url .description span").append('<div class="ok">'+ Drupal.t('Your server has been successfully tested to support this feature.') +"</div>"); } else { // Check failed. - $("#clean-url .description span").append('<div class="warning">'+ Drupal.settings.cleanURL.failure +"</div>"); + $("#clean-url .description span").append('<div class="warning">'+ Drupal.t('Your system configuration does not currently support this feature. The <a href="http://drupal.org/node/15365">handbook page on Clean URLs</a> has additional troubleshooting information.') +"</div>"); } }}); }; diff --git a/modules/system/system.module b/modules/system/system.module index bf1e7b93b..2ba242e22 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -615,7 +615,6 @@ function system_clean_url_settings() { if (!variable_get('clean_url', 0)) { if (strpos(request_uri(), '?q=') !== FALSE) { - drupal_add_js(array('cleanURL' => array('success' => t('Your server has been successfully tested to support this feature.'), 'failure' => t('Your system configuration does not currently support this feature. The <a href="http://drupal.org/node/15365">handbook page on Clean URLs</a> has additional troubleshooting information.'), 'testing' => t('Testing clean URLs...'))), 'setting'); drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module'); drupal_add_js(' // Global Killswitch @@ -1060,6 +1059,7 @@ function system_theme_default() { 'slogan' ), 'stylesheet' => 'style.css', + 'script' => 'script.js', 'screenshot' => 'screenshot.png', ); } @@ -1113,6 +1113,10 @@ function system_theme_data() { if (!empty($themes[$key]->info['stylesheet'])) { $themes[$key]->info['stylesheet'] = dirname($themes[$key]->filename) .'/'. $themes[$key]->info['stylesheet']; } + // Give the script proper path information. + if (!empty($themes[$key]->info['script'])) { + $themes[$key]->info['script'] = dirname($themes[$key]->filename) .'/'. $themes[$key]->info['script']; + } // Give the screenshot proper path information. if (!empty($themes[$key]->info['screenshot'])) { $themes[$key]->info['screenshot'] = dirname($themes[$key]->filename) .'/'. $themes[$key]->info['screenshot']; |