PluginProbe
Independent Analytics – WordPress Analytics Plugin / 2.9.2
Independent Analytics – WordPress Analytics Plugin v2.9.2
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 All 120 releases
independent-analytics / vendor / symfony / translation / Translator.php
Translator.php
408 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 namespace IAWPSCOPED\Symfony\Component\Translation;
12
13 use IAWPSCOPED\Symfony\Component\Config\ConfigCacheFactory;
14 use IAWPSCOPED\Symfony\Component\Config\ConfigCacheFactoryInterface;
15 use IAWPSCOPED\Symfony\Component\Config\ConfigCacheInterface;
16 use IAWPSCOPED\Symfony\Component\Translation\Exception\InvalidArgumentException;
17 use IAWPSCOPED\Symfony\Component\Translation\Exception\NotFoundResourceException;
18 use IAWPSCOPED\Symfony\Component\Translation\Exception\RuntimeException;
19 use IAWPSCOPED\Symfony\Component\Translation\Formatter\IntlFormatterInterface;
20 use IAWPSCOPED\Symfony\Component\Translation\Formatter\MessageFormatter;
21 use IAWPSCOPED\Symfony\Component\Translation\Formatter\MessageFormatterInterface;
22 use IAWPSCOPED\Symfony\Component\Translation\Loader\LoaderInterface;
23 use IAWPSCOPED\Symfony\Contracts\Translation\LocaleAwareInterface;
24 use IAWPSCOPED\Symfony\Contracts\Translation\TranslatorInterface;
25 // Help opcache.preload discover always-needed symbols
26 \class_exists(MessageCatalogue::class);
27 /**
28 * @author Fabien Potencier <fabien@symfony.com>
29 * @internal
30 */
31 class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface
32 {
33 /**
34 * @var MessageCatalogueInterface[]
35 */
36 protected $catalogues = [];
37 /**
38 * @var string
39 */
40 private $locale;
41 /**
42 * @var string[]
43 */
44 private $fallbackLocales = [];
45 /**
46 * @var LoaderInterface[]
47 */
48 private $loaders = [];
49 /**
50 * @var array
51 */
52 private $resources = [];
53 /**
54 * @var MessageFormatterInterface
55 */
56 private $formatter;
57 /**
58 * @var string
59 */
60 private $cacheDir;
61 /**
62 * @var bool
63 */
64 private $debug;
65 private $cacheVary;
66 /**
67 * @var ConfigCacheFactoryInterface|null
68 */
69 private $configCacheFactory;
70 /**
71 * @var array|null
72 */
73 private $parentLocales;
74 private $hasIntlFormatter;
75 /**
76 * @throws InvalidArgumentException If a locale contains invalid characters
77 */
78 public function __construct(string $locale, ?MessageFormatterInterface $formatter = null, ?string $cacheDir = null, bool $debug = \false, array $cacheVary = [])
79 {
80 $this->setLocale($locale);
81 if (null === $formatter) {
82 $formatter = new MessageFormatter();
83 }
84 $this->formatter = $formatter;
85 $this->cacheDir = $cacheDir;
86 $this->debug = $debug;
87 $this->cacheVary = $cacheVary;
88 $this->hasIntlFormatter = $formatter instanceof IntlFormatterInterface;
89 }
90 public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory)
91 {
92 $this->configCacheFactory = $configCacheFactory;
93 }
94 /**
95 * Adds a Loader.
96 *
97 * @param string $format The name of the loader (@see addResource())
98 */
99 public function addLoader(string $format, LoaderInterface $loader)
100 {
101 $this->loaders[$format] = $loader;
102 }
103 /**
104 * Adds a Resource.
105 *
106 * @param string $format The name of the loader (@see addLoader())
107 * @param mixed $resource The resource name
108 *
109 * @throws InvalidArgumentException If the locale contains invalid characters
110 */
111 public function addResource(string $format, $resource, string $locale, ?string $domain = null)
112 {
113 if (null === $domain) {
114 $domain = 'messages';
115 }
116 $this->assertValidLocale($locale);
117 $locale ?: ($locale = \class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
118 $this->resources[$locale][] = [$format, $resource, $domain];
119 if (\in_array($locale, $this->fallbackLocales)) {
120 $this->catalogues = [];
121 } else {
122 unset($this->catalogues[$locale]);
123 }
124 }
125 /**
126 * {@inheritdoc}
127 */
128 public function setLocale(string $locale)
129 {
130 $this->assertValidLocale($locale);
131 $this->locale = $locale;
132 }
133 /**
134 * {@inheritdoc}
135 */
136 public function getLocale()
137 {
138 return $this->locale ?: (\class_exists(\Locale::class) ? \Locale::getDefault() : 'en');
139 }
140 /**
141 * Sets the fallback locales.
142 *
143 * @param string[] $locales
144 *
145 * @throws InvalidArgumentException If a locale contains invalid characters
146 */
147 public function setFallbackLocales(array $locales)
148 {
149 // needed as the fallback locales are linked to the already loaded catalogues
150 $this->catalogues = [];
151 foreach ($locales as $locale) {
152 $this->assertValidLocale($locale);
153 }
154 $this->fallbackLocales = $this->cacheVary['fallback_locales'] = $locales;
155 }
156 /**
157 * Gets the fallback locales.
158 *
159 * @internal
160 */
161 public function getFallbackLocales() : array
162 {
163 return $this->fallbackLocales;
164 }
165 /**
166 * {@inheritdoc}
167 */
168 public function trans(?string $id, array $parameters = [], ?string $domain = null, ?string $locale = null)
169 {
170 if (null === $id || '' === $id) {
171 return '';
172 }
173 if (null === $domain) {
174 $domain = 'messages';
175 }
176 $catalogue = $this->getCatalogue($locale);
177 $locale = $catalogue->getLocale();
178 while (!$catalogue->defines($id, $domain)) {
179 if ($cat = $catalogue->getFallbackCatalogue()) {
180 $catalogue = $cat;
181 $locale = $catalogue->getLocale();
182 } else {
183 break;
184 }
185 }
186 $len = \strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX);
187 if ($this->hasIntlFormatter && ($catalogue->defines($id, $domain . MessageCatalogue::INTL_DOMAIN_SUFFIX) || \strlen($domain) > $len && 0 === \substr_compare($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX, -$len, $len))) {
188 return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, $parameters);
189 }
190 return $this->formatter->format($catalogue->get($id, $domain), $locale, $parameters);
191 }
192 /**
193 * {@inheritdoc}
194 */
195 public function getCatalogue(?string $locale = null)
196 {
197 if (!$locale) {
198 $locale = $this->getLocale();
199 } else {
200 $this->assertValidLocale($locale);
201 }
202 if (!isset($this->catalogues[$locale])) {
203 $this->loadCatalogue($locale);
204 }
205 return $this->catalogues[$locale];
206 }
207 /**
208 * {@inheritdoc}
209 */
210 public function getCatalogues() : array
211 {
212 return \array_values($this->catalogues);
213 }
214 /**
215 * Gets the loaders.
216 *
217 * @return LoaderInterface[]
218 */
219 protected function getLoaders()
220 {
221 return $this->loaders;
222 }
223 protected function loadCatalogue(string $locale)
224 {
225 if (null === $this->cacheDir) {
226 $this->initializeCatalogue($locale);
227 } else {
228 $this->initializeCacheCatalogue($locale);
229 }
230 }
231 protected function initializeCatalogue(string $locale)
232 {
233 $this->assertValidLocale($locale);
234 try {
235 $this->doLoadCatalogue($locale);
236 } catch (NotFoundResourceException $e) {
237 if (!$this->computeFallbackLocales($locale)) {
238 throw $e;
239 }
240 }
241 $this->loadFallbackCatalogues($locale);
242 }
243 private function initializeCacheCatalogue(string $locale) : void
244 {
245 if (isset($this->catalogues[$locale])) {
246 /* Catalogue already initialized. */
247 return;
248 }
249 $this->assertValidLocale($locale);
250 $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale), function (ConfigCacheInterface $cache) use($locale) {
251 $this->dumpCatalogue($locale, $cache);
252 });
253 if (isset($this->catalogues[$locale])) {
254 /* Catalogue has been initialized as it was written out to cache. */
255 return;
256 }
257 /* Read catalogue from cache. */
258 $this->catalogues[$locale] = (include $cache->getPath());
259 }
260 private function dumpCatalogue(string $locale, ConfigCacheInterface $cache) : void
261 {
262 $this->initializeCatalogue($locale);
263 $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]);
264 $content = \sprintf(<<<EOF
265 <?php
266
267 use Symfony\\Component\\Translation\\MessageCatalogue;
268
269 \$catalogue = new MessageCatalogue('%s', %s);
270
271 %s
272 return \$catalogue;
273
274 EOF
275 , $locale, \var_export($this->getAllMessages($this->catalogues[$locale]), \true), $fallbackContent);
276 $cache->write($content, $this->catalogues[$locale]->getResources());
277 }
278 private function getFallbackContent(MessageCatalogue $catalogue) : string
279 {
280 $fallbackContent = '';
281 $current = '';
282 $replacementPattern = '/[^a-z0-9_]/i';
283 $fallbackCatalogue = $catalogue->getFallbackCatalogue();
284 while ($fallbackCatalogue) {
285 $fallback = $fallbackCatalogue->getLocale();
286 $fallbackSuffix = \ucfirst(\preg_replace($replacementPattern, '_', $fallback));
287 $currentSuffix = \ucfirst(\preg_replace($replacementPattern, '_', $current));
288 $fallbackContent .= \sprintf(<<<'EOF'
289 $catalogue%s = new MessageCatalogue('%s', %s);
290 $catalogue%s->addFallbackCatalogue($catalogue%s);
291
292 EOF
293 , $fallbackSuffix, $fallback, \var_export($this->getAllMessages($fallbackCatalogue), \true), $currentSuffix, $fallbackSuffix);
294 $current = $fallbackCatalogue->getLocale();
295 $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue();
296 }
297 return $fallbackContent;
298 }
299 private function getCatalogueCachePath(string $locale) : string
300 {
301 return $this->cacheDir . '/catalogue.' . $locale . '.' . \strtr(\substr(\base64_encode(\hash('sha256', \serialize($this->cacheVary), \true)), 0, 7), '/', '_') . '.php';
302 }
303 /**
304 * @internal
305 */
306 protected function doLoadCatalogue(string $locale) : void
307 {
308 $this->catalogues[$locale] = new MessageCatalogue($locale);
309 if (isset($this->resources[$locale])) {
310 foreach ($this->resources[$locale] as $resource) {
311 if (!isset($this->loaders[$resource[0]])) {
312 if (\is_string($resource[1])) {
313 throw new RuntimeException(\sprintf('No loader is registered for the "%s" format when loading the "%s" resource.', $resource[0], $resource[1]));
314 }
315 throw new RuntimeException(\sprintf('No loader is registered for the "%s" format.', $resource[0]));
316 }
317 $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
318 }
319 }
320 }
321 private function loadFallbackCatalogues(string $locale) : void
322 {
323 $current = $this->catalogues[$locale];
324 foreach ($this->computeFallbackLocales($locale) as $fallback) {
325 if (!isset($this->catalogues[$fallback])) {
326 $this->initializeCatalogue($fallback);
327 }
328 $fallbackCatalogue = new MessageCatalogue($fallback, $this->getAllMessages($this->catalogues[$fallback]));
329 foreach ($this->catalogues[$fallback]->getResources() as $resource) {
330 $fallbackCatalogue->addResource($resource);
331 }
332 $current->addFallbackCatalogue($fallbackCatalogue);
333 $current = $fallbackCatalogue;
334 }
335 }
336 protected function computeFallbackLocales(string $locale)
337 {
338 if (null === $this->parentLocales) {
339 $this->parentLocales = \json_decode(\file_get_contents(__DIR__ . '/Resources/data/parents.json'), \true);
340 }
341 $originLocale = $locale;
342 $locales = [];
343 while ($locale) {
344 $parent = $this->parentLocales[$locale] ?? null;
345 if ($parent) {
346 $locale = 'root' !== $parent ? $parent : null;
347 } elseif (\function_exists('locale_parse')) {
348 $localeSubTags = \locale_parse($locale);
349 $locale = null;
350 if (1 < \count($localeSubTags)) {
351 \array_pop($localeSubTags);
352 $locale = \locale_compose($localeSubTags) ?: null;
353 }
354 } elseif ($i = \strrpos($locale, '_') ?: \strrpos($locale, '-')) {
355 $locale = \substr($locale, 0, $i);
356 } else {
357 $locale = null;
358 }
359 if (null !== $locale) {
360 $locales[] = $locale;
361 }
362 }
363 foreach ($this->fallbackLocales as $fallback) {
364 if ($fallback === $originLocale) {
365 continue;
366 }
367 $locales[] = $fallback;
368 }
369 return \array_unique($locales);
370 }
371 /**
372 * Asserts that the locale is valid, throws an Exception if not.
373 *
374 * @throws InvalidArgumentException If the locale contains invalid characters
375 */
376 protected function assertValidLocale(string $locale)
377 {
378 if (!\preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
379 throw new InvalidArgumentException(\sprintf('Invalid "%s" locale.', $locale));
380 }
381 }
382 /**
383 * Provides the ConfigCache factory implementation, falling back to a
384 * default implementation if necessary.
385 */
386 private function getConfigCacheFactory() : ConfigCacheFactoryInterface
387 {
388 if (!$this->configCacheFactory) {
389 $this->configCacheFactory = new ConfigCacheFactory($this->debug);
390 }
391 return $this->configCacheFactory;
392 }
393 private function getAllMessages(MessageCatalogueInterface $catalogue) : array
394 {
395 $allMessages = [];
396 foreach ($catalogue->all() as $domain => $messages) {
397 if ($intlMessages = $catalogue->all($domain . MessageCatalogue::INTL_DOMAIN_SUFFIX)) {
398 $allMessages[$domain . MessageCatalogue::INTL_DOMAIN_SUFFIX] = $intlMessages;
399 $messages = \array_diff_key($messages, $intlMessages);
400 }
401 if ($messages) {
402 $allMessages[$domain] = $messages;
403 }
404 }
405 return $allMessages;
406 }
407 }
408