diff --git a/modules/locale/locale.module b/modules/locale/locale.module index b60c531..65a25c4 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -683,77 +683,93 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) { $langcode = isset($langcode) ? $langcode : $language->language; - // Store database cached translations in a static variable. Only build the - // cache after $language has been set to avoid an unnecessary cache rebuild. - if (!isset($locale_t[$langcode]) && isset($language)) { - $locale_t[$langcode] = array(); - // Disabling the usage of string caching allows a module to watch for - // the exact list of strings used on a page. From a performance - // perspective that is a really bad idea, so we have no user - // interface for this. Be careful when turning this option off! - if (variable_get('locale_cache_strings', 1) == 1) { - if ($cache = cache_get('locale:' . $langcode, 'cache')) { - $locale_t[$langcode] = $cache->data; - } - elseif (lock_acquire('locale_cache_' . $langcode)) { - // Refresh database stored cache of translations for given language. - // We only store short strings used in current version, to improve - // performance and consume less memory. - $result = db_query("SELECT s.source, s.context, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.textgroup = 'default' AND s.version = :version AND LENGTH(s.source) < :length", array(':language' => $langcode, ':version' => VERSION, ':length' => variable_get('locale_cache_length', 75))); - foreach ($result as $data) { - $locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation); - } - cache_set('locale:' . $langcode, $locale_t[$langcode]); - lock_release('locale_cache_' . $langcode); - } - } + // Strings are cached by langcode and user roles, using instances of the + // LocaleLookup class to handle string lookup and caching. + if (!isset($locale_t[$langcode][$context]) && isset($language)) { + $locale_t[$langcode][$context] = new LocaleLookup($langcode, $context); + } + return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]); +} + +/** + * Extends CacheArray to allow for dynamic building of the locale cache. + */ +class LocaleLookup extends DrupalCacheArray { + + /** + * A language code. + * @var string + */ + protected $langcode; + + /** + * The msgctxt context. + * @var string + */ + protected $context; + + /** + * Constructs a LocaleCache object. + */ + public function __construct($langcode, $context) { + $this->langcode = $langcode; + $this->context = (string) $context; + + // Add the current user's role IDs to the cache key, this ensures that, for + // example, strings for admin menu items and settings forms are not cached + // for anonymous users. + $rids = implode(':', array_keys($GLOBALS['user']->roles)); + parent::__construct("locale:$langcode:$context:$rids", 'cache'); } - // If we have the translation cached, skip checking the database - if (!isset($locale_t[$langcode][$context][$string])) { + /** + * Overrides DrupalCacheArray::resolveCacheMiss(). + */ + protected function resolveCacheMiss($offset) { + + $translation = db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.context = :context", array( - // We do not have this translation cached, so get it from the DB. - $translation = db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.context = :context AND s.textgroup = 'default'", array( - ':language' => $langcode, - ':source' => $string, - ':context' => (string) $context, + ':language' => $this->langcode, + ':source' => $offset, + ':context' => $this->context, ))->fetchObject(); if ($translation) { - // We have the source string at least. - // Cache translation string or TRUE if no translation exists. - $locale_t[$langcode][$context][$string] = (empty($translation->translation) ? TRUE : $translation->translation); - if ($translation->version != VERSION) { - // This is the first use of this string under current Drupal version. Save version - // and clear cache, to include the string into caching next time. Saved version is - // also a string-history information for later pruning of the tables. + // This is the first use of this string under current Drupal version. + // Update the {locales_source} table to indicate the string is current. db_update('locales_source') ->fields(array('version' => VERSION)) ->condition('lid', $translation->lid) ->execute(); - cache_clear_all('locale:', 'cache', TRUE); } + $value = !empty($translation->translation) ? $translation->translation : TRUE; } else { - // We don't have the source string, cache this as untranslated. + // We don't have the source string, update the {locales_source} table to + // indicate the string is not translated. db_merge('locales_source') ->insertFields(array( 'location' => request_uri(), 'version' => VERSION, )) ->key(array( - 'source' => $string, - 'context' => (string) $context, - 'textgroup' => 'default', + 'source' => $offset, + 'context' => $this->context, )) ->execute(); - $locale_t[$langcode][$context][$string] = TRUE; - // Clear locale cache so this string can be added in a later request. - cache_clear_all('locale:', 'cache', TRUE); + $value = TRUE; } - } - return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]); + $this->storage[$offset] = $value; + // Disabling the usage of string caching allows a module to watch for + // the exact list of strings used on a page. From a performance + // perspective that is a really bad idea, so we have no user + // interface for this. Be careful when turning this option off! + if (variable_get('locale_cache_strings', 1)) { + $this->persist($offset); + } + return $value; + } } /**