假设我有一个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中使用,但我认为在这种情况下在这个实体中使用它是合适的。
答案 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}}方法。