强制更改Twig语言环境

33
我想使用Twig模板系统来为我的电子邮件进行模板化。电子邮件的语言环境应基于用户设置,而不是会话或请求语言环境。在渲染Twig模板时如何强制指定语言环境?
手册确实提到了如何强制Translator的语言环境。但我想将此语言环境传递给render()方法,以便在此语言环境下呈现Twig模板中的翻译。
这与在模板中使用into不同,因为我认为这会强制在特定语言环境中翻译模板内的内容。
因此,以Symfony的示例为基础,我正在寻找类似以下代码的解决方案:
public function indexAction($name)
{
    $message = \Swift_Message::newInstance()
        ->setSubject('Hello Email')
        ->setFrom('send@example.com')
        ->setTo('recipient@example.com')
        ->setBody(
            $this->renderView(
                'HelloBundle:Hello:email.txt.twig',
                array('name' => $name),
                'nl_NL' // <-- This would be nice!
            )
        )
    ;
    $this->get('mailer')->send($message);

    return $this->render(...);
}
4个回答

43

当您使用trans过滤器时,可以通过参数传递区域设置(请参见diff:https://github.com/symfony/symfony/commit/3ea31a02630412b1c732ee1647a0724378f67665)。

因此,在控制器中的render方法中传递另一个user_locale变量(或者传递整个用户对象而不是分别传递名称和user_locale,或者在模板中使用app.user如果用户已登录等(根据您的应用程序显然)),然后在电子邮件模板中,您可以像这样编写:

{{ 'greeting' | trans({}, "messages", user_locale) }} {{ name | title }}
{# rest of email template with more translation strings #}

然后在您用于该地区的翻译文件中(假设您正在使用yaml),只需像这样编写一些内容,那么翻译将会很好地运作:

# messages.fr.yml    
greeting: 'Bonjour'

22

在渲染模板之前,获取翻译组件并更改其语言环境。这种解决方案不需要向render()方法的参数数组中传递额外的值,并且不需要痛苦地重构所有的Twig文件。

public function indexAction($name)
{
    $translator = $this->get('translator');

    // Save the current session locale
    // before overwriting it. Suppose its 'en_US'
    $sessionLocale = $translator->getLocale();

    $translator->setLocale('nl_NL');

    $message = \Swift_Message::newInstance()
        ->setSubject('Hello Email')
        ->setFrom('send@example.com')
        ->setTo('recipient@example.com')
        ->setBody(
            $this->renderView(
                'HelloBundle:Hello:email.txt.twig',
                array('name' => $name)
            )
        )
    ;

    $this->get('mailer')->send($message);

    // Otherwise subsequent templates would also
    // be rendered in Dutch instead of English
    $translator->setLocale($sessionLocale);

    return $this->render(...);
}

用户邮件的常见做法是在用户实体中存储用户的语言环境,并直接将其传递给翻译器,例如在此代码片段中:

一种常见的处理用户邮件的方法是将用户的语言环境存储在用户实体中,然后直接将其传递给翻译器,如下面的代码片段所示:

$translator->setLocale($recipientUser->getLocale());

5
包含的子模板不受影响。 - Jacky Chan
这应该是默认答案,因为它只在一个地方更改语言环境,而不是更改数百个(在我们的情况下)模板。 - Serhii Polishchuk

2

这里有一个解决方案(它很好用,除了子模板(Twig: render(controller('AppBundle:Invoice/Index:productTotalPartial'))))

<?php

namespace Main\BoBundle\Service;

use Symfony\Component\Translation\TranslatorInterface;

/**
 * Class LocaleSwitcher
 *
 * @package Main\BoBundle\Service
 */
class LocaleSwitcher
{
    /**
     * @var TranslatorInterface
     */
    private $translator;

    /**
     * @var string
     */
    private $previousLocale;

    /**
     * @param TranslatorInterface $translator
     */
    public function __construct(TranslatorInterface $translator)
    {
        $this->translator = $translator;
    }

    /**
     * Change the locale
     *
     * @param string $locale
     */
    public function setLocale($locale)
    {
        $this->previousLocale = $this->translator->getLocale();

        $this->translator->setLocale($locale);
        $this->setPhpDefaultLocale($locale);
    }

    /**
     * Revert the locale
     */
    public function revertLocale()
    {
        if ($this->previousLocale === null) {
            return;
        }

        $this->translator->setLocale($this->previousLocale);
        $this->setPhpDefaultLocale($this->previousLocale);

        $this->previousLocale = null;
    }

    /**
     * Use temporary locale in closure function
     *
     * @param string $locale
     * @param \Closure $c
     */
    public function temporaryLocale($locale, \Closure $c)
    {
        $this->setLocale($locale);

        $c();

        $this->revertLocale();
    }

    /**
     * Sets the default PHP locale.
     * Copied from Symfony/Component/HttpFoundation/Request.php
     *
     * @param string $locale
     */
    private function setPhpDefaultLocale($locale)
    {
        // if either the class Locale doesn't exist, or an exception is thrown when
        // setting the default locale, the intl module is not installed, and
        // the call can be ignored:
        try {
            if (class_exists('Locale', false)) {
                /** @noinspection PhpUndefinedClassInspection */
                \Locale::setDefault($locale);
            }
        } catch (\Exception $e) {
        }
    }
}

例子:

if ($this->getLocale()) {
    $this->localeSwitcher->setLocale($this->getLocale());
}

$html = $this->templating->render($templatePath);

if ($this->getLocale()) {
    $this->localeSwitcher->revertLocale();
}

-1
你可以这样做:向模板传递一个参数(例如locale)。
public function indexAction($name)
{
    $message = \Swift_Message::newInstance()
        ->setSubject('Hello Email')
        ->setFrom('send@example.com')
        ->setTo('recipient@example.com')
        ->setBody(
            $this->renderView(
                'HelloBundle:Hello:email.txt.twig',
                array('name' => $name, 'locale' => 'nl_NL'),
            )
        )
    ;
    $this->get('mailer')->send($message);

    return $this->render(...);
}

1
是的,但我不认为模板会自动使用此区域设置来处理 {% trans %} 块,对吧? - rolandow
4
不可以强制翻译过滤器使用你想要的语言环境,但可以这样更改:{{ "Hello"|trans({}, "messages", locale) }}。翻译组件会自动使用请求中定义的语言环境,如果需要更改,可以使用 $this->get('translator')->setLocale($locale); - Lhassan Baazzi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接