Как использовать службу переводчика внутри Entity?

Допустим, у меня есть 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();
}

Как я могу использовать службу переводчика внутри этого объекта, чтобы перевести «месье» и «мадам»?

Кажется, что служба переводчика должна использоваться только внутри контроллера, но я думаю, что в этом случае уместно использовать ее внутри этой сущности.


person lepix    schedule 23.11.2011    source источник


Ответы (4)


Служба переводчика, как вы говорите, является «службой», которую вы можете использовать внутри любого класса (т.е. определяя ее как службу и используя контейнер инжектора зависимостей). Таким образом, вы можете использовать переводчик практически везде, где захотите.

Но такие объекты, как также сказал, не должны нести такую ​​ответственность. В худшем случае, если вы действительно хотите перевести что-то внутри объекта, вы можете передать транслятор объекту с помощью заданного метода, т.е.

$entity->setTranslator($translator);

но я рекомендую вам также создать класс, который обрабатывает проблему вне сущности, т.е. используя шаблон ветки

{{ entity.property|trans }}).
person Silence    schedule 23.11.2011
comment
Вау! Я не знал о {{ $variable | транс }} функция. Я считал, что он может работать только с простым текстом (а не с переменным). Большое тебе спасибо ! - person lepix; 23.11.2011
comment
Да, кстати, вам не нужен знак $. Сегодня я работал со Smarty, в twig: {{variable|trans}} вы даже можете переводить текст, возвращаемый из функций, массивов и т. д., например: {{app.session.getFlash('panel_alert').msg|trans} } - person Silence; 24.11.2011

Вы не должны и вообще это невозможно. В соответствии с принципом единой ответственности объект уже имеет свою цель, которая представление данных в базе данных. Кроме того, перевод — это вопрос представления, поэтому маловероятно, что вы захотите решить такую ​​проблему на уровне сущностей (если только вы не хотите предоставлять сущности, переведенные на разные языки, что является совершенно другой проблемой и даже не должно решаться с помощью переводчик).

Переосмыслите свою логику и попробуйте что-то другое для этого. Вы уверены, что не хотите делать этот перевод на уровне просмотра? Это было бы лучше всего, наверное. В противном случае (если вашей логике действительно нужен перевод на уровне модели), вы можете создать класс-оболочку для сущностей и фабрику для создания этих «обернутых сущностей»; на этой фабрике вы можете внедрить службу переводчика.

person Aldo Stracquadanio    schedule 23.11.2011
comment
На самом деле я сделал это из-за другого моего вопроса: stackoverflow.com/questions/8174354/ Но я понимаю, что вы сказали. Спасибо :) - person lepix; 23.11.2011

Я столкнулся с подобной проблемой и, наконец, нашел это решение. Это не прямой ответ на вашу проблему, потому что я также знаю, что сущность не должна иметь ничего общего со службой, такой как переводчик. Поэтому вы должны оставить функцию getDesignation нетронутой. Вместо этого на уровне представления, например, ветке, вы переводите это французское обозначение.

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

И в ваших сообщениях.en.yml

Monsieur: Mr.
Madame: Mrs.
person Anh Nguyen    schedule 21.04.2014

Я сталкивался с этой проблемой несколько раз за последние годы и всегда находил достаточно хороший обходной путь. На этот раз мои методы 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);
    }
}

А затем в 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() (или .identifyingName в Twig) для этих сущностей и заменить их на getIdentifyingNameTranslateObject() вызовом метода trans() TranslateObjectHelper (или фильтра translateObject в Twig).

person spackmat    schedule 04.01.2019