如何在实体内使用翻译服务?

时间:2011-11-23 14:53:45

标签: php symfony entity translation

假设我有一个User实体:

$user = new User(007);
echo $user->getName(); // display Bond
echo $user->getGender(); // display "Male";
echo $user->getDesignation() // display "Monsieur Bond" or "Mister Bond"

使用此功能:

public function getDesignation() {
  if ($this->getGender() == 'Male') return "Monsieur ".$this->getName();
  else return "Madame ".$this->getName();
}

如何使用此实体内的翻译服务翻译“Monsieur”和“Madame”?

似乎翻译服务应该只在Controller中使用,但我认为在这种情况下在这个实体中使用它是合适的。

4 个答案:

答案 0 :(得分:8)

与您说的一样,翻译服务是一种“服务”,您可以在任何类中使用服务(即将其定义为服务并使用依赖注入器容器)。因此,您几乎可以在任何地方使用翻译器。

但像aldo said这样的实体不应该具有这种责任感。在最糟糕的情况下,如果你真的想要翻译实体内部的东西,你可以通过set方法将翻译器传递给实体,即

$entity->setTranslator($translator);

但我也建议您创建一个处理实体外部问题的类,即使用树枝模板

{{ entity.property|trans }}).

答案 1 :(得分:4)

你不应该而且通常不可能。根据{{​​3}},实体已经具有其目的,即表示数据库上的数据。此外,翻译是一个表示问题,因此你不太可能想要在实体层中解决这样的问题(除非你想提供用不同语言翻译的实体,这完全是一个不同的问题,甚至不应该用翻译)。

重新思考你的逻辑并为此尝试不同的东西。您确定不希望在视图层上执行此转换吗?这可能是最好的事情。否则(如果你的逻辑真的需要在模型级别进行翻译),你可以为实体和工厂创建一个包装类来生成这个“包装实体”;在那家工厂,你可以注入翻译服务。

答案 2 :(得分:2)

我遇到了类似的问题,终于找到了这个解决方案。这不是你问题的直接答案,因为我也知道一个实体应该与服务无关,比如翻译。所以你应该保持getDesignation函数不变。相反,例如,在表示层中,您可以翻译该法语名称。

<div>{% trans %}{{ entity.designation }}{% endtrans %} {{ entity.name }}</div>

在你的messages.en.yml

Monsieur: Mr.
Madame: Mrs.

答案 3 :(得分:0)

在过去的几年中,我几次遇到这个问题,并且总是找到一个足够好的解决方法。这次,我在整个项目中大量使用的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()替换为{{ trans()(或Twig中的TranslateObjectHelper过滤器)的1}}方法。