vendor/pimcore/pimcore/lib/Translation/Translator.php line 29

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Translation;
  15. use Pimcore\Cache;
  16. use Pimcore\Model\Translation;
  17. use Pimcore\Tool;
  18. use Symfony\Component\HttpKernel\Kernel;
  19. use Symfony\Component\Translation\Exception\InvalidArgumentException;
  20. use Symfony\Component\Translation\MessageCatalogue;
  21. use Symfony\Component\Translation\MessageCatalogueInterface;
  22. use Symfony\Component\Translation\TranslatorBagInterface;
  23. use Symfony\Contracts\Translation\LocaleAwareInterface;
  24. use Symfony\Contracts\Translation\TranslatorInterface;
  25. class Translator implements TranslatorInterfaceTranslatorBagInterfaceLocaleAwareInterface
  26. {
  27.     /**
  28.      * @var TranslatorInterface|TranslatorBagInterface
  29.      */
  30.     protected $translator;
  31.     /**
  32.      * @var array
  33.      */
  34.     protected $initializedCatalogues = [];
  35.     /**
  36.      * @var string
  37.      */
  38.     protected $adminPath '';
  39.     /**
  40.      * @var array
  41.      */
  42.     protected $adminTranslationMapping = [];
  43.     /**
  44.      * If true, the translator will just return the translation key instead of actually translating
  45.      * the message. Can be useful for debugging and to get an overview over used translation keys on
  46.      * a page.
  47.      *
  48.      * @var bool
  49.      */
  50.     protected $disableTranslations false;
  51.     /**
  52.      * @var Kernel
  53.      */
  54.     protected $kernel;
  55.     /**
  56.      * @param TranslatorInterface $translator
  57.      */
  58.     public function __construct(TranslatorInterface $translator)
  59.     {
  60.         if (!$translator instanceof TranslatorBagInterface) {
  61.             throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.'get_class($translator)));
  62.         }
  63.         $this->translator $translator;
  64.     }
  65.     /**
  66.      * {@inheritdoc}
  67.      */
  68.     public function trans(string $id, array $parameters = [], string $domain nullstring $locale null)
  69.     {
  70.         $id trim($id);
  71.         if ($this->disableTranslations) {
  72.             return $id;
  73.         }
  74.         if (null === $domain) {
  75.             $domain Translation::DOMAIN_DEFAULT;
  76.         }
  77.         $id = (string) $id;
  78.         if ($domain === Translation::DOMAIN_ADMIN && !empty($this->adminTranslationMapping)) {
  79.             if (null === $locale) {
  80.                 $locale $this->getLocale();
  81.             }
  82.             if (array_key_exists($locale$this->adminTranslationMapping)) {
  83.                 $locale $this->adminTranslationMapping[$locale];
  84.             }
  85.         }
  86.         $catalogue $this->getCatalogue($locale);
  87.         $locale $catalogue->getLocale();
  88.         $this->lazyInitialize($domain$locale);
  89.         $originalId $id;
  90.         $term $this->translator->trans($id$parameters$domain$locale);
  91.         // only check for empty translation on original ID - we don't want to create empty
  92.         // translations for normalized IDs when case insensitive
  93.         $term $this->checkForEmptyTranslation($originalId$term$parameters$domain$locale);
  94.         // check for an indexed array, that used the ZF1 vsprintf() notation for parameters
  95.         if (isset($parameters[0])) {
  96.             $term vsprintf($term$parameters);
  97.         }
  98.         $term $this->updateLinks($term);
  99.         return $term;
  100.     }
  101.     /**
  102.      * {@inheritdoc}
  103.      */
  104.     public function setLocale(string $locale)
  105.     {
  106.         if ($this->translator instanceof LocaleAwareInterface) {
  107.             $this->translator->setLocale($locale);
  108.         }
  109.     }
  110.     /**
  111.      * {@inheritdoc}
  112.      */
  113.     public function getLocale()
  114.     {
  115.         if ($this->translator instanceof LocaleAwareInterface) {
  116.             return $this->translator->getLocale();
  117.         }
  118.         return \Pimcore\Tool::getDefaultLanguage();
  119.     }
  120.     /**
  121.      * {@inheritdoc}
  122.      */
  123.     public function getCatalogue(string $locale null)// : MessageCatalogueInterface
  124.     {
  125.         return $this->translator->getCatalogue($locale);
  126.     }
  127.     /**
  128.      * @internal
  129.      *
  130.      * @param string $domain
  131.      * @param string $locale
  132.      */
  133.     public function lazyInitialize($domain$locale)
  134.     {
  135.         $cacheKey 'translation_data_' md5($domain '_' $locale);
  136.         if (isset($this->initializedCatalogues[$cacheKey])) {
  137.             return;
  138.         }
  139.         $this->initializedCatalogues[$cacheKey] = true;
  140.         if (Translation::isAValidDomain($domain)) {
  141.             $catalogue null;
  142.             if (!$catalogue Cache::load($cacheKey)) {
  143.                 $data = ['__pimcore_dummy' => 'only_a_dummy'];
  144.                 $dataIntl = ['__pimcore_dummy' => 'only_a_dummy'];
  145.                 if ($domain == 'admin') {
  146.                     $jsonFiles = [
  147.                         $locale '.json' => 'en.json',
  148.                         $locale '.extended.json' => 'en.extended.json',
  149.                     ];
  150.                     foreach ($jsonFiles as $sourceFile => $fallbackFile) {
  151.                         try {
  152.                             $jsonPath $this->getKernel()->locateResource($this->getAdminPath() . '/' $sourceFile);
  153.                         } catch (\Exception $e) {
  154.                             $jsonPath $this->getKernel()->locateResource($this->getAdminPath() . '/' $fallbackFile);
  155.                         }
  156.                         $jsonTranslations json_decode(file_get_contents($jsonPath), true);
  157.                         if (is_array($jsonTranslations)) {
  158.                             $defaultCatalog $this->getCatalogue($locale);
  159.                             foreach ($jsonTranslations as $translationKey => $translationValue) {
  160.                                 if (!$defaultCatalog->has($translationKey'admin')) {
  161.                                     $data[$translationKey] = $translationValue;
  162.                                 }
  163.                             }
  164.                         }
  165.                     }
  166.                 }
  167.                 $list = new Translation\Listing();
  168.                 $list->setDomain($domain);
  169.                 $debugAdminTranslations \Pimcore\Config::getSystemConfiguration('general')['debug_admin_translations'] ?? false;
  170.                 $list->setCondition('language = ?', [$locale]);
  171.                 $translations $list->loadRaw();
  172.                 foreach ($translations as $translation) {
  173.                     $translationTerm Tool\Text::removeLineBreaks($translation['text']);
  174.                     if (
  175.                         (!isset($data[$translation['key']]) && !$this->getCatalogue($locale)->has($translation['key'], $domain)) ||
  176.                         !empty($translationTerm)) {
  177.                         $translationKey $translation['key'];
  178.                         if (empty($translationTerm) && $debugAdminTranslations) {
  179.                             //wrap non-translated keys with "+", if debug admin translations is enabled
  180.                             $translationTerm '+' $translationKey'+';
  181.                         }
  182.                         if (empty($translation['type']) || $translation['type'] === 'simple') {
  183.                             $data[$translationKey] = $translationTerm;
  184.                         } else {
  185.                             $dataIntl[$translationKey] = $translationTerm;
  186.                         }
  187.                     }
  188.                 }
  189.                 // aliases support
  190.                 if ($domain == 'admin') {
  191.                     $aliasesPath $this->getKernel()->locateResource($this->getAdminPath() . '/aliases.json');
  192.                     $aliases json_decode(file_get_contents($aliasesPath), true);
  193.                     foreach ($aliases as $aliasTarget => $aliasSource) {
  194.                         if (isset($data[$aliasSource]) && (!isset($data[$aliasTarget]) || empty($data[$aliasTarget]))) {
  195.                             $data[$aliasTarget] = $data[$aliasSource];
  196.                         }
  197.                     }
  198.                 }
  199.                 $data = [
  200.                     $domain => $data,
  201.                     $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX => $dataIntl,
  202.                 ];
  203.                 $catalogue = new MessageCatalogue($locale$data);
  204.                 Cache::save($catalogue$cacheKey, ['translator''translator_website''translate'], null999);
  205.             }
  206.             if ($catalogue) {
  207.                 $this->getCatalogue($locale)->addCatalogue($catalogue);
  208.             }
  209.         }
  210.     }
  211.     /**
  212.      * @param string $id
  213.      * @param string $translated
  214.      * @param array $parameters
  215.      * @param string $domain
  216.      * @param string $locale
  217.      *
  218.      * @return string
  219.      *
  220.      * @throws \Exception
  221.      */
  222.     private function checkForEmptyTranslation($id$translated$parameters$domain$locale)
  223.     {
  224.         if (empty($id)) {
  225.             return $translated;
  226.         }
  227.         $normalizedId $id;
  228.         //translate only plural form(seperated by pipe "|") with count param
  229.         if (isset($parameters['%count%']) && $translated && strpos($normalizedId'|') !== false) {
  230.             $normalizedId $id $translated;
  231.             $translated $this->translator->trans($normalizedId$parameters$domain$locale);
  232.         }
  233.         $lookForFallback = empty($translated);
  234.         if ($normalizedId != $translated && $translated) {
  235.             return $translated;
  236.         } elseif ($normalizedId == $translated) {
  237.             if ($this->getCatalogue($locale)->has($normalizedId$domain)) {
  238.                 $translated $this->getCatalogue($locale)->get($normalizedId$domain);
  239.                 if ($normalizedId != $translated && $translated) {
  240.                     return $translated;
  241.                 }
  242.             } elseif (Translation::isAValidDomain($domain)) {
  243.                 if (strlen($id) > 190) {
  244.                     throw new \Exception("Message ID's longer than 190 characters are invalid!");
  245.                 }
  246.                 // no translation found create key
  247.                 if (Translation::IsAValidLanguage($domain$locale)) {
  248.                     $t Translation::getByKey($id$domain);
  249.                     if ($t) {
  250.                         if (!$t->hasTranslation($locale)) {
  251.                             $t->addTranslation($locale'');
  252.                         } else {
  253.                             // return the original not lowercased ID
  254.                             return $id;
  255.                         }
  256.                     } else {
  257.                         $t = new Translation();
  258.                         $t->setDomain($domain);
  259.                         $t->setKey($id);
  260.                         // add all available languages
  261.                         $availableLanguages = (array)Translation::getValidLanguages();
  262.                         foreach ($availableLanguages as $language) {
  263.                             $t->addTranslation($language'');
  264.                         }
  265.                     }
  266.                     $t->save();
  267.                 }
  268.                 // put it into the catalogue, otherwise when there are more calls to the same key during one process
  269.                 // the key would be inserted/updated several times, what would be redundant
  270.                 $this->getCatalogue($locale)->set($normalizedId$id$domain);
  271.             }
  272.         }
  273.         // now check for custom fallback locales, only for shared translations
  274.         if ($lookForFallback && $domain == 'messages') {
  275.             foreach (Tool::getFallbackLanguagesFor($locale) as $fallbackLanguage) {
  276.                 $this->lazyInitialize($domain$fallbackLanguage);
  277.                 $catalogue $this->getCatalogue($fallbackLanguage);
  278.                 $fallbackValue '';
  279.                 if ($catalogue->has($normalizedId$domain)) {
  280.                     $fallbackValue $catalogue->get($normalizedId$domain);
  281.                 }
  282.                 if ($fallbackValue && $normalizedId != $fallbackValue) {
  283.                     // update fallback value in original catalogue otherwise multiple calls to the same id will not work
  284.                     $this->getCatalogue($locale)->set($normalizedId$fallbackValue$domain);
  285.                     return strtr($fallbackValue$parameters);
  286.                 }
  287.             }
  288.         }
  289.         return !empty($translated) ? $translated $id;
  290.     }
  291.     /**
  292.      * @internal
  293.      *
  294.      * @return string
  295.      */
  296.     public function getAdminPath()
  297.     {
  298.         return $this->adminPath;
  299.     }
  300.     /**
  301.      * @internal
  302.      *
  303.      * @param string $adminPath
  304.      */
  305.     public function setAdminPath($adminPath)
  306.     {
  307.         $this->adminPath $adminPath;
  308.     }
  309.     /**
  310.      * @internal
  311.      *
  312.      * @return array
  313.      */
  314.     public function getAdminTranslationMapping(): array
  315.     {
  316.         return $this->adminTranslationMapping;
  317.     }
  318.     /**
  319.      * @internal
  320.      *
  321.      * @param array $adminTranslationMapping
  322.      */
  323.     public function setAdminTranslationMapping(array $adminTranslationMapping): void
  324.     {
  325.         $this->adminTranslationMapping $adminTranslationMapping;
  326.     }
  327.     /**
  328.      * @internal
  329.      *
  330.      * @return Kernel
  331.      */
  332.     public function getKernel()
  333.     {
  334.         return $this->kernel;
  335.     }
  336.     /**
  337.      * @internal
  338.      *
  339.      * @param Kernel $kernel
  340.      */
  341.     public function setKernel($kernel)
  342.     {
  343.         $this->kernel $kernel;
  344.     }
  345.     public function getDisableTranslations(): bool
  346.     {
  347.         return $this->disableTranslations;
  348.     }
  349.     public function setDisableTranslations(bool $disableTranslations)
  350.     {
  351.         $this->disableTranslations $disableTranslations;
  352.     }
  353.     /**
  354.      * @param string $text
  355.      *
  356.      * @return string
  357.      */
  358.     private function updateLinks(string $text): string
  359.     {
  360.         if (strpos($text'pimcore_id')) {
  361.             $text Tool\Text::wysiwygText($text);
  362.         }
  363.         return $text;
  364.     }
  365.     /**
  366.      * Passes through all unknown calls onto the translator object.
  367.      */
  368.     public function __call($method$args)
  369.     {
  370.         return call_user_func_array([$this->translator$method], $args);
  371.     }
  372. }