123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- <?php
- /*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Symfony\Component\Translation;
- use Symfony\Component\Translation\Loader\LoaderInterface;
- use Symfony\Component\Translation\Exception\NotFoundResourceException;
- use Symfony\Component\Config\ConfigCache;
- /**
- * Translator.
- *
- * @author Fabien Potencier <fabien@symfony.com>
- *
- * @api
- */
- class Translator implements TranslatorInterface, TranslatorBagInterface
- {
- /**
- * @var MessageCatalogueInterface[]
- */
- protected $catalogues = array();
- /**
- * @var string
- */
- protected $locale;
- /**
- * @var array
- */
- private $fallbackLocales = array();
- /**
- * @var LoaderInterface[]
- */
- private $loaders = array();
- /**
- * @var array
- */
- private $resources = array();
- /**
- * @var MessageSelector
- */
- private $selector;
- /**
- * @var string
- */
- private $cacheDir;
- /**
- * @var bool
- */
- private $debug;
- /**
- * Constructor.
- *
- * @param string $locale The locale
- * @param MessageSelector|null $selector The message selector for pluralization
- * @param string|null $cacheDir The directory to use for the cache
- * @param bool $debug Use cache in debug mode ?
- *
- * @throws \InvalidArgumentException If a locale contains invalid characters
- *
- * @api
- */
- public function __construct($locale, MessageSelector $selector = null, $cacheDir = null, $debug = false)
- {
- $this->setLocale($locale);
- $this->selector = $selector ?: new MessageSelector();
- $this->cacheDir = $cacheDir;
- $this->debug = $debug;
- }
- /**
- * Adds a Loader.
- *
- * @param string $format The name of the loader (@see addResource())
- * @param LoaderInterface $loader A LoaderInterface instance
- *
- * @api
- */
- public function addLoader($format, LoaderInterface $loader)
- {
- $this->loaders[$format] = $loader;
- }
- /**
- * Adds a Resource.
- *
- * @param string $format The name of the loader (@see addLoader())
- * @param mixed $resource The resource name
- * @param string $locale The locale
- * @param string $domain The domain
- *
- * @throws \InvalidArgumentException If the locale contains invalid characters
- *
- * @api
- */
- public function addResource($format, $resource, $locale, $domain = null)
- {
- if (null === $domain) {
- $domain = 'messages';
- }
- $this->assertValidLocale($locale);
- $this->resources[$locale][] = array($format, $resource, $domain);
- if (in_array($locale, $this->fallbackLocales)) {
- $this->catalogues = array();
- } else {
- unset($this->catalogues[$locale]);
- }
- }
- /**
- * {@inheritdoc}
- *
- * @api
- */
- public function setLocale($locale)
- {
- $this->assertValidLocale($locale);
- $this->locale = $locale;
- }
- /**
- * {@inheritdoc}
- *
- * @api
- */
- public function getLocale()
- {
- return $this->locale;
- }
- /**
- * Sets the fallback locale(s).
- *
- * @param string|array $locales The fallback locale(s)
- *
- * @throws \InvalidArgumentException If a locale contains invalid characters
- *
- * @deprecated since 2.3, to be removed in 3.0. Use setFallbackLocales() instead.
- *
- * @api
- */
- public function setFallbackLocale($locales)
- {
- $this->setFallbackLocales(is_array($locales) ? $locales : array($locales));
- }
- /**
- * Sets the fallback locales.
- *
- * @param array $locales The fallback locales
- *
- * @throws \InvalidArgumentException If a locale contains invalid characters
- *
- * @api
- */
- public function setFallbackLocales(array $locales)
- {
- // needed as the fallback locales are linked to the already loaded catalogues
- $this->catalogues = array();
- foreach ($locales as $locale) {
- $this->assertValidLocale($locale);
- }
- $this->fallbackLocales = $locales;
- }
- /**
- * Gets the fallback locales.
- *
- * @return array $locales The fallback locales
- *
- * @api
- */
- public function getFallbackLocales()
- {
- return $this->fallbackLocales;
- }
- /**
- * {@inheritdoc}
- *
- * @api
- */
- public function trans($id, array $parameters = array(), $domain = null, $locale = null)
- {
- if (null === $locale) {
- $locale = $this->getLocale();
- } else {
- $this->assertValidLocale($locale);
- }
- if (null === $domain) {
- $domain = 'messages';
- }
- if (!isset($this->catalogues[$locale])) {
- $this->loadCatalogue($locale);
- }
- return strtr($this->catalogues[$locale]->get((string) $id, $domain), $parameters);
- }
- /**
- * {@inheritdoc}
- *
- * @api
- */
- public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
- {
- if (null === $locale) {
- $locale = $this->getLocale();
- } else {
- $this->assertValidLocale($locale);
- }
- if (null === $domain) {
- $domain = 'messages';
- }
- if (!isset($this->catalogues[$locale])) {
- $this->loadCatalogue($locale);
- }
- $id = (string) $id;
- $catalogue = $this->catalogues[$locale];
- while (!$catalogue->defines($id, $domain)) {
- if ($cat = $catalogue->getFallbackCatalogue()) {
- $catalogue = $cat;
- $locale = $catalogue->getLocale();
- } else {
- break;
- }
- }
- return strtr($this->selector->choose($catalogue->get($id, $domain), (int) $number, $locale), $parameters);
- }
- /**
- * {@inheritdoc}
- */
- public function getCatalogue($locale = null)
- {
- if (null === $locale) {
- $locale = $this->getLocale();
- }
- if (!isset($this->catalogues[$locale])) {
- $this->loadCatalogue($locale);
- }
- return $this->catalogues[$locale];
- }
- /**
- * Gets the loaders.
- *
- * @return array LoaderInterface[]
- */
- protected function getLoaders()
- {
- return $this->loaders;
- }
- /**
- * Collects all messages for the given locale.
- *
- * @param string|null $locale Locale of translations, by default is current locale
- *
- * @return array[array] indexed by catalog
- */
- public function getMessages($locale = null)
- {
- if (null === $locale) {
- $locale = $this->getLocale();
- }
- if (!isset($this->catalogues[$locale])) {
- $this->loadCatalogue($locale);
- }
- $catalogues = array();
- $catalogues[] = $catalogue = $this->catalogues[$locale];
- while ($catalogue = $catalogue->getFallbackCatalogue()) {
- $catalogues[] = $catalogue;
- }
- $messages = array();
- for ($i = count($catalogues) - 1; $i >= 0; $i--) {
- $localeMessages = $catalogues[$i]->all();
- $messages = array_replace_recursive($messages, $localeMessages);
- }
- return $messages;
- }
- /*
- * @param string $locale
- */
- protected function loadCatalogue($locale)
- {
- if (null === $this->cacheDir) {
- $this->initializeCatalogue($locale);
- } else {
- $this->initializeCacheCatalogue($locale);
- }
- }
- /**
- * @param string $locale
- */
- protected function initializeCatalogue($locale)
- {
- $this->assertValidLocale($locale);
- try {
- $this->doLoadCatalogue($locale);
- } catch (NotFoundResourceException $e) {
- if (!$this->computeFallbackLocales($locale)) {
- throw $e;
- }
- }
- $this->loadFallbackCatalogues($locale);
- }
- /**
- * @param string $locale
- */
- private function initializeCacheCatalogue($locale)
- {
- if (isset($this->catalogues[$locale])) {
- return;
- }
- if (null === $this->cacheDir) {
- $this->initialize();
- return $this->loadCatalogue($locale);
- }
- $this->assertValidLocale($locale);
- $cache = new ConfigCache($this->cacheDir.'/catalogue.'.$locale.'.php', $this->debug);
- if (!$cache->isFresh()) {
- $this->initializeCatalogue($locale);
- $fallbackContent = '';
- $current = '';
- $replacementPattern = '/[^a-z0-9_]/i';
- foreach ($this->computeFallbackLocales($locale) as $fallback) {
- $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback));
- $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current));
- $fallbackContent .= sprintf(<<<EOF
- \$catalogue%s = new MessageCatalogue('%s', %s);
- \$catalogue%s->addFallbackCatalogue(\$catalogue%s);
- EOF
- ,
- $fallbackSuffix,
- $fallback,
- var_export($this->catalogues[$fallback]->all(), true),
- $currentSuffix,
- $fallbackSuffix
- );
- $current = $fallback;
- }
- $content = sprintf(<<<EOF
- <?php
- use Symfony\Component\Translation\MessageCatalogue;
- \$catalogue = new MessageCatalogue('%s', %s);
- %s
- return \$catalogue;
- EOF
- ,
- $locale,
- var_export($this->catalogues[$locale]->all(), true),
- $fallbackContent
- );
- $cache->write($content, $this->catalogues[$locale]->getResources());
- return;
- }
- $this->catalogues[$locale] = include $cache;
- }
- private function doLoadCatalogue($locale)
- {
- $this->catalogues[$locale] = new MessageCatalogue($locale);
- if (isset($this->resources[$locale])) {
- foreach ($this->resources[$locale] as $resource) {
- if (!isset($this->loaders[$resource[0]])) {
- throw new \RuntimeException(sprintf('The "%s" translation loader is not registered.', $resource[0]));
- }
- $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
- }
- }
- }
- private function loadFallbackCatalogues($locale)
- {
- $current = $this->catalogues[$locale];
- foreach ($this->computeFallbackLocales($locale) as $fallback) {
- if (!isset($this->catalogues[$fallback])) {
- $this->doLoadCatalogue($fallback);
- }
- $current->addFallbackCatalogue($this->catalogues[$fallback]);
- $current = $this->catalogues[$fallback];
- }
- }
- protected function computeFallbackLocales($locale)
- {
- $locales = array();
- foreach ($this->fallbackLocales as $fallback) {
- if ($fallback === $locale) {
- continue;
- }
- $locales[] = $fallback;
- }
- if (strrchr($locale, '_') !== false) {
- array_unshift($locales, substr($locale, 0, -strlen(strrchr($locale, '_'))));
- }
- return array_unique($locales);
- }
- /**
- * Asserts that the locale is valid, throws an Exception if not.
- *
- * @param string $locale Locale to tests
- *
- * @throws \InvalidArgumentException If the locale contains invalid characters
- */
- protected function assertValidLocale($locale)
- {
- if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
- throw new \InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
- }
- }
- }
|