假设我有一个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();
}
如何使用实体内的翻译服务翻译"先生"answers"夫人"?
似乎翻译服务应该只在控制器中使用,但我认为在这种情况下,在这个实体中使用它是合适的。
转换器服务,就像你说的,是一个"服务",你可以在任何类中使用服务(也就是把它定义为一个服务,并使用依赖注入器容器)。所以,你几乎可以在任何你想要的地方使用翻译器。
但是像aldo所说的实体不应该有这样的责任。在最坏的情况下,如果你真的想要翻译实体内部的东西,你可以用set方法将转换器传递给实体,例如
$entity->setTranslator($translator);
但我建议你也创建一个类来处理实体之外的问题,即使用twig模板
{{ entity.property|trans }}).
你不应该这样做,一般来说这是不可能的。根据单一职责原则,实体已经有了它们的目的,即表示数据库中的数据。此外,翻译是一个表示问题,所以你不太可能想在实体层解决这样的问题(除非你想提供用不同语言翻译的实体,这完全是一个不同的问题,甚至不应该使用翻译器来解决)。
重新思考你的逻辑,尝试一些不同的东西。你确定不想在视图层上做这个转换吗?这可能是最好的选择。否则(如果你的逻辑确实需要在模型级别进行转换),你可以为实体创建一个包装器类,并创建一个工厂来生成这个"包装实体";在该工厂中,您可以注入translator服务。
我遇到了类似的问题,最后找到了这个解决方案。这不是对你问题的直接回答,因为我也意识到实体应该与服务无关,比如翻译。所以应该保持get指定函数不变。相反,在表示层(例如twig)中,您需要翻译该法语名称。
<div>{% trans %}{{ entity.designation }}{% endtrans %} {{ entity.name }}</div>
在你的messages.en.yml
Monsieur: Mr.
Madame: Mrs.
这个问题很老了,但是为了节省大家的时间,我决定加一个答案。
Symfony在5.2版中提供了很好的解决方案:可翻译对象(类似于packmat的解决方案)
https://symfony.com/blog/new-in-symfony-5-2-translatable-objectsuse 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 }}
在过去几年中,我多次遇到这个问题,总能找到一个足够好的解决方法。这一次,我的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()
,并调用TranslateObjectHelper
的trans()
方法(或Twig中的translateObject
过滤器)。