在过去的几年里,我多次遇到这个问题,并且总能找到足够好的解决方法。这次我在整个项目中大量使用的getIdentifyingName() 方法(例如显式的__toString())必须翻译数据层中使用的一些关键字,因此没有优雅的解决方法。
我这次的解决方案是TranslateObject 和相应的帮助服务。 TranslateObject 是一个普通对象,它包含一个翻译键和一个占位符数组,它们也可以是 TranslateObjects 以允许多级翻译(例如 getIdentifyingNameTranslateObject() 在其中一个占位符内调用另一个相关对象的 getIdentifyingNameTranslateObject()):
namespace App\Utils;
class TranslateObject
{
/** @var string */
protected $transKey;
/** @var array */
protected $placeholders;
public function __construct(string $transKey, array $placeholders = [])
{
$this->transKey = $transKey;
$this->placeholders = self::normalizePlaceholders($placeholders);
}
public static function normalizePlaceholders(array $placeholders): array
{
foreach ($placeholders as $key => &$placeholder) {
if (substr($key, 0, 1) !== '%' || substr($key, -1, 1) !== '%') {
throw new \InvalidArgumentException('The $placeholder attribute must only contain keys in format "%placeholder%".');
}
if ($placeholder instanceof TranslateObject) {
continue;
}
if (is_scalar($placeholder)) {
$placeholder = ['value' => $placeholder];
}
if (!isset($placeholder['value']) || !is_scalar($placeholder['value'])) {
throw new \InvalidArgumentException('$placeholders[\'%placeholder%\'][\'value\'] must be present and a scalar value.');
}
if (!isset($placeholder['translate'])) {
$placeholder['translate'] = false;
}
if (!is_bool($placeholder['translate'])) {
throw new \InvalidArgumentException('$placeholders[\'%placeholder%\'][\'translate\'] must be a boolean.');
}
}
return $placeholders;
}
public function getTransKey(): string
{
return $this->transKey;
}
public function getPlaceholders(): array
{
return $this->placeholders;
}
}
帮助器看起来像这样并完成工作:
namespace App\Services;
use App\Utils\TranslateObject;
use Symfony\Contracts\Translation\TranslatorInterface;
class TranslateObjectHelper
{
/** @var TranslatorInterface */
protected $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function trans(TranslateObject $translateObject): string
{
$placeholders = $translateObject->getPlaceholders();
foreach ($placeholders as $key => &$placeholder) {
if ($placeholder instanceof TranslateObject) {
$placeholder = $this->trans($placeholder);
}
elseif (true === $placeholder['translate']) {
$placeholder = $this->translator->trans($placeholder['value']);
}
else {
$placeholder = $placeholder['value'];
}
}
return $this->translator->trans($translateObject->getTransKey(), $placeholders);
}
}
然后在实体中有一个getIdentifyingNameTranslateObject() 方法返回一个TranslateObject。
/**
* Get an identifying name as a TranslateObject (for use with TranslateObjectHelper)
*/
public function getIdentifyingNameTranslateObject(): TranslateObject
{
return new TranslateObject('my.whatever.translation.key', [
'%placeholder1%' => $this->myEntityProperty1,
'%placeholderWithANeedOfTranslation%' => [
'value' => 'my.whatever.translation.values.' . $this->myPropertyWithANeedOfTranslation,
'translate' => true,
],
'%placeholderWithCascadingTranslationNeeds%' => $this->getRelatedEntity()->getIdentifyingNameTranslateObject(),
]);
}
当我需要返回这样一个翻译后的属性时,我需要访问我注入的TranslateObjectHelper 服务并像在控制器或任何其他服务中一样使用它的trans() 方法:
$this->translateObjectHelper->trans($myObject->getIdentifyingNameTranslateObject());
然后我创建了一个树枝过滤器作为一个简单的助手,如下所示:
namespace App\Twig;
use App\Services\TranslateObjectHelper;
use App\Utils\TranslateObject;
class TranslateObjectExtension extends \Twig_Extension
{
/** @var TranslateObjectHelper */
protected $translateObjectHelper;
public function __construct(TranslateObjectHelper $translateObjectHelper)
{
$this->translateObjectHelper = $translateObjectHelper;
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('translateObject', [$this, 'translateObject']),
);
}
/**
* sends a TranslateObject through a the translateObjectHelper->trans() method
*/
public function translateObject(TranslateObject $translateObject): string
{
return $this->translateObjectHelper->trans($translateObject);
}
public function getName(): string
{
return 'translate_object_twig_extension';
}
}
所以在 Twig 中我可以这样翻译:
{{ myObject.getIdentifyingNameTranslateObject()|translateObject }}
最后,我“只”需要在这些实体上找到所有 getIdentifyingName() 调用(或 Twig 中的 .identifyingName),并将它们替换为 getIdentifyingNameTranslateObject(),并调用 @987654341 的 trans() 方法@(或 Twig 中的 translateObject 过滤器)。