Issue #1777070 by Jose Reyero, Gábor Hojtsy: Refactor and clean up source string...
authorcatch
Tue, 23 Oct 2012 10:25:45 +0000 (11:25 +0100)
committercatch
Tue, 23 Oct 2012 10:25:45 +0000 (11:25 +0100)
14 files changed:
core/includes/update.inc
core/modules/locale/lib/Drupal/locale/LocaleLookup.php
core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
core/modules/locale/lib/Drupal/locale/StringBase.php
core/modules/locale/lib/Drupal/locale/StringDatabaseStorage.php
core/modules/locale/lib/Drupal/locale/StringInterface.php
core/modules/locale/lib/Drupal/locale/StringStorageInterface.php
core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
core/modules/locale/lib/Drupal/locale/Tests/LocaleStringTest.php
core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
core/modules/locale/locale.bulk.inc
core/modules/locale/locale.install
core/modules/locale/locale.module
core/modules/locale/locale.pages.inc

index fe8034c..acdf83e 100644 (file)
@@ -412,6 +412,60 @@ function update_prepare_d8_language() {
       );
       db_add_field('locales_target', 'customized', $spec);
     }
+    // Add locales_location table to track string locations.
+    // When updating in a non-English language, this table is used for
+    // refreshing JavaScript translations.
+    if (db_table_exists('locales_source') && !db_table_exists('locales_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.',
+          ),
+        ),
+        '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);
+    }
   }
 }
 
index cd2f9c0..9dd04f6 100644 (file)
@@ -61,7 +61,6 @@ class LocaleLookup extends CacheArray {
     ));
 
     if ($translation) {
-      $this->stringStorage->checkVersion($translation, VERSION);
       $value = !empty($translation->translation) ? $translation->translation : TRUE;
     }
     else {
@@ -70,9 +69,8 @@ class LocaleLookup extends CacheArray {
       $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;
index 7afa1db..b5b03c0 100644 (file)
@@ -102,6 +102,7 @@ class PoDatabaseWriter implements PoWriterInterface {
       'updates' => 0,
       'deletes' => 0,
       'skips' => 0,
+      'strings' => array(),
     );
     $this->_report = $report;
   }
@@ -226,13 +227,13 @@ class PoDatabaseWriter implements PoWriterInterface {
     $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 @@ class PoDatabaseWriter implements PoWriterInterface {
           $string->save();
           $this->_report['updates']++;
         }
+        $this->_report['strings'][] = $string->getId();
         return $string->lid;
       }
       else {
@@ -272,6 +274,7 @@ class PoDatabaseWriter implements PoWriterInterface {
         ))->save();
 
         $this->_report['additions']++;
+        $this->_report['strings'][] = $string->getId();
         return $string->lid;
       }
     }
@@ -279,6 +282,7 @@ class PoDatabaseWriter implements PoWriterInterface {
       // Empty translation, remove existing if instructed.
       $string->delete();
       $this->_report['deletes']++;
+      $this->_report['strings'][] = $string->lid;
       return $string->lid;
     }
   }
index 8ff9815..e2d9bcc 100644 (file)
@@ -22,11 +22,11 @@ abstract class StringBase implements StringInterface {
   public $lid;
 
   /**
-   * The string location.
+   * The string locations indexed by type.
    *
    * @var string
    */
-  public $location;
+  public $locations;
 
   /**
    * The source string.
@@ -152,6 +152,35 @@ abstract class StringBase implements StringInterface {
   }
 
   /**
+   * Implements Drupal\locale\StringInterface::getLocation().
+   */
+  public function getLocations($check_only = FALSE) {
+    if (!isset($this->locations) && !$check_only) {
+      $this->locations = array();
+      foreach ($this->getStorage()->getLocations(array('sid' => $this->getId())) as $location) {
+        $this->locations[$location->type][$location->name] = $location->lid;
+      }
+    }
+    return isset($this->locations) ? $this->locations : array();
+  }
+
+  /**
+   * Implements Drupal\locale\StringInterface::addLocation().
+   */
+  public function addLocation($type, $name) {
+    $this->locations[$type][$name] = TRUE;
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\locale\StringInterface::hasLocation().
+   */
+  public function hasLocation($type, $name) {
+    $locations = $this->getLocations();
+    return isset($locations[$type]) ? !empty($locations[$type][$name]) : FALSE;
+  }
+
+  /**
    * Implements Drupal\locale\LocaleString::save().
    */
   public function save() {
index 38e9df5..33138e7 100644 (file)
@@ -80,12 +80,25 @@ class StringDatabaseStorage implements StringStorageInterface {
     ->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 @@ class StringDatabaseStorage implements StringStorageInterface {
   }
 
   /**
-   * 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 @@ class StringDatabaseStorage implements StringStorageInterface {
     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 ($locations = $string->getLocations(TRUE)) {
+      $created = FALSE;
+      foreach ($locations as $type => $location) {
+        foreach ($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 @@ class StringDatabaseStorage implements StringStorageInterface {
       $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 @@ class StringDatabaseStorage implements StringStorageInterface {
     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 @@ class StringDatabaseStorage implements StringStorageInterface {
    *   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 @@ class StringDatabaseStorage implements StringStorageInterface {
       }
     }
 
+    // If we have conditions 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);
index ec04386..05e3c01 100644 (file)
@@ -158,6 +158,54 @@ interface StringInterface {
   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 $check_only
+   *   (optional) Set to TRUE to get only new locations added during the
+   *   current page request and not loading all existing locations.
+   *
+   * @return array
+   *   Location ids indexed by type and name.
+   */
+  public function getLocations($check_only = FALSE);
+
+  /**
+   * 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
index bdfd5fa..d02774f 100644 (file)
@@ -50,6 +50,24 @@ interface StringStorageInterface {
    *   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 @@ interface StringStorageInterface {
   /**
    * 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 @@ interface StringStorageInterface {
   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
index 8962267..d8e028f 100644 (file)
@@ -37,12 +37,9 @@ class LocaleJavascriptTranslation extends WebTestBase {
     _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" => '',
index 3c9e739..5dd4e23 100644 (file)
@@ -72,8 +72,7 @@ class LocaleStringTest extends WebTestBase {
 
     // 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.
index 3476db7..0dc3948 100644 (file)
@@ -70,9 +70,9 @@ class LocaleUninstallTest extends WebTestBase {
     $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');
index b7850db..0be2aec 100644 (file)
@@ -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);
+    }
   }
 }
 
index b470963..1e8a3e8 100644 (file)
@@ -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',
@@ -76,7 +70,7 @@ function locale_schema() {
         'length' => 20,
         'not null' => TRUE,
         'default' => 'none',
-        'description' => 'Version of Drupal, where the string was last used (for locales optimization).',
+        'description' => 'Version of Drupal where the string was last used (for locales optimization).',
       ),
     ),
     'primary key' => array('lid'),
@@ -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.',
+      ),
+    ),
+    '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(
@@ -741,6 +783,13 @@ function locale_update_8013() {
 }
 
 /**
+ * Drop old 'location' field.
+ */
+function locale_update_8014() {
+  db_drop_field('locales_source', 'location');
+}
+
+/**
  * @} End of "addtogroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
  */
index 3d3ce1f..3ebe97d 100644 (file)
@@ -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('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['context'], 1, -1)));
 
     $source = locale_storage()->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;
   }
 
index 71aa14d..8ea8780 100644 (file)
@@ -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);
+  }
+
 }
 
 /**