【问题标题】:How to use the translator service inside an Entity?如何在实体内使用翻译服务?
【发布时间】:2011-11-23 14:53:44
【问题描述】:

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

如何使用该实体内的翻译服务来翻译“先生”和“夫人”?

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

【问题讨论】:

    标签: php symfony entity translation


    【解决方案1】:

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

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

    $entity->setTranslator($translator);
    

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

    {{ entity.property|trans }}).
    

    【讨论】:

    • 哇!我不知道 {{ $variable |反}}函数。我相信它只能使用纯文本(而不是变量)。非常感谢!
    • 是的,顺便说一句,您不需要 $ 符号。我今天一直在使用 Smarty,在 twig: {{variable|trans}} 你甚至可以翻译从函数、数组等返回的文本,例如: {{app.session.getFlash('panel_alert').msg|trans} }
    【解决方案2】:

    您不应该这样做,一般来说这是不可能的。根据Single Responsibility Principle,实体已经有了它们的目的,即表示数据库上的数据。此外,翻译是一个表示问题,因此您不太可能希望在实体层解决这样的问题(除非您想提供以不同语言翻译的实体,这完全是一个不同的问题,甚至不应该使用译者)。

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

    【讨论】:

    【解决方案3】:

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

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

    在你的messages.en.yml中

    Monsieur: Mr.
    Madame: Mrs.
    

    【讨论】:

      【解决方案4】:

      在过去的几年里,我多次遇到这个问题,并且总能找到足够好的解决方法。这次我在整个项目中大量使用的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 过滤器)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-05
        • 2012-05-03
        • 2021-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多