3 namespace Drupal\locale;
5 use Drupal\Core\Cache\CacheBackendInterface;
6 use Drupal\Core\Cache\CacheCollector;
7 use Drupal\Core\Config\ConfigFactoryInterface;
8 use Drupal\Core\Language\LanguageManagerInterface;
9 use Drupal\Core\Lock\LockBackendInterface;
10 use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
11 use Symfony\Component\HttpFoundation\RequestStack;
14 * A cache collector to allow for dynamic building of the locale cache.
16 class LocaleLookup extends CacheCollector {
26 * The msgctxt context.
35 * @var \Drupal\locale\StringStorageInterface
37 protected $stringStorage;
40 * The cache backend that should be used.
42 * @var \Drupal\Core\Cache\CacheBackendInterface
47 * The lock backend that should be used.
49 * @var \Drupal\Core\Lock\LockBackendInterface
54 * The configuration factory.
56 * @var \Drupal\Core\Config\ConfigFactoryInterface
58 protected $configFactory;
61 * The language manager.
63 * @var \Drupal\Core\Language\LanguageManagerInterface
65 protected $languageManager;
70 * @var \Symfony\Component\HttpFoundation\RequestStack
72 protected $requestStack;
75 * Constructs a LocaleLookup object.
77 * @param string $langcode
79 * @param string $context
81 * @param \Drupal\locale\StringStorageInterface $string_storage
83 * @param \Drupal\Core\Cache\CacheBackendInterface $cache
85 * @param \Drupal\Core\Lock\LockBackendInterface $lock
87 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
89 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
90 * The language manager.
91 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
94 public function __construct($langcode, $context, StringStorageInterface $string_storage, CacheBackendInterface $cache, LockBackendInterface $lock, ConfigFactoryInterface $config_factory, LanguageManagerInterface $language_manager, RequestStack $request_stack) {
95 $this->langcode = $langcode;
96 $this->context = (string) $context;
97 $this->stringStorage = $string_storage;
98 $this->configFactory = $config_factory;
99 $this->languageManager = $language_manager;
101 $this->cache = $cache;
103 $this->tags = ['locale'];
104 $this->requestStack = $request_stack;
110 protected function getCid() {
111 if (!isset($this->cid)) {
112 // Add the current user's role IDs to the cache key, this ensures that,
113 // for example, strings for admin menu items and settings forms are not
114 // cached for anonymous users.
115 $user = \Drupal::currentUser();
116 $rids = $user ? implode(':', $user->getRoles()) : '';
117 $this->cid = "locale:{$this->langcode}:{$this->context}:$rids";
119 // Getting the roles from the current user might have resulted in t()
120 // calls that attempted to get translations from the locale cache. In that
121 // case they would not go into this method again as
122 // CacheCollector::lazyLoadCache() already set the loaded flag. They would
123 // however call resolveCacheMiss() and add that string to the list of
124 // cache misses that need to be written into the cache. Prevent that by
125 // resetting that list. All that happens in such a case are a few uncached
126 // translation lookups.
127 $this->keysToPersist = [];
135 protected function resolveCacheMiss($offset) {
136 $translation = $this->stringStorage->findTranslation([
137 'language' => $this->langcode,
139 'context' => $this->context,
143 $value = !empty($translation->translation) ? $translation->translation : TRUE;
146 // We don't have the source string, update the {locales_source} table to
147 // indicate the string is not translated.
148 $this->stringStorage->createString([
150 'context' => $this->context,
151 'version' => \Drupal::VERSION,
152 ])->addLocation('path', $this->requestStack->getCurrentRequest()->getRequestUri())->save();
156 // If there is no translation available for the current language then use
157 // language fallback to try other translations.
158 if ($value === TRUE) {
159 $fallbacks = $this->languageManager->getFallbackCandidates(['langcode' => $this->langcode, 'operation' => 'locale_lookup', 'data' => $offset]);
160 if (!empty($fallbacks)) {
161 foreach ($fallbacks as $langcode) {
162 $translation = $this->stringStorage->findTranslation([
163 'language' => $langcode,
165 'context' => $this->context,
168 if ($translation && !empty($translation->translation)) {
169 $value = $translation->translation;
176 if (is_string($value) && strpos($value, PluralTranslatableMarkup::DELIMITER) !== FALSE) {
177 // Community translations imported from localize.drupal.org as well as
178 // migrated translations may contain @count[number].
179 $value = preg_replace('!@count\[\d+\]!', '@count', $value);
182 $this->storage[$offset] = $value;
183 // Disabling the usage of string caching allows a module to watch for
184 // the exact list of strings used on a page. From a performance
185 // perspective that is a really bad idea, so we have no user
186 // interface for this. Be careful when turning this option off!
187 if ($this->configFactory->get('locale.settings')->get('cache_strings')) {
188 $this->persist($offset);