LocaleLookup.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. namespace Drupal\locale;
  3. use Drupal\Core\Cache\CacheBackendInterface;
  4. use Drupal\Core\Cache\CacheCollector;
  5. use Drupal\Core\Config\ConfigFactoryInterface;
  6. use Drupal\Core\Language\LanguageManagerInterface;
  7. use Drupal\Core\Lock\LockBackendInterface;
  8. use Symfony\Component\HttpFoundation\RequestStack;
  9. /**
  10. * A cache collector to allow for dynamic building of the locale cache.
  11. */
  12. class LocaleLookup extends CacheCollector {
  13. /**
  14. * A language code.
  15. *
  16. * @var string
  17. */
  18. protected $langcode;
  19. /**
  20. * The msgctxt context.
  21. *
  22. * @var string
  23. */
  24. protected $context;
  25. /**
  26. * The locale storage.
  27. *
  28. * @var \Drupal\locale\StringStorageInterface
  29. */
  30. protected $stringStorage;
  31. /**
  32. * The cache backend that should be used.
  33. *
  34. * @var \Drupal\Core\Cache\CacheBackendInterface
  35. */
  36. protected $cache;
  37. /**
  38. * The lock backend that should be used.
  39. *
  40. * @var \Drupal\Core\Lock\LockBackendInterface
  41. */
  42. protected $lock;
  43. /**
  44. * The configuration factory.
  45. *
  46. * @var \Drupal\Core\Config\ConfigFactoryInterface
  47. */
  48. protected $configFactory;
  49. /**
  50. * The language manager.
  51. *
  52. * @var \Drupal\Core\Language\LanguageManagerInterface
  53. */
  54. protected $languageManager;
  55. /**
  56. * The request stack.
  57. *
  58. * @var \Symfony\Component\HttpFoundation\RequestStack
  59. */
  60. protected $requestStack;
  61. /**
  62. * Constructs a LocaleLookup object.
  63. *
  64. * @param string $langcode
  65. * The language code.
  66. * @param string $context
  67. * The string context.
  68. * @param \Drupal\locale\StringStorageInterface $string_storage
  69. * The string storage.
  70. * @param \Drupal\Core\Cache\CacheBackendInterface $cache
  71. * The cache backend.
  72. * @param \Drupal\Core\Lock\LockBackendInterface $lock
  73. * The lock backend.
  74. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
  75. * The config factory.
  76. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
  77. * The language manager.
  78. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
  79. * The request stack.
  80. */
  81. public function __construct($langcode, $context, StringStorageInterface $string_storage, CacheBackendInterface $cache, LockBackendInterface $lock, ConfigFactoryInterface $config_factory, LanguageManagerInterface $language_manager, RequestStack $request_stack) {
  82. $this->langcode = $langcode;
  83. $this->context = (string) $context;
  84. $this->stringStorage = $string_storage;
  85. $this->configFactory = $config_factory;
  86. $this->languageManager = $language_manager;
  87. $this->cache = $cache;
  88. $this->lock = $lock;
  89. $this->tags = ['locale'];
  90. $this->requestStack = $request_stack;
  91. }
  92. /**
  93. * {@inheritdoc}
  94. */
  95. protected function getCid() {
  96. if (!isset($this->cid)) {
  97. // Add the current user's role IDs to the cache key, this ensures that,
  98. // for example, strings for admin menu items and settings forms are not
  99. // cached for anonymous users.
  100. $user = \Drupal::currentUser();
  101. $rids = $user ? implode(':', $user->getRoles()) : '';
  102. $this->cid = "locale:{$this->langcode}:{$this->context}:$rids";
  103. // Getting the roles from the current user might have resulted in t()
  104. // calls that attempted to get translations from the locale cache. In that
  105. // case they would not go into this method again as
  106. // CacheCollector::lazyLoadCache() already set the loaded flag. They would
  107. // however call resolveCacheMiss() and add that string to the list of
  108. // cache misses that need to be written into the cache. Prevent that by
  109. // resetting that list. All that happens in such a case are a few uncached
  110. // translation lookups.
  111. $this->keysToPersist = [];
  112. }
  113. return $this->cid;
  114. }
  115. /**
  116. * {@inheritdoc}
  117. */
  118. protected function resolveCacheMiss($offset) {
  119. $translation = $this->stringStorage->findTranslation([
  120. 'language' => $this->langcode,
  121. 'source' => $offset,
  122. 'context' => $this->context,
  123. ]);
  124. if ($translation) {
  125. $value = !empty($translation->translation) ? $translation->translation : TRUE;
  126. }
  127. else {
  128. // We don't have the source string, update the {locales_source} table to
  129. // indicate the string is not translated.
  130. $this->stringStorage->createString([
  131. 'source' => $offset,
  132. 'context' => $this->context,
  133. 'version' => \Drupal::VERSION,
  134. ])->addLocation('path', $this->requestStack->getCurrentRequest()->getRequestUri())->save();
  135. $value = TRUE;
  136. }
  137. // If there is no translation available for the current language then use
  138. // language fallback to try other translations.
  139. if ($value === TRUE) {
  140. $fallbacks = $this->languageManager->getFallbackCandidates(['langcode' => $this->langcode, 'operation' => 'locale_lookup', 'data' => $offset]);
  141. if (!empty($fallbacks)) {
  142. foreach ($fallbacks as $langcode) {
  143. $translation = $this->stringStorage->findTranslation([
  144. 'language' => $langcode,
  145. 'source' => $offset,
  146. 'context' => $this->context,
  147. ]);
  148. if ($translation && !empty($translation->translation)) {
  149. $value = $translation->translation;
  150. break;
  151. }
  152. }
  153. }
  154. }
  155. $this->storage[$offset] = $value;
  156. // Disabling the usage of string caching allows a module to watch for
  157. // the exact list of strings used on a page. From a performance
  158. // perspective that is a really bad idea, so we have no user
  159. // interface for this. Be careful when turning this option off!
  160. if ($this->configFactory->get('locale.settings')->get('cache_strings')) {
  161. $this->persist($offset);
  162. }
  163. return $value;
  164. }
  165. }