diff --git a/core/includes/update.inc b/core/includes/update.inc index bd13dc7..7c07c79 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -412,6 +412,79 @@ function update_prepare_d8_language() { ); db_add_field('locales_target', 'customized', $spec); } + if (db_table_exists('locales_source')) { + if (db_field_exists('locales_source', 'location')) { + $table = array( + 'description' => 'Location information for source strings.', + 'fields' => array( + 'lid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Unique identifier of this location.', + ), + 'sid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Unique identifier of this string.', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 50, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The location type (file, config, path, etc).', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Type dependent location information (file name, path, etc).', + ), + 'version' => array( + 'type' => 'varchar', + 'length' => 20, + 'not null' => TRUE, + 'default' => 'none', + 'description' => 'Version of Drupal, where the location was found (for locales optimization).', + ), + ), + 'primary key' => array('lid'), + 'foreign keys' => array( + 'locales_source' => array( + 'table' => 'locales_source', + 'columns' => array('sid' => 'lid'), + ), + ), + 'indexes' => array( + 'string_id' => array('sid'), + 'string_type' => array('sid', 'type'), + ), + ); + + db_create_table('locales_location', $table); + + // Copy locations for JavaScript strings from old table to new one. + // Other locations from Drupal 7 are not actually used for anything. + $result = db_query("SELECT lid, location FROM {locales_source} WHERE location LIKE '%.js%'"); + while ($string = $result->fetchObject()) { + // The field may contain multiple locations glued together by '; '. + $locations = preg_split('~\s*;\s*~', $string->location); + foreach ($locations as $location) { + if (substr($location, -3) == '.js') { + db_insert('locales_location')->fields(array( + 'sid' => $string->lid, + 'type' => 'javascript', + 'name' => $location, + 'version' => VERSION + ))->execute(); + } + } + } + // Drop old locales_source.location field. + db_drop_field('locales_source', 'location'); + } + } } } diff --git a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php index cd2f9c0..9dd04f6 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php @@ -61,7 +61,6 @@ protected function resolveCacheMiss($offset) { )); if ($translation) { - $this->stringStorage->checkVersion($translation, VERSION); $value = !empty($translation->translation) ? $translation->translation : TRUE; } else { @@ -70,9 +69,8 @@ protected function resolveCacheMiss($offset) { $this->stringStorage->createString(array( 'source' => $offset, 'context' => $this->context, - 'location' => request_uri(), 'version' => VERSION - ))->save(); + ))->addLocation('path', request_uri())->save(); $value = TRUE; } $this->storage[$offset] = $value; diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php index 7afa1db..b5b03c0 100644 --- a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php +++ b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php @@ -102,6 +102,7 @@ function setReport($report = array()) { 'updates' => 0, 'deletes' => 0, 'skips' => 0, + 'strings' => array(), ); $this->_report = $report; } @@ -226,13 +227,13 @@ private function importString(PoItem $item) { $source = $item->getSource(); $translation = $item->getTranslation(); - // Look up the source string and any existing translation. - $string = locale_storage()->findTranslation(array( + $strings = locale_storage()->getTranslations(array( 'language' => $this->_langcode, 'source' => $source, 'context' => $context )); + $string = reset($strings); if (!empty($translation)) { // Skip this string unless it passes a check for dangerous code. @@ -258,6 +259,7 @@ private function importString(PoItem $item) { $string->save(); $this->_report['updates']++; } + $this->_report['strings'][] = $string->getId(); return $string->lid; } else { @@ -272,6 +274,7 @@ private function importString(PoItem $item) { ))->save(); $this->_report['additions']++; + $this->_report['strings'][] = $string->getId(); return $string->lid; } } @@ -279,6 +282,7 @@ private function importString(PoItem $item) { // Empty translation, remove existing if instructed. $string->delete(); $this->_report['deletes']++; + $this->_report['strings'][] = $string->lid; return $string->lid; } } diff --git a/core/modules/locale/lib/Drupal/locale/StringBase.php b/core/modules/locale/lib/Drupal/locale/StringBase.php index 8ff9815..977b89f 100644 --- a/core/modules/locale/lib/Drupal/locale/StringBase.php +++ b/core/modules/locale/lib/Drupal/locale/StringBase.php @@ -22,7 +22,7 @@ public $lid; /** - * The string location. + * The string locations indexed by type. * * @var string */ @@ -152,6 +152,35 @@ public function getValues(array $fields) { } /** + * Implements Drupal\locale\StringInterface::getLocation(). + */ + public function getLocation($load = TRUE) { + if ($load && !isset($this->location)) { + $this->location = array(); + foreach ($this->getStorage()->getLocations(array('sid' => $this->getId())) as $location) { + $this->location[$location->type][$location->name] = $location->lid; + } + } + return $this->location; + } + + /** + * Implements Drupal\locale\StringInterface::addLocation(). + */ + public function addLocation($type, $name) { + $this->location[$type][$name] = TRUE; + return $this; + } + + /** + * Implements Drupal\locale\StringInterface::hasLocation(). + */ + public function hasLocation($type, $name) { + $location = $this->getLocation(TRUE); + return isset($location[$type]) ? !empty($location[$type][$name]) : FALSE; + } + + /** * Implements Drupal\locale\LocaleString::save(). */ public function save() { diff --git a/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php b/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php index 38e9df5..8c1c2bd 100644 --- a/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php +++ b/core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php @@ -80,12 +80,25 @@ public function findTranslation(array $conditions) { ->execute() ->fetchObject('Drupal\locale\TranslationString'); if ($string) { + $this->checkVersion($string, VERSION); $string->setStorage($this); } return $string; } /** + * Implements Drupal\locale\StringStorageInterface::getLocations(). + */ + function getLocations(array $conditions = array()) { + $query = $this->connection->select('locales_location', 'l', $this->options) + ->fields('l'); + foreach ($conditions as $field => $value) { + $query->condition('l.' . $field, $value); + } + return $query->execute()->fetchAll(); + } + + /** * Implements Drupal\locale\StringStorageInterface::countStrings(). */ public function countStrings() { @@ -100,19 +113,6 @@ public function countTranslations() { } /** - * Implements Drupal\locale\StringStorageInterface::checkVersion(). - */ - public function checkVersion($string, $version) { - if ($string->getId() && $string->getVersion() != $version) { - $string->setVersion($version); - $this->connection->update('locales_source', $this->options) - ->condition('lid', $string->getId()) - ->fields(array('version' => $version)) - ->execute(); - } - } - - /** * Implements Drupal\locale\StringStorageInterface::save(). */ public function save($string) { @@ -127,10 +127,63 @@ public function save($string) { else { $this->dbStringUpdate($string); } + // Update locations if they come with the string. + $this->updateLocation($string); return $this; } /** + * Update locations for string. + * + * @param Drupal\locale\StringInterface $string + * The string object. + */ + protected function updateLocation($string) { + if ($location = $string->getLocation(FALSE)) { + $created = FALSE; + foreach ($location as $type => $type_location) { + foreach ($type_location as $name => $lid) { + if (!$lid) { + $this->dbDelete('locales_location', array('sid' => $string->getId(), 'type' => $type, 'name' => $name)) + ->execute(); + } + elseif ($lid === TRUE) { + // This is a new location to add, take care not to duplicate. + $this->connection->merge('locales_location', $this->options) + ->key(array('sid' => $string->getId(), 'type' => $type, 'name' => $name)) + ->fields(array('version' => VERSION)) + ->execute(); + $created = TRUE; + } + // Loaded locations have 'lid' integer value, nor FALSE, nor TRUE. + } + } + if ($created) { + // As we've set a new location, check string version too. + $this->checkVersion($string, VERSION); + } + } + } + + /** + * Checks whether the string version matches a given version, fix it if not. + * + * @param Drupal\locale\StringInterface $string + * The string object. + * @param string $version + * Drupal version to check against. + */ + protected function checkVersion($string, $version) { + if ($string->getId() && $string->getVersion() != $version) { + $string->setVersion($version); + $this->connection->update('locales_source', $this->options) + ->condition('lid', $string->getId()) + ->fields(array('version' => $version)) + ->execute(); + } + } + + /** * Implements Drupal\locale\StringStorageInterface::delete(). */ public function delete($string) { @@ -138,6 +191,7 @@ public function delete($string) { $this->dbDelete('locales_target', $keys)->execute(); if ($string->isSource()) { $this->dbDelete('locales_source', $keys)->execute(); + $this->dbDelete('locales_location', $keys)->execute(); $string->setId(NULL); } } @@ -157,6 +211,7 @@ public function deleteStrings($conditions) { if ($lids) { $this->dbDelete('locales_target', array('lid' => $lids))->execute(); $this->dbDelete('locales_source', array('lid' => $lids))->execute(); + $this->dbDelete('locales_location', array('sid' => $lids))->execute(); } } @@ -191,11 +246,19 @@ public function createTranslation($values = array()) { * Field name to find the table alias for. * * @return string - * Either 's' or 't' depending on whether the field belongs to source or - * target table. + * Either 's', 't' or 'l' depending on whether the field belongs to source, + * target or location table table. */ protected function dbFieldTable($field) { - return in_array($field, array('language', 'translation', 'customized')) ? 't' : 's'; + if (in_array($field, array('language', 'translation', 'customized'))) { + return 't'; + } + elseif (in_array($field, array('type', 'name'))) { + return 'l'; + } + else { + return 's'; + } } /** @@ -367,6 +430,20 @@ protected function dbStringSelect(array $conditions, array $options = array()) { } } + // If we have conditins for location's type or name, then we need the + // location table, for which we add a subquery. + if (isset($conditions['type']) || isset($conditions['name'])) { + $subquery = $this->connection->select('locales_location', 'l', $this->options) + ->fields('l', array('sid')); + foreach (array('type', 'name') as $field) { + if (isset($conditions[$field])) { + $subquery->condition('l.' . $field, $conditions[$field]); + unset($conditions[$field]); + } + } + $query->condition('s.lid', $subquery, 'IN'); + } + // Add conditions for both tables. foreach ($conditions as $field => $value) { $table_alias = $this->dbFieldTable($field); diff --git a/core/modules/locale/lib/Drupal/locale/StringInterface.php b/core/modules/locale/lib/Drupal/locale/StringInterface.php index ec04386..171c6e2 100644 --- a/core/modules/locale/lib/Drupal/locale/StringInterface.php +++ b/core/modules/locale/lib/Drupal/locale/StringInterface.php @@ -158,6 +158,54 @@ public function setValues(array $values, $override = TRUE); public function getValues(array $fields); /** + * Gets location information for this string. + * + * Locations are arbitrary pairs of type and name strings, used to store + * information about the origins of the string, like the file name it + * was found on, the path on which it was discovered, etc... + * + * A string can have any number of locations since the same string may be + * found on different places of Drupal code and configuration. + * + * @param bool $load + * (optional) Whether to load the string locations if not loaded yet. + * Defaults to TRUE. + * + * @return array + * Location ids indexed by type and name. + */ + public function getLocation($load = TRUE); + + /** + * Adds a location for this string. + * + * @param string $type + * Location type that may be any arbitrary string. Types used in Drupal + * core are: 'javascript', 'path', 'code', 'configuration'. + * @param string $name + * Location name. Drupal path in case of online discovered translations, + * file path in case of imported strings, configuration name for strings + * that come from configuration, etc... + * + * @return Drupal\locale\LocaleString + * The called object. + */ + public function addLocation($type, $name); + + /** + * Checks whether the string has a given location. + * + * @param string $type. + * Location type. + * @param string $name. + * Location name. + * + * @return bool + * TRUE if the string has a location with this type and name. + */ + public function hasLocation($type, $name); + + /** * Saves string object to storage. * * @return Drupal\locale\LocaleString diff --git a/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php b/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php index bdfd5fa..d02774f 100644 --- a/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php +++ b/core/modules/locale/lib/Drupal/locale/StringStorageInterface.php @@ -50,6 +50,24 @@ public function getStrings(array $conditions = array(), array $options = array() * Array of Drupal\locale\StringInterface objects matching the conditions. */ public function getTranslations(array $conditions = array(), array $options = array()); + + /** + * Loads string location information. + * + * @see Drupal\locale\StringStorageInterface::getStrings() + * + * @param array $conditions + * (optional) Array with conditions to filter the locations that may be any + * of the follwing elements: + * - 'sid', The tring identifier. + * - 'type', The location type. + * - 'name', The location name. + * + * @return array + * Array of location objects matching the conditions. + */ + public function getLocations(array $conditions = array()); + /** * Loads a string source object, fast query. * @@ -69,6 +87,10 @@ public function findString(array $conditions); /** * Loads a string translation object, fast query. * + * This function must only be used when actually translating strings as it + * will have the effect of updating the string version. For other purposes + * the getTranslations() method should be used instead. + * * @param array $conditions * (optional) Array with conditions that will be used to filter the strings * returned and may include all of the conditions defined by getStrings(). @@ -79,16 +101,6 @@ public function findString(array $conditions); public function findTranslation(array $conditions); /** - * Checks whether the string version matches a given version, fix it if not. - * - * @param Drupal\locale\StringInterface $string - * The string object. - * @param string $version - * Drupal version to check against. - */ - public function checkVersion($string, $version); - - /** * Save string object to storage. * * @param Drupal\locale\StringInterface $string diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php index 8962267..d8e028f 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php @@ -37,12 +37,9 @@ function testFileParsing() { _locale_parse_js_file($filename); // Get all of the source strings that were found. - $source_strings = db_select('locales_source', 's') - ->fields('s', array('source', 'context')) - ->condition('s.location', $filename) - ->execute() - ->fetchAllKeyed(); - + foreach (locale_storage()->getStrings(array('type' => 'javascript', 'name' => $filename)) as $string) { + $source_strings[$string->source] = $string->context; + } // List of all strings that should be in the file. $test_strings = array( "Standard Call t" => '', diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php index 3c9e739..5dd4e23 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php @@ -72,8 +72,7 @@ function testStringCRUDAPI() { // Check version handling and updating. $this->assertEqual($source->version, 'none', 'String originally created without version.'); - $this->storage->checkVersion($source, VERSION); - $string = $this->storage->findString(array('lid' => $source->lid)); + $string = $this->storage->findTranslation(array('lid' => $source->lid)); $this->assertEqual($string->version, VERSION, 'Checked and updated string version to Drupal version.'); // Create translation and find it by lid and source. diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php index f3c528d..ec7dc42 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php @@ -70,9 +70,9 @@ function testUninstallProcess() { $user = $this->drupalCreateUser(array('translate interface', 'access administration pages')); $this->drupalLogin($user); $this->drupalGet('admin/config/regional/translate/translate'); - $string = db_query('SELECT min(lid) AS lid, source FROM {locales_source} WHERE location LIKE :location', array( - ':location' => '%.js%', - ))->fetchObject(); + // Get any of the javascript strings to translate. + $js_strings = locale_storage()->getStrings(array('type' => 'javascript')); + $string = reset($js_strings); $edit = array('string' => $string->source); $this->drupalPost('admin/config/regional/translate', $edit, t('Filter')); $edit = array('strings[' . $string->lid . '][translations][0]' => 'french translation'); diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index b7850db..0be2aec 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -492,17 +492,20 @@ function locale_translate_batch_import($filepath, $options, &$context) { $file->timestamp = filemtime($file->uri); locale_translate_update_file_history($file); $context['results']['files'][$filepath] = $filepath; + $context['results']['languages'][$filepath] = $file->langcode; } // Add the values from the report to the stats for this file. if (!isset($context['results']['stats']) || !isset($context['results']['stats'][$filepath])) { $context['results']['stats'][$filepath] = array(); } foreach ($report as $key => $value) { - if (is_numeric($report[$key])) { - if (!isset($context['results']['stats'][$filepath][$key])) { - $context['results']['stats'][$filepath][$key] = 0; - } - $context['results']['stats'][$filepath][$key] += $report[$key]; + if (is_numeric($value)) { + $context['results']['stats'][$filepath] += array($key => 0); + $context['results']['stats'][$filepath][$key] += $value; + } + elseif (is_array($value)) { + $context['results']['stats'][$filepath] += array($key => array()); + $context['results']['stats'][$filepath][$key] = array_merge($context['results']['stats'][$filepath][$key], $value); } } } @@ -519,6 +522,7 @@ function locale_translate_batch_import($filepath, $options, &$context) { function locale_translate_batch_finished($success, $results) { if ($success) { $additions = $updates = $deletes = $skips = 0; + $strings = $langcodes = array(); drupal_set_message(format_plural(count($results['files']), 'One translation file imported.', '@count translation files imported.')); $skipped_files = array(); // If there are no results and/or no stats (eg. coping with an empty .po @@ -532,7 +536,11 @@ function locale_translate_batch_finished($success, $results) { if ($report['skips'] > 0) { $skipped_files[] = $filepath; } + $strings = array_merge($strings, $report['strings']); } + // Get list of unique string identifiers and language codes updated. + $strings = array_unique($strings); + $langcodes = array_unique(array_values($results['languages'])); } drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes))); watchdog('locale', 'The translation was succesfully imported. %number new strings added, %update updated and %delete removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes)); @@ -547,9 +555,10 @@ function locale_translate_batch_finished($success, $results) { watchdog('locale', '@count disallowed HTML string(s) in files: @files.', array('@count' => $skips, '@files' => implode(',', $skipped_files)), WATCHDOG_WARNING); } - // Clear cache and force refresh of JavaScript translations. - _locale_invalidate_js(); - cache()->invalidateTags(array('locale' => TRUE)); + if ($strings) { + // Clear cache and force refresh of JavaScript translations. + _locale_refresh_translations($langcodes, $strings); + } } } diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install index b470963..5fa8a05 100644 --- a/core/modules/locale/locale.install +++ b/core/modules/locale/locale.install @@ -52,12 +52,6 @@ function locale_schema() { 'not null' => TRUE, 'description' => 'Unique identifier of this string.', ), - 'location' => array( - 'type' => 'text', - 'not null' => FALSE, - 'size' => 'big', - 'description' => 'Drupal path in case of online discovered translations or file path in case of imported strings.', - ), 'source' => array( 'type' => 'text', 'mysql_type' => 'blob', @@ -126,6 +120,54 @@ function locale_schema() { ), ); + $schema['locales_location'] = array( + 'description' => 'Location information for source strings.', + 'fields' => array( + 'lid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Unique identifier of this location.', + ), + 'sid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Unique identifier of this string.', + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 50, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The location type (file, config, path, etc).', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Type dependent location information (file name, path, etc).', + ), + 'version' => array( + 'type' => 'varchar', + 'length' => 20, + 'not null' => TRUE, + 'default' => 'none', + 'description' => 'Version of Drupal, where the location was found (for locales optimization).', + ), + ), + 'primary key' => array('lid'), + 'foreign keys' => array( + 'locales_source' => array( + 'table' => 'locales_source', + 'columns' => array('sid' => 'lid'), + ), + ), + 'indexes' => array( + 'string_id' => array('sid'), + 'string_type' => array('sid', 'type'), + ), + ); + $schema['locale_file'] = array( 'description' => 'File import status information for interface translation files.', 'fields' => array( diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 3d3ce1f..3ebe97d 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -771,8 +771,37 @@ function locale_string_is_safe($string) { } /** + * Refresh related information after string translations have been updated. + * + * The information that will be refreshed includes: + * - JavaScript translations. + * - Locale cache. + * + * @param array $langcodes + * Language codes for updated translations. + * @param array $lids + * List of string identifiers that have been updated / created. + */ +function _locale_refresh_translations($langcodes, $lids) { + if ($lids && $langcodes) { + // Update javascript translations if any of the strings has a javascript location. + if ($strings = locale_storage()->getStrings(array('lid' => $lids, 'type' => 'javascript'))) { + array_map('_locale_invalidate_js', $langcodes); + } + } + // Clear locale cache. + cache()->invalidateTags(array('locale' => TRUE)); +} + +/** * Parses a JavaScript file, extracts strings wrapped in Drupal.t() and * Drupal.formatPlural() and inserts them into the database. + * + * @param string $filepath + * File name to parse. + * + * @return array + * Array of string objects to update indexed by context and source. */ function _locale_parse_js_file($filepath) { // The file path might contain a query string, so make sure we only use the @@ -858,28 +887,17 @@ function _locale_parse_js_file($filepath) { $context = implode('', preg_split('~(?findString(array('source' => $string, 'context' => $context)); - if ($source) { - // 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. - $source->setValues(array('location' => $locations)) - ->save(); - } - } - else { + + if (!$source) { // We don't have the source string yet, thus we insert it into the database. - locale_storage()->createString(array( - 'location' => $filepath, + $source = locale_storage()->createString(array( 'source' => $string, 'context' => $context, - ))->save(); + )); } + // Besides adding the location this will tag it for current version. + $source->addLocation('javascript', $filepath); + $source->save(); } } @@ -934,10 +952,13 @@ function _locale_rebuild_js($langcode = NULL) { // Construct the array for JavaScript translations. // Only add strings with a translation to the translations array. - $options['filters']['location'] = '.js'; - $conditions['language'] = $language->langcode; + $conditions = array( + 'type' => 'javascript', + 'language' => $language->langcode, + 'translated' => TRUE, + ); $translations = array(); - foreach (locale_storage()->getTranslations($conditions, $options) as $data) { + foreach (locale_storage()->getTranslations($conditions) as $data) { $translations[$data->context][$data->source] = $data->translation; } diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc index 71aa14d..8ea8780 100644 --- a/core/modules/locale/locale.pages.inc +++ b/core/modules/locale/locale.pages.inc @@ -324,11 +324,6 @@ function locale_translate_edit_form($form, &$form_state) { '#value' => check_plain($string->context), ); } - $form['strings'][$string->lid]['location'] = array( - '#type' => 'value', - '#value' => $string->location, - ); - // Approximate the number of rows to use in the default textarea. $rows = min(ceil(str_word_count($source_array[0]) / 12), 10); if (empty($form['strings'][$string->lid]['plural']['#value'])) { @@ -403,10 +398,16 @@ function locale_translate_edit_form_validate($form, &$form_state) { */ function locale_translate_edit_form_submit($form, &$form_state) { $langcode = $form_state['values']['langcode']; - foreach ($form_state['values']['strings'] as $lid => $translations) { - // Get target string, that may be NULL if there's no translation. - $target = locale_storage()->findTranslation(array('language' => $langcode, 'lid' => $lid)); + $updated = array(); + + // Preload all translations for strings in the form. + $lids = array_keys($form_state['values']['strings']); + $strings = array(); + foreach (locale_storage()->getTranslations(array('lid' => $lids, 'language' => $langcode, 'translated' => TRUE)) as $string) { + $strings[$string->lid] = $string; + } + foreach ($form_state['values']['strings'] as $lid => $translations) { // No translation when all strings are empty. $has_translation = FALSE; foreach ($translations['translations'] as $string) { @@ -417,14 +418,16 @@ function locale_translate_edit_form_submit($form, &$form_state) { } if ($has_translation) { // Only update or insert if we have a value to use. - $target = $target && !$target->isNew() ? $target : locale_storage()->createTranslation(array('lid' => $lid, 'language' => $langcode)); + $target = isset($strings[$lid]) ? $strings[$lid] : locale_storage()->createTranslation(array('lid' => $lid, 'language' => $langcode)); $target->setPlurals($translations['translations']) ->setCustomized() ->save(); + $updated[] = $target->getId(); } - elseif ($target) { + elseif (isset($strings[$lid])) { // Empty translation entered: remove existing entry from database. - $target->delete(); + $strings[$lid]->delete(); + $updated[] = $lid; } } @@ -435,10 +438,11 @@ function locale_translate_edit_form_submit($form, &$form_state) { $form_state['redirect'] = array('admin/config/regional/translate', array('query' => array('page' => $_GET['page']))); } - // Force JavaScript translation file recreation for this language. - _locale_invalidate_js($langcode); - // Clear locale cache. - cache()->invalidateTags(array('locale' => TRUE)); + if ($updated) { + // Clear cache and force refresh of JavaScript translations. + _locale_refresh_translations(array($langcode), $updated); + } + } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php index 65c30de..89d5196 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php @@ -135,6 +135,10 @@ public function testLanguageUpgrade() { $translation_string = db_query("SELECT * FROM {locales_target} WHERE lid = 22 AND language = 'ca'")->fetchObject(); $this->assertEqual($translation_string->translation, implode(LOCALE_PLURAL_DELIMITER, array('1 byte', '@count bytes'))); + + // Check string locations have been upgraded successfully. + $strings = locale_storage()->getStrings(array('type' => 'javascript', 'name' => 'misc/drupal.js')); + $this->assertEqual(count($strings), 8, 'String locations have been successfully upgraded.'); } /**