diff --git a/composer.json b/composer.json index fda87cb..9f6b78b 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,8 @@ "symfony/event-dispatcher": "^5.4", "symfony/event-dispatcher-contracts": "^1.1 || ^2.0", "symfony/http-kernel": "^5.4", - "symfony/translation": "^5.4" + "symfony/translation": "^5.4", + "symfony/translation-contracts": "^2.3" }, "require-dev": { "contao-community-alliance/dependency-container": "^2.1", @@ -66,6 +67,7 @@ }, "extra": { "branch-alias": { + "dev-release/2.4.x": "2.4.0-dev", "dev-master": "2.3.x-dev", "dev-support/2.0.x": "2.0.x-dev", "dev-support/2.1.x": "2.1.x-dev", diff --git a/src/AbstractTranslator.php b/src/AbstractTranslator.php index 092960c..19f4438 100644 --- a/src/AbstractTranslator.php +++ b/src/AbstractTranslator.php @@ -43,7 +43,7 @@ abstract class AbstractTranslator implements TranslatorInterface public function translate($string, $domain = null, array $parameters = [], $locale = null) { $newString = $this->getValue($string, $domain, $locale); - assert(is_string($newString)); + assert(is_string($newString), 'Expected ' . var_export($newString, true) . ' to be a string.'); if ($newString == $string) { return $string; diff --git a/src/Contao/ContaoTranslatorFactory.php b/src/Contao/ContaoTranslatorFactory.php index a8f6c5b..8b0d328 100644 --- a/src/Contao/ContaoTranslatorFactory.php +++ b/src/Contao/ContaoTranslatorFactory.php @@ -20,45 +20,43 @@ namespace ContaoCommunityAlliance\Translator\Contao; +use ContaoCommunityAlliance\Translator\SymfonyTranslatorBridge; use ContaoCommunityAlliance\Translator\TranslatorChain; use ContaoCommunityAlliance\Translator\TranslatorInitializer; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Translation\TranslatorInterface as SymfonyTranslator; /** * This class create the contao translator service. */ class ContaoTranslatorFactory { - /** - * The event dispatcher. - * - * @var EventDispatcherInterface - */ - protected $dispatcher; - /** * Create a new instance. * * @param EventDispatcherInterface $dispatcher The event dispatcher to use. */ - public function __construct(EventDispatcherInterface $dispatcher) - { - $this->dispatcher = $dispatcher; + public function __construct( + protected EventDispatcherInterface $dispatcher, + private SymfonyTranslator $translator + ) { } /** * Create the translator service. * - * @return \ContaoCommunityAlliance\Translator\TranslatorInterface + * @return TranslatorInterface */ public function createService() { $translator = new TranslatorChain(); $translator->add(new LangArrayTranslator($this->dispatcher)); - $initializer = new TranslatorInitializer($this->dispatcher); + $initializer->initialize($translator); + $translator->add(new SymfonyTranslatorBridge($this->translator)); - return $initializer->initialize($translator); + return $translator; } } diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 8a3771b..3f8c07a 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -2,6 +2,7 @@ services: ContaoCommunityAlliance\Translator\Contao\ContaoTranslatorFactory: arguments: - '@event_dispatcher' + - '@translator' cca.translator.contao_translator: class: ContaoCommunityAlliance\Translator\TranslatorInterface diff --git a/src/SymfonyTranslatorBridge.php b/src/SymfonyTranslatorBridge.php new file mode 100644 index 0000000..9cb1385 --- /dev/null +++ b/src/SymfonyTranslatorBridge.php @@ -0,0 +1,125 @@ + + * @copyright 2014-2023 Contao Community Alliance. + * @license https://github.com/contao-community-alliance/translator/blob/master/LICENSE LGPL-3.0 + * @filesource + */ + +declare(strict_types=1); + +namespace ContaoCommunityAlliance\Translator; + +use Symfony\Contracts\Translation\TranslatorInterface as SymfonyTranslator; + +final class SymfonyTranslatorBridge implements TranslatorInterface +{ + private SymfonyTranslator $translator; + + public function __construct(SymfonyTranslator $translator) + { + $this->translator = $translator; + } + + public function translate($string, $domain = null, array $parameters = [], $locale = null): string + { + if (($string !== $transValue = $this->try($string, $parameters, $domain, $locale))) { + return $transValue; + } + if (null === $locale) { + return $string; + } + + return $this->try($string, $parameters, $domain, null); + } + + public function translatePluralized( + $string, + $number, + $domain = null, + array $parameters = [], + $locale = null + ): string { + if (($string !== $transValue = $this->tryPluralized($string, $number, $parameters, $domain, $locale))) { + return $transValue; + } + if (null === $locale) { + return $string; + } + + return $this->tryPluralized($string, $number, $parameters, $domain, null); + } + + /** + * This tries to fetch the translation string, if there is no match, return the original string. + * + * If there is a match, it will check the parameters and try to interpolate, as long as the parameters are a list. + * If the parameters are an associative array, they will get passed to the symfony translator instead. + * + * @param array|list $parameters + */ + private function try(string $string, array $parameters, ?string $domain, ?string $locale): string + { + if (!empty($domain)) { + // FIXME: Do we really need to always add contao_ prefix? + $domain = 'contao_' . $domain; + } + + // Check if we have an associative array. + $transParams = array_is_list($parameters) ? [] : $parameters; + + if (($string === $transValue = $this->translator->trans($string, $transParams, $domain, $locale))) { + return $string; + } + if ([] === $transParams && [] !== $parameters) { + /** @var list $parameters */ + return vsprintf($transValue, $parameters); + } + return $transValue; + } + + /** + * This tries to fetch the translation string, if there is no match, return the original string. + * + * If there is a match, it will check the parameters and try to interpolate, as long as the parameters are a list. + * If the parameters are an associative array, they will get passed to the symfony translator instead. + * + * @param array|list $parameters + */ + private function tryPluralized( + string $string, + int $count, + array $parameters, + ?string $domain, + ?string $locale + ): string { + if (!empty($domain)) { + // FIXME: Do we really need to always add contao_ prefix? + $domain = 'contao_' . $domain; + } + + // Check if we have an associative array. + $transParams = array_is_list($parameters) ? [] : $parameters; + $transParams['%count%'] = $count; + + if (($string === $transValue = $this->translator->trans($string, $transParams, $domain, $locale))) { + return $string; + } + if (['%count%' => $count] === $transParams && [] !== $parameters) { + /** @var list $parameters */ + return vsprintf($transValue, $parameters); + } + return $transValue; + } +}