diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 94b2f0b0..c0e89ab0 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + @@ -55,41 +55,18 @@ - - - - - - file]]> file]]> file]]> - - file, 4 * $num))]]> - file, 4 * $num))]]> - - - - - - - - - - - - - - @@ -142,13 +119,6 @@ - - - - - - - @@ -177,6 +147,17 @@ + + + + + + + + + + + @@ -308,10 +289,6 @@ - - - - @@ -330,92 +307,53 @@ - - - - - - - - - - - files[$textDomain][$currentLocale]]]> - files[$textDomain][$currentLocale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale][$message]]]> - messages[$textDomain][$locale][$textDomain . "\x04" . $message]]]> - - files[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - messages[$textDomain][$locale]]]> - patterns[$textDomain][]]]> - remote[$textDomain][]]]> - - - - - - - - - - - messages[$textDomain][$locale][$message]]]> messages[$textDomain][$locale][$textDomain . "\x04" . $message]]]> @@ -423,13 +361,6 @@ - - - - - - - @@ -445,20 +376,9 @@ - - - get('TranslatorPluginManager')]]> - - - - - - - - diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index c42d5d39..7ac87880 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -52,9 +52,11 @@ public function getDependencyConfig() Geography\CountryCodeListInterface::class => Geography\DefaultCountryCodeList::class, ], 'factories' => [ - Translator\TranslatorInterface::class => Translator\TranslatorServiceFactory::class, - Translator\LoaderPluginManager::class => Translator\LoaderPluginManagerFactory::class, - Geography\DefaultCountryCodeList::class => [Geography\DefaultCountryCodeList::class, 'create'], + Translator\TranslatorInterface::class => Translator\TranslatorServiceFactory::class, + Translator\LoaderPluginManager::class => Translator\LoaderPluginManagerFactory::class, + Translator\FormatterPluginManager::class => Translator\FormatterPluginManagerFactory::class, + Translator\TranslatorFormatterDecorator::class => Translator\TranslatorFormatterDecoratorFactory::class, + Geography\DefaultCountryCodeList::class => [Geography\DefaultCountryCodeList::class, 'create'], ], ]; } @@ -171,11 +173,11 @@ public function getViewHelperConfig() 'NumberFormat' => View\Helper\NumberFormat::class, 'plural' => View\Helper\Plural::class, 'Plural' => View\Helper\Plural::class, - 'translate' => View\Helper\Translate::class, - 'Translate' => View\Helper\Translate::class, - 'translateplural' => View\Helper\TranslatePlural::class, - 'translatePlural' => View\Helper\TranslatePlural::class, - 'TranslatePlural' => View\Helper\TranslatePlural::class, + 'translate' => View\Helper\TranslateWithParams::class, + 'Translate' => View\Helper\TranslateWithParams::class, + 'translateplural' => View\Helper\TranslatePluralWithParams::class, + 'translatePlural' => View\Helper\TranslatePluralWithParams::class, + 'TranslatePlural' => View\Helper\TranslatePluralWithParams::class, // Legacy Zend Framework aliases 'Zend\I18n\View\Helper\CurrencyFormat' => View\Helper\CurrencyFormat::class, @@ -186,13 +188,15 @@ public function getViewHelperConfig() 'Zend\I18n\View\Helper\TranslatePlural' => View\Helper\TranslatePlural::class, ], 'factories' => [ - View\Helper\CountryCodeDataList::class => View\Helper\Container\CountryCodeDataListFactory::class, - View\Helper\CurrencyFormat::class => InvokableFactory::class, - View\Helper\DateFormat::class => InvokableFactory::class, - View\Helper\NumberFormat::class => InvokableFactory::class, - View\Helper\Plural::class => InvokableFactory::class, - View\Helper\Translate::class => InvokableFactory::class, - View\Helper\TranslatePlural::class => InvokableFactory::class, + View\Helper\CountryCodeDataList::class => View\Helper\Container\CountryCodeDataListFactory::class, + View\Helper\CurrencyFormat::class => InvokableFactory::class, + View\Helper\DateFormat::class => InvokableFactory::class, + View\Helper\NumberFormat::class => InvokableFactory::class, + View\Helper\Plural::class => InvokableFactory::class, + View\Helper\Translate::class => InvokableFactory::class, + View\Helper\TranslatePlural::class => InvokableFactory::class, + View\Helper\TranslateWithParams::class => InvokableFactory::class, + View\Helper\TranslatePluralWithParams::class => InvokableFactory::class, ], ]; } diff --git a/src/Translator/Formatter/FormatterInterface.php b/src/Translator/Formatter/FormatterInterface.php new file mode 100644 index 00000000..2f5dabc9 --- /dev/null +++ b/src/Translator/Formatter/FormatterInterface.php @@ -0,0 +1,13 @@ + $params + */ + public function format(string $locale, string $message, iterable $params = []): string; +} diff --git a/src/Translator/Formatter/HandlebarFormatter.php b/src/Translator/Formatter/HandlebarFormatter.php new file mode 100644 index 00000000..e89aa8d5 --- /dev/null +++ b/src/Translator/Formatter/HandlebarFormatter.php @@ -0,0 +1,20 @@ + $value) { + $compiled = str_replace("{{{$key}}}", $value, $compiled); + } + + return $compiled; + } +} diff --git a/src/Translator/Formatter/IcuFormatter.php b/src/Translator/Formatter/IcuFormatter.php new file mode 100644 index 00000000..b92f1de3 --- /dev/null +++ b/src/Translator/Formatter/IcuFormatter.php @@ -0,0 +1,22 @@ + strlen((string) $b); + }); + + $compiled = $message; + foreach ($params as $key => $value) { + $key = (string) $key; + $compiled = str_replace([':' . $key, ':' . strtoupper($key), ':' . ucfirst($key)], [ + $value, + strtoupper($value), + ucfirst($value), + ], $compiled); + } + } catch (Throwable $e) { + throw new ParseException( + 'An error occurred while replacing placeholders in the message', + 0, + $e + ); + } + + return $compiled; + } +} diff --git a/src/Translator/FormatterPluginManager.php b/src/Translator/FormatterPluginManager.php new file mode 100644 index 00000000..e48c0eb3 --- /dev/null +++ b/src/Translator/FormatterPluginManager.php @@ -0,0 +1,70 @@ + + * @method Formatter\FormatterInterface get(string $name, ?array $options = null) + */ +class FormatterPluginManager extends AbstractPluginManager +{ + /** @inheritDoc */ + protected $aliases = [ + 'segment' => Formatter\SegmentFormatter::class, + 'colon' => Formatter\SegmentFormatter::class, + 'laravel' => Formatter\SegmentFormatter::class, + 'handlebar' => Formatter\HandlebarFormatter::class, + 'handlebars' => Formatter\HandlebarFormatter::class, + 'icu' => Formatter\IcuFormatter::class, + 'vsprintf' => Formatter\PrintfFormatter::class, + 'sprintf' => Formatter\PrintfFormatter::class, + 'printf' => Formatter\PrintfFormatter::class, + ]; + + /** @inheritDoc */ + protected $factories = [ + Formatter\SegmentFormatter::class => InvokableFactory::class, + Formatter\HandlebarFormatter::class => InvokableFactory::class, + Formatter\IcuFormatter::class => InvokableFactory::class, + Formatter\PrintfFormatter::class => InvokableFactory::class, + ]; + + /** + * Validate the plugin. + * + * Checks that the filter loaded is an instance of Formatter\FormatterInterface + * + * @throws Exception\RuntimeException If invalid. + * @psalm-assert RemoteLoaderInterface $instance + */ + public function validate(mixed $instance): void + { + if ($instance instanceof Formatter\FormatterInterface) { + // we're okay + return; + } + + throw new InvalidServiceException(sprintf( + 'Plugin of type %s is invalid; must implement %s', + is_object($instance) ? $instance::class : gettype($instance), + Formatter\FormatterInterface::class + )); + } +} diff --git a/src/Translator/FormatterPluginManagerFactory.php b/src/Translator/FormatterPluginManagerFactory.php new file mode 100644 index 00000000..d7e4c2b5 --- /dev/null +++ b/src/Translator/FormatterPluginManagerFactory.php @@ -0,0 +1,54 @@ +has('ServiceListener')) { + return $pluginManager; + } + + // If we do not have a config service, nothing more to do + if (! $container->has('config')) { + return $pluginManager; + } + + $config = $container->get('config'); + + // If we do not have translator_plugins configuration, nothing more to do + if (! isset($config['translator_formatter']) || ! is_array($config['translator_formatter'])) { + return $pluginManager; + } + + // Wire service configuration for translator_plugins + (new Config($config['translator_formatter']))->configureServiceManager($pluginManager); + + return $pluginManager; + } +} diff --git a/src/Translator/Loader/Gettext.php b/src/Translator/Loader/Gettext.php index eb8e8ea8..87d67ccb 100644 --- a/src/Translator/Loader/Gettext.php +++ b/src/Translator/Loader/Gettext.php @@ -182,15 +182,14 @@ protected function readInteger() /** * Read an integer from the current file. * - * @param int $num - * @return int + * @param int $num + * @return array */ protected function readIntegerList($num) { - if ($this->littleEndian) { - return unpack('V' . $num, fread($this->file, 4 * $num)); - } + /** @var array|false $integerList */ + $integerList = unpack(($this->littleEndian ? 'V' : 'N') . $num, fread($this->file, 4 * $num)); - return unpack('N' . $num, fread($this->file, 4 * $num)); + return $integerList === false ? [] : $integerList; } } diff --git a/src/Translator/LoaderPluginManager.php b/src/Translator/LoaderPluginManager.php index f7a15663..e7b3e479 100644 --- a/src/Translator/LoaderPluginManager.php +++ b/src/Translator/LoaderPluginManager.php @@ -98,21 +98,21 @@ class LoaderPluginManager extends AbstractPluginManager * Checks that the filter loaded is an instance of * Loader\FileLoaderInterface or Loader\RemoteLoaderInterface. * - * @param mixed $plugin + * @param mixed $instance * @return void * @throws Exception\RuntimeException If invalid. - * @psalm-assert InstanceType $plugin + * @psalm-assert InstanceType $instance */ - public function validate($plugin) + public function validate($instance) { - if ($plugin instanceof FileLoaderInterface || $plugin instanceof RemoteLoaderInterface) { + if ($instance instanceof FileLoaderInterface || $instance instanceof RemoteLoaderInterface) { // we're okay return; } throw new InvalidServiceException(sprintf( 'Plugin of type %s is invalid; must implement %s or %s', - is_object($plugin) ? $plugin::class : gettype($plugin), + is_object($instance) ? $instance::class : gettype($instance), FileLoaderInterface::class, RemoteLoaderInterface::class )); @@ -126,18 +126,19 @@ public function validate($plugin) * @deprecated Since 2.16.0 - This component is no longer compatible with Service Manager v2. * This method will be removed in version 3.0 * - * @param mixed $plugin + * @param mixed $instance + * @return void * @throws Exception\RuntimeException - * @psalm-assert InstanceType $plugin + * @psalm-assert InstanceType $instance */ - public function validatePlugin($plugin) + public function validatePlugin($instance) { try { - $this->validate($plugin); + $this->validate($instance); } catch (InvalidServiceException $e) { throw new Exception\RuntimeException(sprintf( 'Plugin of type %s is invalid; must implement %s or %s', - is_object($plugin) ? $plugin::class : gettype($plugin), + is_object($instance) ? $instance::class : gettype($instance), FileLoaderInterface::class, RemoteLoaderInterface::class )); diff --git a/src/Translator/Plural/Symbol.php b/src/Translator/Plural/Symbol.php index 7317a89d..2b913f40 100644 --- a/src/Translator/Plural/Symbol.php +++ b/src/Translator/Plural/Symbol.php @@ -127,7 +127,6 @@ public function getNullDenotation() throw new Exception\ParseException(sprintf('Syntax error: %s', $this->id)); } - /** @var callable $function */ $function = $this->nullDenotationGetter; return $function($this); } @@ -145,7 +144,6 @@ public function getLeftDenotation($left) throw new Exception\ParseException(sprintf('Unknown operator: %s', $this->id)); } - /** @var callable $function */ $function = $this->leftDenotationGetter; return $function($this, $left); } diff --git a/src/Translator/Translator.php b/src/Translator/Translator.php index 597d2395..f3f9545b 100644 --- a/src/Translator/Translator.php +++ b/src/Translator/Translator.php @@ -3,6 +3,7 @@ namespace Laminas\I18n\Translator; use Laminas\Cache; +use Laminas\Cache\Exception\ExceptionInterface; use Laminas\Cache\Storage\StorageInterface as CacheStorage; use Laminas\EventManager\Event; use Laminas\EventManager\EventManager; @@ -42,28 +43,28 @@ class Translator implements TranslatorInterface /** * Messages loaded by the translator. * - * @var array + * @var array|null>> */ protected $messages = []; /** * Files used for loading messages. * - * @var array + * @var array>> */ protected $files = []; /** * Patterns used for loading messages. * - * @var array + * @var array>> */ protected $patterns = []; /** * Remote locations for loading messages. * - * @var array + * @var array> */ protected $remote = []; @@ -345,10 +346,10 @@ public function getPluginManager() */ public function translate($message, $textDomain = 'default', $locale = null) { - $locale = $locale ?? $this->getLocale(); + $locale ??= $this->getLocale(); $translation = $this->getTranslatedMessage($message, $locale, $textDomain); - if ($translation !== null && $translation !== '') { + if (is_string($translation) && $translation !== '') { return $translation; } @@ -380,7 +381,7 @@ public function translatePlural( $textDomain = 'default', $locale = null ) { - $locale = $locale ?? $this->getLocale(); + $locale ??= $this->getLocale(); $translation = $this->getTranslatedMessage($singular, $locale, $textDomain); if (is_string($translation)) { @@ -388,13 +389,13 @@ public function translatePlural( } $index = $number === 1 ? 0 : 1; // en_EN Plural rule - if ($this->messages[$textDomain][$locale] instanceof TextDomain) { + if (isset($this->messages[$textDomain][$locale])) { $index = $this->messages[$textDomain][$locale] ->getPluralRule() ->evaluate($number); } - if (isset($translation[$index]) && $translation[$index] !== '' && $translation[$index] !== null) { + if (isset($translation[$index]) && $translation[$index] !== '') { return $translation[$index]; } @@ -458,7 +459,7 @@ protected function getTranslatedMessage( } if ($this->isEventManagerEnabled()) { - $until = static fn($r): bool => is_string($r); + $until = static fn(mixed $r): bool => is_string($r); $event = new Event(self::EVENT_MISSING_TRANSLATION, $this, [ 'message' => $message, @@ -492,12 +493,16 @@ public function addTranslationFile( $textDomain = 'default', $locale = null ) { - $locale = $locale ?? '*'; + $locale ??= '*'; if (! isset($this->files[$textDomain])) { $this->files[$textDomain] = []; } + if (! isset($this->files[$textDomain][$locale])) { + $this->files[$textDomain][$locale] = []; + } + $this->files[$textDomain][$locale][] = [ 'type' => $type, 'filename' => $filename, @@ -567,9 +572,10 @@ public function getCacheId($textDomain, $locale) /** * Clears the cache for a specific textDomain and locale. * - * @param string $textDomain - * @param string $locale + * @param string $textDomain + * @param string $locale * @return bool + * @throws ExceptionInterface */ public function clearCache($textDomain, $locale) { @@ -583,10 +589,11 @@ public function clearCache($textDomain, $locale) * Load messages for a given language and domain. * * @triggers loadMessages.no-messages-loaded - * @param string $textDomain - * @param string $locale - * @throws Exception\RuntimeException - * @return void + * @param string $textDomain + * @param string $locale + * @return void + * @throws ExceptionInterface + * @throws Exception\RuntimeException */ protected function loadMessages($textDomain, $locale) { @@ -594,10 +601,12 @@ protected function loadMessages($textDomain, $locale) $this->messages[$textDomain] = []; } + $cacheId = ''; if (null !== ($cache = $this->getCache())) { $cacheId = $this->getCacheId($textDomain, $locale); - - if (null !== ($result = $cache->getItem($cacheId))) { + /** @var TextDomain|null $result */ + $result = $cache->getItem($cacheId); + if ($result instanceof TextDomain) { $this->messages[$textDomain][$locale] = $result; return; @@ -655,12 +664,7 @@ protected function loadMessagesFromRemote($textDomain, $locale) throw new Exception\RuntimeException('Specified loader is not a remote loader'); } - if (isset($this->messages[$textDomain][$locale])) { - $this->messages[$textDomain][$locale]->merge($loader->load($locale, $textDomain)); - } else { - $this->messages[$textDomain][$locale] = $loader->load($locale, $textDomain); - } - + $this->storeTextDomain($textDomain, $locale, $loader->load($locale, $textDomain)); $messagesLoaded = true; } } @@ -691,11 +695,7 @@ protected function loadMessagesFromPatterns($textDomain, $locale) throw new Exception\RuntimeException('Specified loader is not a file loader'); } - if (isset($this->messages[$textDomain][$locale])) { - $this->messages[$textDomain][$locale]->merge($loader->load($locale, $filename)); - } else { - $this->messages[$textDomain][$locale] = $loader->load($locale, $filename); - } + $this->storeTextDomain($textDomain, $locale, $loader->load($locale, $filename)); $messagesLoaded = true; } @@ -729,12 +729,7 @@ protected function loadMessagesFromFiles($textDomain, $locale) throw new Exception\RuntimeException('Specified loader is not a file loader'); } - if (isset($this->messages[$textDomain][$locale])) { - $this->messages[$textDomain][$locale]->merge($loader->load($locale, $file['filename'])); - } else { - $this->messages[$textDomain][$locale] = $loader->load($locale, $file['filename']); - } - + $this->storeTextDomain($textDomain, $locale, $loader->load($locale, $file['filename'])); $messagesLoaded = true; } @@ -823,4 +818,17 @@ public function disableEventManager() $this->eventsEnabled = false; return $this; } + + protected function storeTextDomain(string $textDomain, string $locale, ?TextDomain $loaded): void + { + if (! $loaded instanceof TextDomain) { + return; + } + + if (isset($this->messages[$textDomain][$locale])) { + $this->messages[$textDomain][$locale]->merge($loaded); + } else { + $this->messages[$textDomain][$locale] = $loaded; + } + } } diff --git a/src/Translator/TranslatorFormatterDecorator.php b/src/Translator/TranslatorFormatterDecorator.php new file mode 100644 index 00000000..70358904 --- /dev/null +++ b/src/Translator/TranslatorFormatterDecorator.php @@ -0,0 +1,86 @@ + $params + */ + public function translate( + $message, + $textDomain = 'default', + $locale = null, + iterable $params = [] + ): string { + if ($locale === null) { + $locale = $this->getLocale(); + } + + return $this->formatMessage($this->translator->translate($message, $textDomain, $locale), $params, $locale); + } + + /** + * @param string $singular + * @param string $plural + * @param int $number + * @param string|null $textDomain + * @param string|null $locale + * @param iterable $params + */ + public function translatePlural( + $singular, + $plural, + $number, + $textDomain = 'default', + $locale = null, + iterable $params = [] + ): string { + if ($locale === null) { + $locale = $this->getLocale(); + } + + return $this->formatMessage( + $this->translatePlural($singular, $plural, $number, $textDomain, $locale), + $params, + $locale + ); + } + + /** + * @param iterable $params + */ + protected function formatMessage(string $message, iterable $params, string $locale): string + { + return $message !== '' ? $this->formatter->format($locale, $message, $params) : $message; + } + + protected function getLocale(): string + { + $locale = null; + if (method_exists($this->translator, 'getLocale')) { + /** @var string|null $translatorLocale */ + $translatorLocale = $this->translator->getLocale(); + if (is_string($translatorLocale)) { + $locale = $translatorLocale; + } + } + + return $locale ?? Locale::getDefault(); + } +} diff --git a/src/Translator/TranslatorFormatterDecoratorFactory.php b/src/Translator/TranslatorFormatterDecoratorFactory.php new file mode 100644 index 00000000..f906786d --- /dev/null +++ b/src/Translator/TranslatorFormatterDecoratorFactory.php @@ -0,0 +1,65 @@ + $config */ + $config = $container->get('config'); + $trConfig = $config['translator'] ?? []; + $translator = $container->get(TranslatorInterface::class); + + /** @var FormatterPluginManager $formatterPluginManager */ + $formatterPluginManager = $container->get(FormatterPluginManager::class); + /** @var string|FormatterInterface|mixed $formatterName */ + $formatterName = $trConfig['message_format'] ?? 'handlebars'; + if ($formatterName instanceof FormatterInterface) { + $formatter = $formatterName; + } elseif (is_string($formatterName)) { + if (! $formatterPluginManager->has($formatterName)) { + throw new ServiceNotCreatedException( + sprintf('Could not find a placeholder format with the name "%s"', $formatterName) + ); + } + + $formatter = $formatterPluginManager->get($formatterName); + } else { + throw new InvalidServiceException(sprintf( + '\'message_format\' of type %s is invalid; must be a string or object that implements %s', + is_object($formatterName) ? $formatterName::class : gettype($formatterName), + FormatterInterface::class + )); + } + + return new TranslatorFormatterDecorator($translator, $formatter); + } +} diff --git a/src/Translator/TranslatorServiceFactory.php b/src/Translator/TranslatorServiceFactory.php index d7b61f8a..0c45e223 100644 --- a/src/Translator/TranslatorServiceFactory.php +++ b/src/Translator/TranslatorServiceFactory.php @@ -2,9 +2,10 @@ namespace Laminas\I18n\Translator; -use Laminas\ServiceManager\FactoryInterface; -use Laminas\ServiceManager\ServiceLocatorInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; +use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; +use Psr\Container\NotFoundExceptionInterface; /** * Translator. @@ -15,33 +16,20 @@ class TranslatorServiceFactory implements FactoryInterface * Create a Translator instance. * * @param string $requestedName - * @param null|array $options - * @return Translator + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): Translator { // Configure the translator + /** @var array $config */ $config = $container->get('config'); $trConfig = $config['translator'] ?? []; $translator = Translator::factory($trConfig); if ($container->has('TranslatorPluginManager')) { $translator->setPluginManager($container->get('TranslatorPluginManager')); } - return $translator; - } - /** - * laminas-servicemanager v2 factory for creating Translator instance. - * - * @deprecated Since 2.16.0 - This component is no longer compatible with Service Manager v2. - * This method will be removed in version 3.0 - * - * Proxies to `__invoke()`. - * - * @return Translator - */ - public function createService(ServiceLocatorInterface $serviceLocator) - { - return $this($serviceLocator, Translator::class); + return $translator; } } diff --git a/src/View/Helper/TranslatePluralWithParams.php b/src/View/Helper/TranslatePluralWithParams.php new file mode 100644 index 00000000..500cc551 --- /dev/null +++ b/src/View/Helper/TranslatePluralWithParams.php @@ -0,0 +1,42 @@ + $params + * @noinspection PhpTooManyParametersInspection + */ + public function __invoke( + string $singular, + string $plural, + int $number, + ?string $textDomain = null, + ?string $locale = null, + iterable $params = [], + ): string { + $translator = $this->getTranslator(); + if (null === $translator) { + throw new Exception\RuntimeException('Translator has not been set'); + } + if (! $translator instanceof TranslatorFormatterDecorator) { + throw new Exception\RuntimeException( + 'No param support, the translator bust be decorated with TranslatorFormatterDecorator' + ); + } + if (null === $textDomain) { + $textDomain = $this->getTranslatorTextDomain(); + } + + return $translator->translatePlural($singular, $plural, $number, $textDomain, $locale, $params); + } +} diff --git a/src/View/Helper/TranslateWithParams.php b/src/View/Helper/TranslateWithParams.php new file mode 100644 index 00000000..3a409dff --- /dev/null +++ b/src/View/Helper/TranslateWithParams.php @@ -0,0 +1,39 @@ + $params + */ + public function __invoke( + string $message, + ?string $textDomain = null, + ?string $locale = null, + iterable $params = [] + ): string { + $translator = $this->getTranslator(); + if (null === $translator) { + throw new Exception\RuntimeException('Translator has not been set'); + } + if (! $translator instanceof TranslatorFormatterDecorator) { + throw new Exception\RuntimeException( + 'No param support, the translator bust be decorated with TranslatorFormatterDecorator' + ); + } + if (null === $textDomain) { + $textDomain = $this->getTranslatorTextDomain(); + } + + return $translator->translate($message, $textDomain, $locale, $params); + } +}