forked from laminas/laminas-i18n
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of placeholders within translated text
- Loading branch information
1 parent
b3259ac
commit f5413d6
Showing
12 changed files
with
407 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Laminas\I18n\Translator\Placeholder; | ||
|
||
use function str_replace; | ||
|
||
class HandlebarPlaceholder implements PlaceholderInterface | ||
{ | ||
/** | ||
* @param iterable<string, string> $placeholders | ||
*/ | ||
public function compile(string $locale, string $message, iterable $placeholders = []): string | ||
{ | ||
$compiled = $message; | ||
foreach ($placeholders as $key => $value) { | ||
$compiled = str_replace("{{{$key}}}", $value, $compiled); | ||
} | ||
|
||
return $compiled; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Laminas\I18n\Translator\Placeholder; | ||
|
||
use MessageFormatter; | ||
use Traversable; | ||
|
||
use function iterator_to_array; | ||
|
||
class IcuPlaceholder implements PlaceholderInterface | ||
{ | ||
public function compile(string $locale, string $message, iterable $placeholders = []): string | ||
{ | ||
if ($placeholders instanceof Traversable) { | ||
$placeholders = iterator_to_array($placeholders); | ||
} | ||
|
||
return MessageFormatter::formatMessage($locale, $message, $placeholders); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Laminas\I18n\Translator\Placeholder; | ||
|
||
interface PlaceholderInterface | ||
{ | ||
/** | ||
* @param iterable<string|int, string> $placeholders | ||
*/ | ||
public function compile(string $locale, string $message, iterable $placeholders = []): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Laminas\I18n\Translator\Placeholder; | ||
|
||
use Laminas\I18n\Exception\ParseException; | ||
use Traversable; | ||
|
||
use function call_user_func_array; | ||
use function iterator_to_array; | ||
|
||
class PrintfPlaceholder implements PlaceholderInterface | ||
{ | ||
/** | ||
* @param iterable<int, string> $placeholders | ||
*/ | ||
public function compile(string $locale, string $message, iterable $placeholders = []): string | ||
{ | ||
if ($placeholders instanceof Traversable) { | ||
$placeholders = iterator_to_array($placeholders); | ||
} | ||
|
||
/** @var string|false $compiled */ | ||
$compiled = call_user_func_array('vsprintf', [$message, $placeholders]); | ||
if ($compiled === false) { | ||
throw new ParseException( | ||
'Error occurred while processing sprintf placeholders for message "' . $message . '"' | ||
); | ||
} | ||
|
||
return $compiled; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Laminas\I18n\Translator\Placeholder; | ||
|
||
use Laminas\I18n\Exception\InvalidArgumentException; | ||
use Laminas\I18n\Exception\ParseException; | ||
use Laminas\Stdlib\ArrayUtils; | ||
use Throwable; | ||
use Traversable; | ||
|
||
use function iterator_to_array; | ||
use function str_replace; | ||
use function strlen; | ||
use function strtoupper; | ||
use function ucfirst; | ||
use function uksort; | ||
|
||
class SegmentPlaceholder implements PlaceholderInterface | ||
{ | ||
public function compile(string $locale, string $message, iterable $placeholders = []): string | ||
{ | ||
if ($placeholders instanceof Traversable) { | ||
$placeholders = iterator_to_array($placeholders); | ||
} | ||
|
||
if (empty($placeholders)) { | ||
return $message; | ||
} | ||
|
||
if (! ArrayUtils::hasStringKeys($placeholders)) { | ||
throw new InvalidArgumentException( | ||
'SegmentPlaceholder expects an associative array of placeholder names and values' | ||
); | ||
} | ||
|
||
try { | ||
// Sorting the array by key length to replace placeholders with longer names first | ||
// to avoid replacing placeholders with shorter names that are part of longer names | ||
uksort($placeholders, static function (string|int $a, string|int $b) { | ||
return strlen((string) $a) <=> strlen((string) $b); | ||
}); | ||
|
||
$compiled = $message; | ||
foreach ($placeholders 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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?php | ||
|
||
namespace Laminas\I18n\Translator; | ||
|
||
use Laminas\I18n\Exception; | ||
use Laminas\I18n\Translator\Loader\RemoteLoaderInterface; | ||
use Laminas\ServiceManager\AbstractPluginManager; | ||
use Laminas\ServiceManager\Exception\InvalidServiceException; | ||
use Laminas\ServiceManager\Factory\InvokableFactory; | ||
|
||
use function gettype; | ||
use function is_object; | ||
use function sprintf; | ||
|
||
/** | ||
* Plugin manager implementation for translation placeholder compilers. | ||
* | ||
* Enforces that placeholder compilers retrieved are either instances of | ||
* Placeholder\PlaceholderInterface. Additionally, it registers a number | ||
* of default placeholder compilers. | ||
* | ||
* @template InstanceType of Placeholder\PlaceholderInterface | ||
* @extends AbstractPluginManager<InstanceType> | ||
* @method Placeholder\PlaceholderInterface get(string $name) | ||
*/ | ||
class PlaceholderPluginManager extends AbstractPluginManager | ||
{ | ||
/** @inheritDoc */ | ||
protected $aliases = [ | ||
'colon' => Placeholder\SegmentPlaceholder::class, | ||
'laravel' => Placeholder\SegmentPlaceholder::class, | ||
'handlebar' => Placeholder\HandlebarPlaceholder::class, | ||
'handlebars' => Placeholder\HandlebarPlaceholder::class, | ||
'icu' => Placeholder\IcuPlaceholder::class, | ||
'vsprintf' => Placeholder\PrintfPlaceholder::class, | ||
'sprintf' => Placeholder\PrintfPlaceholder::class, | ||
'printf' => Placeholder\PrintfPlaceholder::class, | ||
]; | ||
|
||
/** @inheritDoc */ | ||
protected $factories = [ | ||
Placeholder\SegmentPlaceholder::class => InvokableFactory::class, | ||
Placeholder\HandlebarPlaceholder::class => InvokableFactory::class, | ||
Placeholder\IcuPlaceholder::class => InvokableFactory::class, | ||
Placeholder\PrintfPlaceholder::class => InvokableFactory::class, | ||
]; | ||
|
||
/** | ||
* Validate the plugin. | ||
* | ||
* Checks that the filter loaded is an instance of | ||
* Loader\FileLoaderInterface or Loader\RemoteLoaderInterface. | ||
* | ||
* @throws Exception\RuntimeException If invalid. | ||
* @psalm-assert RemoteLoaderInterface $instance | ||
*/ | ||
public function validate(mixed $instance): void | ||
{ | ||
if ($instance instanceof Placeholder\PlaceholderInterface) { | ||
// we're okay | ||
return; | ||
} | ||
|
||
throw new InvalidServiceException(sprintf( | ||
'Plugin of type %s is invalid; must implement %s', | ||
is_object($instance) ? $instance::class : gettype($instance), | ||
Placeholder\PlaceholderInterface::class | ||
)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
namespace Laminas\I18n\Translator; | ||
|
||
use Laminas\ServiceManager\Config; | ||
use Laminas\ServiceManager\Factory\FactoryInterface; | ||
use Laminas\ServiceManager\ServiceLocatorInterface; | ||
use Laminas\ServiceManager\ServiceManager; | ||
use Psr\Container\ContainerInterface; | ||
|
||
use function is_array; | ||
|
||
/** @psalm-import-type ServiceManagerConfiguration from ServiceManager */ | ||
class PlaceholderPluginManagerFactory implements FactoryInterface | ||
{ | ||
/** | ||
* Create and return a PlaceholderPluginManager. | ||
* | ||
* @param string $name | ||
* @param array<string, mixed>|null $options | ||
* @psalm-param ServiceManagerConfiguration|null $options | ||
* @return LoaderPluginManager | ||
*/ | ||
public function __invoke(ContainerInterface $container, $name, ?array $options = null) | ||
{ | ||
$options = $options ?? []; | ||
$pluginManager = new PlaceholderPluginManager($container, $options); | ||
|
||
// If this is in a laminas-mvc application, the ServiceListener will inject | ||
// merged configuration during bootstrap. | ||
if ($container->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_placeholders']) || ! is_array($config['translator_placeholders'])) { | ||
return $pluginManager; | ||
} | ||
|
||
// Wire service configuration for translator_plugins | ||
(new Config($config['translator_placeholders']))->configureServiceManager($pluginManager); | ||
|
||
return $pluginManager; | ||
} | ||
|
||
/** | ||
* laminas-servicemanager v2 factory to return LoaderPluginManager | ||
* | ||
* @deprecated Since 2.16.0 - This component is no longer compatible with Service Manager v2. | ||
* This method will be removed in version 3.0 | ||
* | ||
* @return LoaderPluginManager | ||
*/ | ||
public function createService(ServiceLocatorInterface $container) | ||
{ | ||
return $this($container, 'TranslatorPluginManager', $this->creationOptions); | ||
} | ||
|
||
/** | ||
* v2 support for instance creation options. | ||
* | ||
* @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 array $options | ||
* @return void | ||
*/ | ||
public function setCreationOptions(array $options) | ||
{ | ||
$this->creationOptions = $options; | ||
} | ||
} |
Oops, something went wrong.