You are here

class TwigNodeTrans in Drupal 8

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Template/TwigNodeTrans.php \Drupal\Core\Template\TwigNodeTrans

A class that defines the Twig 'trans' tag for Drupal.

This Twig extension was originally based on Twig i18n extension. It has been severely modified to work properly with the complexities of the Drupal translation system.

Hierarchy

  • class \Drupal\Core\Template\TwigNodeTrans extends \Drupal\Core\Template\Twig_Node

Expanded class hierarchy of TwigNodeTrans

See also

https://twig-extensions.readthedocs.io/en/latest/i18n.html

https://github.com/fabpot/Twig-extensions

File

core/lib/Drupal/Core/Template/TwigNodeTrans.php, line 17

Namespace

Drupal\Core\Template
View source
class TwigNodeTrans extends \Twig_Node {

  /**
   * {@inheritdoc}
   */
  public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) {
    $nodes['body'] = $body;
    if ($count !== NULL) {
      $nodes['count'] = $count;
    }
    if ($plural !== NULL) {
      $nodes['plural'] = $plural;
    }
    if ($options !== NULL) {
      $nodes['options'] = $options;
    }
    parent::__construct($nodes, [], $lineno, $tag);
  }

  /**
   * {@inheritdoc}
   */
  public function compile(\Twig_Compiler $compiler) {
    $compiler
      ->addDebugInfo($this);
    list($singular, $tokens) = $this
      ->compileString($this
      ->getNode('body'));
    $plural = NULL;
    if ($this
      ->hasNode('plural')) {
      list($plural, $pluralTokens) = $this
        ->compileString($this
        ->getNode('plural'));
      $tokens = array_merge($tokens, $pluralTokens);
    }

    // Start writing with the function to be called.
    $compiler
      ->write('echo ' . (empty($plural) ? 't' : '\\Drupal::translation()->formatPlural') . '(');

    // Move the count to the beginning of the parameters list.
    if (!empty($plural)) {
      $compiler
        ->raw('abs(')
        ->subcompile($this
        ->getNode('count'))
        ->raw('), ');
    }

    // Write the singular text parameter.
    $compiler
      ->subcompile($singular);

    // Write the plural text parameter, if necessary.
    if (!empty($plural)) {
      $compiler
        ->raw(', ')
        ->subcompile($plural);
    }

    // Write any tokens found as an associative array parameter, otherwise just
    // leave as an empty array.
    $compiler
      ->raw(', array(');
    foreach ($tokens as $token) {
      $compiler
        ->string($token
        ->getAttribute('placeholder'))
        ->raw(' => ')
        ->subcompile($token)
        ->raw(', ');
    }
    $compiler
      ->raw(')');

    // Write any options passed.
    if ($this
      ->hasNode('options')) {
      $compiler
        ->raw(', ')
        ->subcompile($this
        ->getNode('options'));
    }

    // Write function closure.
    $compiler
      ->raw(')');

    // @todo Add debug output, see https://www.drupal.org/node/2512672
    // End writing.
    $compiler
      ->raw(";\n");
  }

  /**
   * Extracts the text and tokens for the "trans" tag.
   *
   * @param \Twig_Node $body
   *   The node to compile.
   *
   * @return array
   *   Returns an array containing the two following parameters:
   *   - string $text
   *       The extracted text.
   *   - array $tokens
   *       The extracted tokens as new \Twig_Node_Expression_Name instances.
   */
  protected function compileString(\Twig_Node $body) {
    if ($body instanceof \Twig_Node_Expression_Name || $body instanceof \Twig_Node_Expression_Constant || $body instanceof \Twig_Node_Expression_TempName) {
      return [
        $body,
        [],
      ];
    }
    $tokens = [];
    if (count($body)) {
      $text = '';
      foreach ($body as $node) {
        if (get_class($node) === 'Twig_Node' && $node
          ->getNode(0) instanceof \Twig_Node_SetTemp) {
          $node = $node
            ->getNode(1);
        }
        if ($node instanceof \Twig_Node_Print) {
          $n = $node
            ->getNode('expr');
          while ($n instanceof \Twig_Node_Expression_Filter) {
            $n = $n
              ->getNode('node');
          }
          if ($n instanceof CheckToStringNode) {
            $n = $n
              ->getNode('expr');
          }
          $args = $n;

          // Support TwigExtension->renderVar() function in chain.
          if ($args instanceof \Twig_Node_Expression_Function) {
            $args = $n
              ->getNode('arguments')
              ->getNode(0);
          }

          // Detect if a token implements one of the filters reserved for
          // modifying the prefix of a token. The default prefix used for
          // translations is "@". This escapes the printed token and makes them
          // safe for templates.
          // @see TwigExtension::getFilters()
          $argPrefix = '@';
          while ($args instanceof \Twig_Node_Expression_Filter) {
            switch ($args
              ->getNode('filter')
              ->getAttribute('value')) {
              case 'placeholder':
                $argPrefix = '%';
                break;
            }
            $args = $args
              ->getNode('node');
          }
          if ($args instanceof CheckToStringNode) {
            $args = $args
              ->getNode('expr');
          }
          if ($args instanceof \Twig_Node_Expression_GetAttr) {
            $argName = [];

            // Reuse the incoming expression.
            $expr = $args;

            // Assemble a valid argument name by walking through the expression.
            $argName[] = $args
              ->getNode('attribute')
              ->getAttribute('value');
            while ($args
              ->hasNode('node')) {
              $args = $args
                ->getNode('node');
              if ($args instanceof \Twig_Node_Expression_Name) {
                $argName[] = $args
                  ->getAttribute('name');
              }
              else {
                $argName[] = $args
                  ->getNode('attribute')
                  ->getAttribute('value');
              }
            }
            $argName = array_reverse($argName);
            $argName = implode('.', $argName);
          }
          else {
            $argName = $n
              ->getAttribute('name');
            if (!is_null($args)) {
              $argName = $args
                ->getAttribute('name');
            }
            $expr = new \Twig_Node_Expression_Name($argName, $n
              ->getTemplateLine());
          }
          $placeholder = sprintf('%s%s', $argPrefix, $argName);
          $text .= $placeholder;
          $expr
            ->setAttribute('placeholder', $placeholder);
          $tokens[] = $expr;
        }
        else {
          $text .= $node
            ->getAttribute('data');
        }
      }
    }
    elseif (!$body
      ->hasAttribute('data')) {
      throw new \Twig_Error_Syntax('{% trans %} tag cannot be empty');
    }
    else {
      $text = $body
        ->getAttribute('data');
    }
    return [
      new \Twig_Node([
        new \Twig_Node_Expression_Constant(trim($text), $body
          ->getTemplateLine()),
      ]),
      $tokens,
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
TwigNodeTrans::compile public function
TwigNodeTrans::compileString protected function Extracts the text and tokens for the "trans" tag.
TwigNodeTrans::__construct public function