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

发布于 2024-12-17 16:06:45 字数 546 浏览 1 评论 0 原文

假设我有一个 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”?

翻译服务似乎只能在控制器内部使用,但我认为在这种情况下在该实体内部使用它是合适的。

Let's say I have a User Entity :

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

With this function :

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

How can I use the translator service inside this Entity to translate "Monsieur" and "Madame" ?

It seems the translator service should be used only inside a Controller, but I think it's appropriate in that case to use it inside this Entity.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

孤寂小茶 2024-12-24 16:06:45

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

但像 aldo said 这样的实体不应该承担这样的责任。在最糟糕的情况下,如果您确实想翻译实体内部的内容,您可以使用 set 方法将翻译器传递给实体,即

$entity->setTranslator($translator);

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

{{ entity.property|trans }}).

The translator service is, like you say, a "service" you can use a service inside any class (i.e. defining it as a service too and using the dependency injector container). So, you can use the translator almost wherever you want.

But the entities like aldo said shouldn't have that responsability. In the worst scenario if you really want to translate things inside the entity, you could pass the translator to the entity with a set method, i.e.

$entity->setTranslator($translator);

but I recommend you too to create a class that handles the problem outside the entity, i.e. using the twig template

{{ entity.property|trans }}).
始终不够 2024-12-24 16:06:45

你不应该这样做,而且一般来说这是不可能的。根据单一责任原则,实体已经有其目的,即表示数据库上的数据。此外,翻译是一个表示问题,因此您不太可能想在实体层解决这样的问题(除非您想提供用不同语言翻译的实体,这完全是一个不同的问题,甚至不应该使用译者)。

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

You shouldn't and in general it is not possible. According to the Single Responsibility Principle the entity have already their purpose, which is representing data on a database. Moreover the translation is a matter of representation, so it is unlikely that you want to address such a problem in the entity layer (unless you want to provide entities translated in different languages, which totally a different problem and shouldn't even be solved using the translator).

Rethink to your logic and try something different for this. Are you sure that you don't want to do this translation on the view layer? That would be the best thing probably. Otherwise (if your logic really need to have translation at a model level) you could create a wrapper class for entities and a factory to generate this "wrapped entities"; in that factory you could inject the translator service.

格子衫的從容 2024-12-24 16:06:45

这个问题很旧,但我决定添加一个答案以节省某人的时间。

Symfony 从 5.2 版开始就有了很好的解决方案:可翻译对象(类似于 spackmat 的解决方案)

https://symfony.com/blog/new-in-symfony-5-2-translatable-objects

use Symfony\Component\Translation\TranslatableMessage;
public function getDesignation(): TranslatableMessage {
    if ($this->getGender() == 'Male') {
        $translationKey = 'man_designation';
    } else {
        $translationKey = 'woman_designation';
    }
   return new TranslatableMessage($translationKey, ['%name%' => $this->getName()]);
}

然后在twig 模板,你可以这样渲染它:

{{ user.designation | trans }}

The question is old, but I decided to add an answer to save someone's time.

Symfony has nice solution from version 5.2: Translatable Objects (similar to spackmat's solution)

https://symfony.com/blog/new-in-symfony-5-2-translatable-objects

use Symfony\Component\Translation\TranslatableMessage;
public function getDesignation(): TranslatableMessage {
    if ($this->getGender() == 'Male') {
        $translationKey = 'man_designation';
    } else {
        $translationKey = 'woman_designation';
    }
   return new TranslatableMessage($translationKey, ['%name%' => $this->getName()]);
}

Then in twig template you can render it like that:

{{ user.designation | trans }}
俏︾媚 2024-12-24 16:06:45

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

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

在你的 messages.en.yml 中

Monsieur: Mr.
Madame: Mrs.

I ran into the similar problem and finally found this solution. This is not a direct answer to your problem because I'm also aware that an entity should have nothing to do with a service, like translator. So you should leave the getDesignation function untouched. Instead, in the presentation layer, twig for example, you translate that French designation.

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

And in your messages.en.yml

Monsieur: Mr.
Madame: Mrs.
甜扑 2024-12-24 16:06:45

在过去的几年里,我多次遇到这个问题,并且总是找到足够好的解决方法。这次,我在整个项目中大量使用的 getIdentifyingName() 方法(例如显式的 __toString())必须转换数据层中使用的一些关键字,因此这不是一个优雅的解决方法。

这次我的解决方案是一个 TranslateObject 和相应的帮助服务。 TranslateObject 是一个普通对象,包含翻译键和占位符数组,也可以是 TranslateObjects 以允许多级翻译(例如 getIdentifyingNameTranslateObject() 调用另一个相关对象的 getIdentifyingNameTranslateObject() code>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() 调用(或 .identifyingName 在Twig) 到该实体上,并通过调用 TranslateObjectHelpertrans() 方法(或 getIdentifyingNameTranslateObject() Twig 中的 >translateObject 过滤器)。

I ran into this problem several times over the last years and always found a good enough workaround. This time my getIdentifyingName() methods that are heavily used across the whole project (like an explicit __toString()) had to translate some keywords used in the data layer, so there was no elegant workaround.

My solution this time is a TranslateObject and a corresponding helper service. The TranslateObject is a plain object holding a translation key and an array of placeholders which also can be TranslateObjects to allow multi level translation (like a getIdentifyingNameTranslateObject() calling another related object's getIdentifyingNameTranslateObject() within one of the placeholders):

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;
    }
}

The helper looks like this and does the work:

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);
    }
}

And then within the Entity there is a getIdentifyingNameTranslateObject() method returning a 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(),
    ]);
}

When I need to return such a translated property, I need access to my injected TranslateObjectHelper service and use its trans() method like in a controller or any other service:

$this->translateObjectHelper->trans($myObject->getIdentifyingNameTranslateObject());

Then I created a twig filter as a simple helper like this:

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';
    }
}

So in Twig I can translate like this:

{{ myObject.getIdentifyingNameTranslateObject()|translateObject }}

In the end, I "just" needed to find all getIdentifyingName() calls (or .identifyingName in Twig) on that entities and replace them with getIdentifyingNameTranslateObject() with a call to the trans() method of the TranslateObjectHelper (or the translateObject filter in Twig).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文