annotate vendor/symfony/translation/Translator.php @ 8:50b0d041100e

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