Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of TranslatorInterface as Translator #224

Merged
merged 10 commits into from
Jun 12, 2024
10 changes: 5 additions & 5 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,11 @@
<code><![CDATA[gettype($type)]]></code>
</RedundantConditionGivenDocblockType>
</file>
<file src="src/Translator/DummyTranslator.php">
<RedundantCastGivenDocblockType>
<code><![CDATA[(int) $number]]></code>
</RedundantCastGivenDocblockType>
</file>
<file src="src/Translator/TranslatorAwareInterface.php">
<PossiblyUnusedReturnValue>
<code><![CDATA[TranslatorAwareInterface]]></code>
Expand Down Expand Up @@ -2004,10 +2009,6 @@
<code><![CDATA[$configOrContainerInstance]]></code>
<code><![CDATA[$container->get('MvcTranslator')]]></code>
</MixedArgument>
<MixedMethodCall>
<code><![CDATA[get]]></code>
<code><![CDATA[has]]></code>
</MixedMethodCall>
<PossiblyUnusedMethod>
<code><![CDATA[validatePlugin]]></code>
</PossiblyUnusedMethod>
Expand All @@ -2018,7 +2019,6 @@
<code><![CDATA[$container === $this && method_exists($container, 'getServiceLocator')]]></code>
</RedundantCondition>
<RedundantConditionGivenDocblockType>
<code><![CDATA[$container]]></code>
<code><![CDATA[$container->getServiceLocator()]]></code>
</RedundantConditionGivenDocblockType>
</file>
Expand Down
4 changes: 3 additions & 1 deletion src/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ public function getDependencyConfig()
{
return [
'aliases' => [
'ValidatorManager' => ValidatorPluginManager::class,
Translator\TranslatorInterface::class => Translator\Translator::class,
'ValidatorManager' => ValidatorPluginManager::class,

// Legacy Zend Framework aliases
'Zend\Validator\ValidatorPluginManager' => ValidatorPluginManager::class,
],
'factories' => [
Translator\Translator::class => Translator\TranslatorFactory::class,
ValidatorPluginManager::class => ValidatorPluginManagerFactory::class,
],
];
Expand Down
22 changes: 22 additions & 0 deletions src/Translator/DummyTranslator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Laminas\Validator\Translator;

use Laminas\I18n\Translator\TranslatorInterface as I18nTranslatorInterface;

final class DummyTranslator implements I18nTranslatorInterface
{
/** @inheritDoc */
public function translate($message, $textDomain = 'default', $locale = null)
{
return $message;
}

/** @inheritDoc */
public function translatePlural($singular, $plural, $number, $textDomain = 'default', $locale = null)
{
return (int) $number === 1 ? $singular : $plural;
}
}
45 changes: 45 additions & 0 deletions src/Translator/Translator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Laminas\Validator\Translator;

use Laminas\I18n\Translator\TranslatorInterface as I18nTranslatorInterface;
use Laminas\Validator\Translator\TranslatorInterface as ValidatorTranslatorInterface;

final class Translator implements
I18nTranslatorInterface,
ValidatorTranslatorInterface
{
public function __construct(protected I18nTranslatorInterface $translator)
{
}

/**
* Translate a message using the given text domain and locale
*
* @param string $message
* @param string $textDomain
* @param string $locale
* @return string
*/
public function translate($message, $textDomain = 'default', $locale = null)
{
return $this->translator->translate($message, $textDomain, $locale);
}

/**
* Provide a pluralized translation of the given string using the given text domain and locale
*
* @param string $singular
* @param string $plural
* @param int $number
* @param string $textDomain
* @param string $locale
* @return string
*/
public function translatePlural($singular, $plural, $number, $textDomain = 'default', $locale = null)
{
return $this->translator->translatePlural($singular, $plural, $number, $textDomain, $locale);
}
}
130 changes: 130 additions & 0 deletions src/Translator/TranslatorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php

declare(strict_types=1);

namespace Laminas\Validator\Translator;

use Laminas\I18n\Translator\LoaderPluginManager;
use Laminas\I18n\Translator\Translator as I18nTranslator;
use Laminas\I18n\Translator\TranslatorInterface;
use Laminas\ServiceManager\Factory\FactoryInterface;
use Laminas\ServiceManager\ServiceManager;
use Psr\Container\ContainerInterface;
use Traversable;

use function array_key_exists;
use function assert;
use function extension_loaded;
use function is_array;

/**
* Overrides the translator factory from the i18n component in order to
* replace it with the bridge class from this namespace.
*/
class TranslatorFactory implements FactoryInterface
{
/**
* @param string $requestedName
* @return Translator
*/
public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null)
{
// Assume that if a user has registered a service for the
// TranslatorInterface, it must be valid
if ($container->has(TranslatorInterface::class)) {
return new Translator($container->get(TranslatorInterface::class));
}

return $this->marshalTranslator($container);
}

/**
* Marshal an Translator.
*
* If configuration exists, will pass it to the I18nTranslator::factory,
* decorating the returned instance in an MvcTranslator.
*
* Otherwise:
*
* - returns an Translator decorating a DummyTranslator instance if
* ext/intl is not loaded.
* - returns an Translator decorating an empty I18nTranslator instance.
*
* @return Translator
*/
private function marshalTranslator(ContainerInterface $container)
{
// Load a translator from configuration, if possible
$translator = $this->marshalTranslatorFromConfig($container);
if ($translator) {
return $translator;
}

// If ext/intl is not loaded, return a dummy translator
if (! extension_loaded('intl')) {
return new Translator(new DummyTranslator());
}

return new Translator(new I18nTranslator());
}

/**
* Attempt to marshal a translator from configuration.
*
* Returns:
* - an Translator seeded with a DummyTranslator if "translator"
* configuration is available, and evaluates to boolean false.
* - an Translator seed with an I18nTranslator if "translator"
* configuration is available, and is a non-empty array or a Traversable
* instance.
* - null in all other cases, including absence of a configuration service.
*
* @return void|Translator
*/
private function marshalTranslatorFromConfig(ContainerInterface $container)
{
if (! $container->has('config')) {
return;
}

$config = $container->get('config');

if (! is_array($config) || ! array_key_exists('translator', $config)) {
return;
}

// 'translator' => false
if ($config['translator'] === false) {
return new Translator(new DummyTranslator());
}

// Empty translator configuration
if (is_array($config['translator']) && empty($config['translator'])) {
return;
}

// Unusable translator configuration
if (! is_array($config['translator']) && ! $config['translator'] instanceof Traversable) {
return;
}

// Create translator from configuration
$i18nTranslator = I18nTranslator::factory($config['translator']);

// Inject plugins, if present
if ($container->has('TranslatorPluginManager')) {
$loaderManager = $container->get('TranslatorPluginManager');

assert($loaderManager instanceof LoaderPluginManager);

$i18nTranslator->setPluginManager($loaderManager);
}

// Inject into service manager instances
if ($container instanceof ServiceManager) {
$container->setService(TranslatorInterface::class, $i18nTranslator);
}

return new Translator($i18nTranslator);
}
}
7 changes: 5 additions & 2 deletions src/ValidatorPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laminas\Validator;

use Laminas\I18n\Translator\TranslatorInterface;
use Laminas\I18n\Validator as I18nValidator;
use Laminas\ServiceManager\AbstractPluginManager;
use Laminas\ServiceManager\Exception\InvalidServiceException;
Expand Down Expand Up @@ -589,9 +590,11 @@ public function injectTranslator($first, $second)
$container = $container->getServiceLocator();
}

if ($validator instanceof Translator\TranslatorAwareInterface) {
if ($container && $container->has('MvcTranslator')) {
if ($validator instanceof Translator\TranslatorAwareInterface && $container instanceof ContainerInterface) {
if ($container->has('MvcTranslator')) {
$validator->setTranslator($container->get('MvcTranslator'));
} elseif ($container->has(TranslatorInterface::class)) {
$validator->setTranslator($container->get(Translator\TranslatorInterface::class));
}
}
}
Expand Down
Loading