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

dy2hfwbg  于 2023-08-06  发布在  其他
关注(0)|答案(5)|浏览(99)

假设我有一个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();
}


我如何使用本实体内的翻译服务来翻译“先生”和“女士”?
翻译服务似乎应该只在Controller内部使用,但我认为在这种情况下,在这个实体内部使用它是合适的。

7fhtutme

7fhtutme1#

这个问题很老了,但我决定添加一个答案来保存某人的时间。

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

bqf10yzr

bqf10yzr2#

翻译服务,就像你说的,是一个“服务”,你可以在任何类中使用一个服务(即也将其定义为服务并使用依赖注入器容器)。因此,您几乎可以在任何地方使用翻译器。
但是像aldo said这样的实体不应该有这种责任。在最坏的情况下,如果你真的想在实体内部翻译东西,你可以用一个set方法把翻译器传递给实体,也就是。

$entity->setTranslator($translator);

字符串
但是我也建议您创建一个类来处理实体之外的问题,例如。使用细枝模板

{{ entity.property|trans }}).

7cwmlq89

7cwmlq893#

你不应该而且一般来说这是不可能的。根据Single Responsibility Principle,这些实体已经有了它们的用途,即表示数据库上的数据。此外,翻译是一个表示的问题,因此您不太可能希望在实体层解决这样的问题(除非您希望提供以不同语言翻译的实体,这是一个完全不同的问题,甚至不应该使用翻译器来解决)。
重新思考你的逻辑,尝试一些不同的方法。您确定不想在视图层上执行此转换吗?那可能是最好的选择了。否则(如果您的逻辑确实需要在模型级别进行转换),您可以为实体创建一个 Package 类,并创建一个工厂来生成这个“ Package 的实体”;在工厂中,您可以注入Translator服务。

8zzbczxx

8zzbczxx4#

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

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

字符串
在你的信息里。en.yml

Monsieur: Mr.
Madame: Mrs.

j2cgzkjk

j2cgzkjk5#

在过去的几年里,我遇到过几次这个问题,总是找到了一个足够好的解决办法。这一次,我的getIdentifyingName()方法在整个项目中被大量使用(比如显式的__toString()),它必须转换数据层中使用的一些关键字,所以没有优雅的解决方法。
我这次的解决方案是一个TranslateObject和一个相应的helper服务。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);
    }
}


然后在Entity中有一个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(),并调用TranslateObjectHelpertrans()方法(或Twig中的translateObject过滤器)。

相关问题