vendor/ezsystems/ezplatform-kernel/eZ/Publish/Core/Helper/TranslationHelper.php line 24

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. namespace eZ\Publish\Core\Helper;
  7. use eZ\Publish\API\Repository\ContentService;
  8. use eZ\Publish\API\Repository\Values\Content\Content;
  9. use eZ\Publish\API\Repository\Values\Content\ContentInfo;
  10. use eZ\Publish\API\Repository\Values\Content\Field;
  11. use eZ\Publish\API\Repository\Values\Content\VersionInfo;
  12. use eZ\Publish\API\Repository\Values\ContentType\ContentType;
  13. use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
  14. use eZ\Publish\API\Repository\Values\ValueObject;
  15. use eZ\Publish\Core\MVC\ConfigResolverInterface;
  16. use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
  17. use Psr\Log\LoggerInterface;
  18. /**
  19.  * Helper class for translation.
  20.  */
  21. class TranslationHelper
  22. {
  23.     /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */
  24.     protected $configResolver;
  25.     /** @var \eZ\Publish\API\Repository\ContentService */
  26.     protected $contentService;
  27.     /** @var array */
  28.     private $siteAccessesByLanguage;
  29.     /** @var \Psr\Log\LoggerInterface */
  30.     private $logger;
  31.     public function __construct(ConfigResolverInterface $configResolverContentService $contentService, array $siteAccessesByLanguageLoggerInterface $logger null)
  32.     {
  33.         $this->configResolver $configResolver;
  34.         $this->contentService $contentService;
  35.         $this->siteAccessesByLanguage $siteAccessesByLanguage;
  36.         $this->logger $logger;
  37.     }
  38.     /**
  39.      * Returns content name, translated.
  40.      * By default this method uses prioritized languages, unless $forcedLanguage is provided.
  41.      *
  42.      * @param \eZ\Publish\API\Repository\Values\Content\Content $content
  43.      * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
  44.      *
  45.      * @return string
  46.      */
  47.     public function getTranslatedContentName(Content $content$forcedLanguage null)
  48.     {
  49.         return $this->getTranslatedContentNameByVersionInfo(
  50.             $content->getVersionInfo(),
  51.             $forcedLanguage
  52.         );
  53.     }
  54.     /**
  55.      * Returns content name, translated, from a VersionInfo object.
  56.      * By default this method uses prioritized languages, unless $forcedLanguage is provided.
  57.      *
  58.      * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
  59.      * @param string $forcedLanguage
  60.      *
  61.      * @return string
  62.      */
  63.     private function getTranslatedContentNameByVersionInfo(VersionInfo $versionInfo$forcedLanguage null)
  64.     {
  65.         foreach ($this->getLanguages($forcedLanguage) as $lang) {
  66.             $translatedName $versionInfo->getName($lang);
  67.             if ($translatedName !== null) {
  68.                 return $translatedName;
  69.             }
  70.         }
  71.         return '';
  72.     }
  73.     /**
  74.      * Returns content name, translated, from a ContentInfo object.
  75.      * By default this method uses prioritized languages, unless $forcedLanguage is provided.
  76.      *
  77.      * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
  78.      * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
  79.      *
  80.      * @todo Remove ContentService usage when translated names are available in ContentInfo (see https://jira.ez.no/browse/EZP-21755)
  81.      *
  82.      * @return string
  83.      */
  84.     public function getTranslatedContentNameByContentInfo(ContentInfo $contentInfo$forcedLanguage null)
  85.     {
  86.         if ($contentInfo->mainLocationId === 1) {
  87.             return $contentInfo->name;
  88.         }
  89.         if (isset($forcedLanguage) && $forcedLanguage === $contentInfo->mainLanguageCode) {
  90.             return $contentInfo->name;
  91.         }
  92.         return $this->getTranslatedContentNameByVersionInfo(
  93.             $this->contentService->loadVersionInfo($contentInfo),
  94.             $forcedLanguage
  95.         );
  96.     }
  97.     /**
  98.      * Returns Field object in the appropriate language for a given content.
  99.      * By default, this method will return the field in current language if translation is present. If not, main language will be used.
  100.      * If $forcedLanguage is provided, will return the field in this language, if translation is present.
  101.      *
  102.      * @param \eZ\Publish\API\Repository\Values\Content\Content $content
  103.      * @param string $fieldDefIdentifier Field definition identifier.
  104.      * @param string $forcedLanguage Locale we want the field translation in (e.g. "fre-FR"). Null by default (takes current locale)
  105.      *
  106.      * @return \eZ\Publish\API\Repository\Values\Content\Field|null
  107.      */
  108.     public function getTranslatedField(Content $content$fieldDefIdentifier$forcedLanguage null)
  109.     {
  110.         // Loop over prioritized languages to get the appropriate translated field.
  111.         foreach ($this->getLanguages($forcedLanguage) as $lang) {
  112.             $field $content->getField($fieldDefIdentifier$lang);
  113.             if ($field instanceof Field) {
  114.                 return $field;
  115.             }
  116.         }
  117.     }
  118.     /**
  119.      * Returns Field definition name in the appropriate language for a given content.
  120.      *
  121.      * By default, this method will return the field definition name in current language if translation is present. If not, main language will be used.
  122.      * If $forcedLanguage is provided, will return the field definition name in this language, if translation is present.
  123.      *
  124.      * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
  125.      * @param string $fieldDefIdentifier Field Definition identifier
  126.      * @param string $property Specifies if 'name' or 'description' should be used
  127.      * @param string $forcedLanguage Locale we want the field definition name translated in in (e.g. "fre-FR"). Null by default (takes current locale)
  128.      *
  129.      * @throws InvalidArgumentException
  130.      *
  131.      * @return string|null
  132.      */
  133.     public function getTranslatedFieldDefinitionProperty(
  134.         ContentType $contentType,
  135.         $fieldDefIdentifier,
  136.         $property 'name',
  137.         $forcedLanguage null
  138.     ) {
  139.         $fieldDefinition $contentType->getFieldDefinition($fieldDefIdentifier);
  140.         if (!$fieldDefinition instanceof FieldDefinition) {
  141.             throw new InvalidArgumentException(
  142.                 '$fieldDefIdentifier',
  143.                 "Field '{$fieldDefIdentifier}' not found in {$contentType->identifier}"
  144.             );
  145.         }
  146.         $method 'get' $property;
  147.         if (!method_exists($fieldDefinition$method)) {
  148.             throw new InvalidArgumentException('$property'"Method {$method}() not found in the FieldDefinition");
  149.         }
  150.         // Loop over prioritized languages to get the appropriate translated field definition name
  151.         // Should ideally have used array_unique, but in that case the loop should ideally never reach last item
  152.         foreach ($this->getLanguages($forcedLanguage$contentType->mainLanguageCode) as $lang) {
  153.             if ($name $fieldDefinition->$method($lang)) {
  154.                 return $name;
  155.             }
  156.         }
  157.     }
  158.     /**
  159.      * Gets translated property generic helper.
  160.      *
  161.      * For generic use, expects array property as-is on value object, typically $object->$property[$language]
  162.      *
  163.      * Languages will consist of either forced language or current languages list, in addition helper will check if for
  164.      * mainLanguage property and append that to languages if alwaysAvailable property is true or non-existing.
  165.      *
  166.      * @param \eZ\Publish\API\Repository\Values\ValueObject $object  Can be any kid of Value object which directly holds the translated property
  167.      * @param string $property Property name, example 'names', 'descriptions'
  168.      * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
  169.      *
  170.      * @throws InvalidArgumentException
  171.      *
  172.      * @return string|null
  173.      */
  174.     public function getTranslatedByProperty(ValueObject $object$property$forcedLanguage null)
  175.     {
  176.         if (!isset($object->$property)) {
  177.             throw new InvalidArgumentException('$property'"Property '{$property}' not found in " get_class($object));
  178.         }
  179.         // Always force main language as fallback, if defined and if either alwaysAvailable is true or not defined
  180.         // if language is already is set on array we still do this as ideally the loop will never
  181.         if (isset($object->mainLanguageCode) && (!isset($object->alwaysAvailable) || $object->alwaysAvailable)) {
  182.             $languages $this->getLanguages($forcedLanguage$object->mainLanguageCode);
  183.         } else {
  184.             $languages $this->getLanguages($forcedLanguage);
  185.         }
  186.         // Get property value first in case it is magic (__isset and __get) property
  187.         $propertyValue $object->$property;
  188.         foreach ($languages as $lang) {
  189.             if (isset($propertyValue[$lang])) {
  190.                 return $propertyValue[$lang];
  191.             }
  192.         }
  193.     }
  194.     /**
  195.      * Gets translated method generic helper.
  196.      *
  197.      * For generic use, expects method exposing translated property as-is on value object, typically $object->$method($language)
  198.      *
  199.      * Languages will consist of either forced language or current languages list, in addition helper will append null
  200.      * to list of languages so method may fallback to main/initial language if supported by domain.
  201.      *
  202.      * @param \eZ\Publish\API\Repository\Values\ValueObject $object  Can be any kind of Value object which directly holds the methods that provides translated value.
  203.      * @param string $method Method name, example 'getName', 'description'
  204.      * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale)
  205.      *
  206.      * @throws InvalidArgumentException
  207.      *
  208.      * @return string|null
  209.      */
  210.     public function getTranslatedByMethod(ValueObject $object$method$forcedLanguage null)
  211.     {
  212.         if (!method_exists($object$method)) {
  213.             throw new InvalidArgumentException('$method'"Method '{$method}' not found in " get_class($object));
  214.         }
  215.         foreach ($this->getLanguages($forcedLanguage) as $lang) {
  216.             if ($value $object->$method($lang)) {
  217.                 return $value;
  218.             }
  219.         }
  220.     }
  221.     /**
  222.      * Returns a SiteAccess name for translation in $languageCode.
  223.      * This is used for LanguageSwitcher feature (generate links for current content in a different language if available).
  224.      * Will use configured translation_siteaccesses if any. Otherwise will use related siteaccesses (e.g. same repository, same rootLocationId).
  225.      *
  226.      * Will return null if no translation SiteAccess can be found.
  227.      *
  228.      * @param string $languageCode Translation language code.
  229.      *
  230.      * @return string|null
  231.      */
  232.     public function getTranslationSiteAccess($languageCode)
  233.     {
  234.         $translationSiteAccesses $this->configResolver->getParameter('translation_siteaccesses');
  235.         $relatedSiteAccesses $this->configResolver->getParameter('related_siteaccesses');
  236.         if (!isset($this->siteAccessesByLanguage[$languageCode])) {
  237.             if ($this->logger) {
  238.                 $this->logger->error("Couldn't find any SiteAccess with '$languageCode' as main language.");
  239.             }
  240.             return null;
  241.         }
  242.         $relatedSiteAccesses $translationSiteAccesses ?: $relatedSiteAccesses;
  243.         $translationSiteAccesses array_intersect($this->siteAccessesByLanguage[$languageCode], $relatedSiteAccesses);
  244.         return array_shift($translationSiteAccesses);
  245.     }
  246.     /**
  247.      * Returns the list of all available languages, including the ones configured in related SiteAccesses.
  248.      *
  249.      * @return array
  250.      */
  251.     public function getAvailableLanguages()
  252.     {
  253.         $translationSiteAccesses $this->configResolver->getParameter('translation_siteaccesses');
  254.         $relatedSiteAccesses $translationSiteAccesses ?: $this->configResolver->getParameter('related_siteaccesses');
  255.         $availableLanguages = [];
  256.         $currentLanguages $this->configResolver->getParameter('languages');
  257.         $availableLanguages[] = array_shift($currentLanguages);
  258.         foreach ($relatedSiteAccesses as $sa) {
  259.             $languages $this->configResolver->getParameter('languages'null$sa);
  260.             $availableLanguages[] = array_shift($languages);
  261.         }
  262.         sort($availableLanguages);
  263.         return array_unique($availableLanguages);
  264.     }
  265.     /**
  266.      * @param string|null $forcedLanguage
  267.      * @param string|null $fallbackLanguage
  268.      *
  269.      * @return array|mixed
  270.      */
  271.     private function getLanguages($forcedLanguage null$fallbackLanguage null)
  272.     {
  273.         if ($forcedLanguage !== null) {
  274.             $languages = [$forcedLanguage];
  275.         } else {
  276.             $languages $this->configResolver->getParameter('languages');
  277.         }
  278.         // Always add $fallbackLanguage, even if null, as last entry so that we can fallback to
  279.         // main/initial language if domain supports it.
  280.         $languages[] = $fallbackLanguage;
  281.         return $languages;
  282.     }
  283. }