Mercurial > hg > cmmr2012-drupal-site
comparison vendor/symfony/translation/Translator.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
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 | |
12 namespace Symfony\Component\Translation; | |
13 | |
14 use Symfony\Component\Translation\Loader\LoaderInterface; | |
15 use Symfony\Component\Translation\Exception\NotFoundResourceException; | |
16 use Symfony\Component\Translation\Exception\InvalidArgumentException; | |
17 use Symfony\Component\Translation\Exception\LogicException; | |
18 use Symfony\Component\Translation\Exception\RuntimeException; | |
19 use Symfony\Component\Config\ConfigCacheInterface; | |
20 use Symfony\Component\Config\ConfigCacheFactoryInterface; | |
21 use Symfony\Component\Config\ConfigCacheFactory; | |
22 use Symfony\Component\Translation\Formatter\MessageFormatterInterface; | |
23 use Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface; | |
24 use Symfony\Component\Translation\Formatter\MessageFormatter; | |
25 | |
26 /** | |
27 * @author Fabien Potencier <fabien@symfony.com> | |
28 */ | |
29 class Translator implements TranslatorInterface, TranslatorBagInterface | |
30 { | |
31 /** | |
32 * @var MessageCatalogueInterface[] | |
33 */ | |
34 protected $catalogues = array(); | |
35 | |
36 /** | |
37 * @var string | |
38 */ | |
39 private $locale; | |
40 | |
41 /** | |
42 * @var array | |
43 */ | |
44 private $fallbackLocales = array(); | |
45 | |
46 /** | |
47 * @var LoaderInterface[] | |
48 */ | |
49 private $loaders = array(); | |
50 | |
51 /** | |
52 * @var array | |
53 */ | |
54 private $resources = array(); | |
55 | |
56 /** | |
57 * @var MessageFormatterInterface | |
58 */ | |
59 private $formatter; | |
60 | |
61 /** | |
62 * @var string | |
63 */ | |
64 private $cacheDir; | |
65 | |
66 /** | |
67 * @var bool | |
68 */ | |
69 private $debug; | |
70 | |
71 /** | |
72 * @var ConfigCacheFactoryInterface|null | |
73 */ | |
74 private $configCacheFactory; | |
75 | |
76 /** | |
77 * @param string $locale The locale | |
78 * @param MessageFormatterInterface|null $formatter The message formatter | |
79 * @param string|null $cacheDir The directory to use for the cache | |
80 * @param bool $debug Use cache in debug mode ? | |
81 * | |
82 * @throws InvalidArgumentException If a locale contains invalid characters | |
83 */ | |
84 public function __construct($locale, $formatter = null, $cacheDir = null, $debug = false) | |
85 { | |
86 $this->setLocale($locale); | |
87 | |
88 if ($formatter instanceof MessageSelector) { | |
89 $formatter = new MessageFormatter($formatter); | |
90 @trigger_error(sprintf('Passing a "%s" instance into the "%s" as a second argument is deprecated since Symfony 3.4 and will be removed in 4.0. Inject a "%s" implementation instead.', MessageSelector::class, __METHOD__, MessageFormatterInterface::class), E_USER_DEPRECATED); | |
91 } elseif (null === $formatter) { | |
92 $formatter = new MessageFormatter(); | |
93 } | |
94 | |
95 $this->formatter = $formatter; | |
96 $this->cacheDir = $cacheDir; | |
97 $this->debug = $debug; | |
98 } | |
99 | |
100 public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory) | |
101 { | |
102 $this->configCacheFactory = $configCacheFactory; | |
103 } | |
104 | |
105 /** | |
106 * Adds a Loader. | |
107 * | |
108 * @param string $format The name of the loader (@see addResource()) | |
109 * @param LoaderInterface $loader A LoaderInterface instance | |
110 */ | |
111 public function addLoader($format, LoaderInterface $loader) | |
112 { | |
113 $this->loaders[$format] = $loader; | |
114 } | |
115 | |
116 /** | |
117 * Adds a Resource. | |
118 * | |
119 * @param string $format The name of the loader (@see addLoader()) | |
120 * @param mixed $resource The resource name | |
121 * @param string $locale The locale | |
122 * @param string $domain The domain | |
123 * | |
124 * @throws InvalidArgumentException If the locale contains invalid characters | |
125 */ | |
126 public function addResource($format, $resource, $locale, $domain = null) | |
127 { | |
128 if (null === $domain) { | |
129 $domain = 'messages'; | |
130 } | |
131 | |
132 $this->assertValidLocale($locale); | |
133 | |
134 $this->resources[$locale][] = array($format, $resource, $domain); | |
135 | |
136 if (in_array($locale, $this->fallbackLocales)) { | |
137 $this->catalogues = array(); | |
138 } else { | |
139 unset($this->catalogues[$locale]); | |
140 } | |
141 } | |
142 | |
143 /** | |
144 * {@inheritdoc} | |
145 */ | |
146 public function setLocale($locale) | |
147 { | |
148 $this->assertValidLocale($locale); | |
149 $this->locale = $locale; | |
150 } | |
151 | |
152 /** | |
153 * {@inheritdoc} | |
154 */ | |
155 public function getLocale() | |
156 { | |
157 return $this->locale; | |
158 } | |
159 | |
160 /** | |
161 * Sets the fallback locales. | |
162 * | |
163 * @param array $locales The fallback locales | |
164 * | |
165 * @throws InvalidArgumentException If a locale contains invalid characters | |
166 */ | |
167 public function setFallbackLocales(array $locales) | |
168 { | |
169 // needed as the fallback locales are linked to the already loaded catalogues | |
170 $this->catalogues = array(); | |
171 | |
172 foreach ($locales as $locale) { | |
173 $this->assertValidLocale($locale); | |
174 } | |
175 | |
176 $this->fallbackLocales = $locales; | |
177 } | |
178 | |
179 /** | |
180 * Gets the fallback locales. | |
181 * | |
182 * @return array $locales The fallback locales | |
183 */ | |
184 public function getFallbackLocales() | |
185 { | |
186 return $this->fallbackLocales; | |
187 } | |
188 | |
189 /** | |
190 * {@inheritdoc} | |
191 */ | |
192 public function trans($id, array $parameters = array(), $domain = null, $locale = null) | |
193 { | |
194 if (null === $domain) { | |
195 $domain = 'messages'; | |
196 } | |
197 | |
198 return $this->formatter->format($this->getCatalogue($locale)->get((string) $id, $domain), $locale, $parameters); | |
199 } | |
200 | |
201 /** | |
202 * {@inheritdoc} | |
203 */ | |
204 public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) | |
205 { | |
206 if (!$this->formatter instanceof ChoiceMessageFormatterInterface) { | |
207 throw new LogicException(sprintf('The formatter "%s" does not support plural translations.', get_class($this->formatter))); | |
208 } | |
209 | |
210 if (null === $domain) { | |
211 $domain = 'messages'; | |
212 } | |
213 | |
214 $id = (string) $id; | |
215 $catalogue = $this->getCatalogue($locale); | |
216 $locale = $catalogue->getLocale(); | |
217 while (!$catalogue->defines($id, $domain)) { | |
218 if ($cat = $catalogue->getFallbackCatalogue()) { | |
219 $catalogue = $cat; | |
220 $locale = $catalogue->getLocale(); | |
221 } else { | |
222 break; | |
223 } | |
224 } | |
225 | |
226 return $this->formatter->choiceFormat($catalogue->get($id, $domain), $number, $locale, $parameters); | |
227 } | |
228 | |
229 /** | |
230 * {@inheritdoc} | |
231 */ | |
232 public function getCatalogue($locale = null) | |
233 { | |
234 if (null === $locale) { | |
235 $locale = $this->getLocale(); | |
236 } else { | |
237 $this->assertValidLocale($locale); | |
238 } | |
239 | |
240 if (!isset($this->catalogues[$locale])) { | |
241 $this->loadCatalogue($locale); | |
242 } | |
243 | |
244 return $this->catalogues[$locale]; | |
245 } | |
246 | |
247 /** | |
248 * Gets the loaders. | |
249 * | |
250 * @return array LoaderInterface[] | |
251 */ | |
252 protected function getLoaders() | |
253 { | |
254 return $this->loaders; | |
255 } | |
256 | |
257 /** | |
258 * @param string $locale | |
259 */ | |
260 protected function loadCatalogue($locale) | |
261 { | |
262 if (null === $this->cacheDir) { | |
263 $this->initializeCatalogue($locale); | |
264 } else { | |
265 $this->initializeCacheCatalogue($locale); | |
266 } | |
267 } | |
268 | |
269 /** | |
270 * @param string $locale | |
271 */ | |
272 protected function initializeCatalogue($locale) | |
273 { | |
274 $this->assertValidLocale($locale); | |
275 | |
276 try { | |
277 $this->doLoadCatalogue($locale); | |
278 } catch (NotFoundResourceException $e) { | |
279 if (!$this->computeFallbackLocales($locale)) { | |
280 throw $e; | |
281 } | |
282 } | |
283 $this->loadFallbackCatalogues($locale); | |
284 } | |
285 | |
286 /** | |
287 * @param string $locale | |
288 */ | |
289 private function initializeCacheCatalogue($locale) | |
290 { | |
291 if (isset($this->catalogues[$locale])) { | |
292 /* Catalogue already initialized. */ | |
293 return; | |
294 } | |
295 | |
296 $this->assertValidLocale($locale); | |
297 $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale), | |
298 function (ConfigCacheInterface $cache) use ($locale) { | |
299 $this->dumpCatalogue($locale, $cache); | |
300 } | |
301 ); | |
302 | |
303 if (isset($this->catalogues[$locale])) { | |
304 /* Catalogue has been initialized as it was written out to cache. */ | |
305 return; | |
306 } | |
307 | |
308 /* Read catalogue from cache. */ | |
309 $this->catalogues[$locale] = include $cache->getPath(); | |
310 } | |
311 | |
312 private function dumpCatalogue($locale, ConfigCacheInterface $cache) | |
313 { | |
314 $this->initializeCatalogue($locale); | |
315 $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]); | |
316 | |
317 $content = sprintf(<<<EOF | |
318 <?php | |
319 | |
320 use Symfony\Component\Translation\MessageCatalogue; | |
321 | |
322 \$catalogue = new MessageCatalogue('%s', %s); | |
323 | |
324 %s | |
325 return \$catalogue; | |
326 | |
327 EOF | |
328 , | |
329 $locale, | |
330 var_export($this->catalogues[$locale]->all(), true), | |
331 $fallbackContent | |
332 ); | |
333 | |
334 $cache->write($content, $this->catalogues[$locale]->getResources()); | |
335 } | |
336 | |
337 private function getFallbackContent(MessageCatalogue $catalogue) | |
338 { | |
339 $fallbackContent = ''; | |
340 $current = ''; | |
341 $replacementPattern = '/[^a-z0-9_]/i'; | |
342 $fallbackCatalogue = $catalogue->getFallbackCatalogue(); | |
343 while ($fallbackCatalogue) { | |
344 $fallback = $fallbackCatalogue->getLocale(); | |
345 $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback)); | |
346 $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current)); | |
347 | |
348 $fallbackContent .= sprintf(<<<'EOF' | |
349 $catalogue%s = new MessageCatalogue('%s', %s); | |
350 $catalogue%s->addFallbackCatalogue($catalogue%s); | |
351 | |
352 EOF | |
353 , | |
354 $fallbackSuffix, | |
355 $fallback, | |
356 var_export($fallbackCatalogue->all(), true), | |
357 $currentSuffix, | |
358 $fallbackSuffix | |
359 ); | |
360 $current = $fallbackCatalogue->getLocale(); | |
361 $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); | |
362 } | |
363 | |
364 return $fallbackContent; | |
365 } | |
366 | |
367 private function getCatalogueCachePath($locale) | |
368 { | |
369 return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php'; | |
370 } | |
371 | |
372 private function doLoadCatalogue($locale) | |
373 { | |
374 $this->catalogues[$locale] = new MessageCatalogue($locale); | |
375 | |
376 if (isset($this->resources[$locale])) { | |
377 foreach ($this->resources[$locale] as $resource) { | |
378 if (!isset($this->loaders[$resource[0]])) { | |
379 throw new RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0])); | |
380 } | |
381 $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2])); | |
382 } | |
383 } | |
384 } | |
385 | |
386 private function loadFallbackCatalogues($locale) | |
387 { | |
388 $current = $this->catalogues[$locale]; | |
389 | |
390 foreach ($this->computeFallbackLocales($locale) as $fallback) { | |
391 if (!isset($this->catalogues[$fallback])) { | |
392 $this->initializeCatalogue($fallback); | |
393 } | |
394 | |
395 $fallbackCatalogue = new MessageCatalogue($fallback, $this->catalogues[$fallback]->all()); | |
396 foreach ($this->catalogues[$fallback]->getResources() as $resource) { | |
397 $fallbackCatalogue->addResource($resource); | |
398 } | |
399 $current->addFallbackCatalogue($fallbackCatalogue); | |
400 $current = $fallbackCatalogue; | |
401 } | |
402 } | |
403 | |
404 protected function computeFallbackLocales($locale) | |
405 { | |
406 $locales = array(); | |
407 foreach ($this->fallbackLocales as $fallback) { | |
408 if ($fallback === $locale) { | |
409 continue; | |
410 } | |
411 | |
412 $locales[] = $fallback; | |
413 } | |
414 | |
415 if (false !== strrchr($locale, '_')) { | |
416 array_unshift($locales, substr($locale, 0, -strlen(strrchr($locale, '_')))); | |
417 } | |
418 | |
419 return array_unique($locales); | |
420 } | |
421 | |
422 /** | |
423 * Asserts that the locale is valid, throws an Exception if not. | |
424 * | |
425 * @param string $locale Locale to tests | |
426 * | |
427 * @throws InvalidArgumentException If the locale contains invalid characters | |
428 */ | |
429 protected function assertValidLocale($locale) | |
430 { | |
431 if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) { | |
432 throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale)); | |
433 } | |
434 } | |
435 | |
436 /** | |
437 * Provides the ConfigCache factory implementation, falling back to a | |
438 * default implementation if necessary. | |
439 * | |
440 * @return ConfigCacheFactoryInterface $configCacheFactory | |
441 */ | |
442 private function getConfigCacheFactory() | |
443 { | |
444 if (!$this->configCacheFactory) { | |
445 $this->configCacheFactory = new ConfigCacheFactory($this->debug); | |
446 } | |
447 | |
448 return $this->configCacheFactory; | |
449 } | |
450 } |