Version 1
[yaffs-website] / web / core / lib / Drupal / Core / Template / TwigNodeTrans.php
1 <?php
2
3 namespace Drupal\Core\Template;
4
5 /**
6  * A class that defines the Twig 'trans' tag for Drupal.
7  *
8  * This Twig extension was originally based on Twig i18n extension. It has been
9  * severely modified to work properly with the complexities of the Drupal
10  * translation system.
11  *
12  * @see http://twig.sensiolabs.org/doc/extensions/i18n.html
13  * @see https://github.com/fabpot/Twig-extensions
14  */
15 class TwigNodeTrans extends \Twig_Node {
16
17   /**
18    * {@inheritdoc}
19    */
20   public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) {
21     parent::__construct([
22       'count' => $count,
23       'body' => $body,
24       'plural' => $plural,
25       'options' => $options,
26     ], [], $lineno, $tag);
27   }
28
29   /**
30    * {@inheritdoc}
31    */
32   public function compile(\Twig_Compiler $compiler) {
33     $compiler->addDebugInfo($this);
34
35     $options = $this->getNode('options');
36
37     list($singular, $tokens) = $this->compileString($this->getNode('body'));
38     $plural = NULL;
39
40     if (NULL !== $this->getNode('plural')) {
41       list($plural, $pluralTokens) = $this->compileString($this->getNode('plural'));
42       $tokens = array_merge($tokens, $pluralTokens);
43     }
44
45     // Start writing with the function to be called.
46     $compiler->write('echo ' . (empty($plural) ? 't' : '\Drupal::translation()->formatPlural') . '(');
47
48     // Move the count to the beginning of the parameters list.
49     if (!empty($plural)) {
50       $compiler->raw('abs(')->subcompile($this->getNode('count'))->raw('), ');
51     }
52
53     // Write the singular text parameter.
54     $compiler->subcompile($singular);
55
56     // Write the plural text parameter, if necessary.
57     if (!empty($plural)) {
58       $compiler->raw(', ')->subcompile($plural);
59     }
60
61     // Write any tokens found as an associative array parameter, otherwise just
62     // leave as an empty array.
63     $compiler->raw(', array(');
64     foreach ($tokens as $token) {
65       $compiler->string($token->getAttribute('placeholder'))->raw(' => ')->subcompile($token)->raw(', ');
66     }
67     $compiler->raw(')');
68
69     // Write any options passed.
70     if (!empty($options)) {
71       $compiler->raw(', ')->subcompile($options);
72     }
73
74     // Write function closure.
75     $compiler->raw(')');
76
77     // @todo Add debug output, see https://www.drupal.org/node/2512672
78
79     // End writing.
80     $compiler->raw(";\n");
81   }
82
83   /**
84    * Extracts the text and tokens for the "trans" tag.
85    *
86    * @param \Twig_Node $body
87    *   The node to compile.
88    *
89    * @return array
90    *   Returns an array containing the two following parameters:
91    *   - string $text
92    *       The extracted text.
93    *   - array $tokens
94    *       The extracted tokens as new \Twig_Node_Expression_Name instances.
95    */
96   protected function compileString(\Twig_Node $body) {
97     if ($body instanceof \Twig_Node_Expression_Name || $body instanceof \Twig_Node_Expression_Constant || $body instanceof \Twig_Node_Expression_TempName) {
98       return [$body, []];
99     }
100
101     $tokens = [];
102     if (count($body)) {
103       $text = '';
104
105       foreach ($body as $node) {
106         if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof \Twig_Node_SetTemp) {
107           $node = $node->getNode(1);
108         }
109
110         if ($node instanceof \Twig_Node_Print) {
111           $n = $node->getNode('expr');
112           while ($n instanceof \Twig_Node_Expression_Filter) {
113             $n = $n->getNode('node');
114           }
115
116           $args = $n;
117
118           // Support TwigExtension->renderVar() function in chain.
119           if ($args instanceof \Twig_Node_Expression_Function) {
120             $args = $n->getNode('arguments')->getNode(0);
121           }
122
123           // Detect if a token implements one of the filters reserved for
124           // modifying the prefix of a token. The default prefix used for
125           // translations is "@". This escapes the printed token and makes them
126           // safe for templates.
127           // @see TwigExtension::getFilters()
128           $argPrefix = '@';
129           while ($args instanceof \Twig_Node_Expression_Filter) {
130             switch ($args->getNode('filter')->getAttribute('value')) {
131               case 'placeholder':
132                 $argPrefix = '%';
133                 break;
134             }
135             $args = $args->getNode('node');
136           }
137           if ($args instanceof \Twig_Node_Expression_GetAttr) {
138             $argName = [];
139             // Reuse the incoming expression.
140             $expr = $args;
141             // Assemble a valid argument name by walking through the expression.
142             $argName[] = $args->getNode('attribute')->getAttribute('value');
143             while ($args->hasNode('node')) {
144               $args = $args->getNode('node');
145               if ($args instanceof \Twig_Node_Expression_Name) {
146                 $argName[] = $args->getAttribute('name');
147               }
148               else {
149                 $argName[] = $args->getNode('attribute')->getAttribute('value');
150               }
151             }
152             $argName = array_reverse($argName);
153             $argName = implode('.', $argName);
154           }
155           else {
156             $argName = $n->getAttribute('name');
157             if (!is_null($args)) {
158               $argName = $args->getAttribute('name');
159             }
160             $expr = new \Twig_Node_Expression_Name($argName, $n->getLine());
161           }
162           $placeholder = sprintf('%s%s', $argPrefix, $argName);
163           $text .= $placeholder;
164           $expr->setAttribute('placeholder', $placeholder);
165           $tokens[] = $expr;
166         }
167         else {
168           $text .= $node->getAttribute('data');
169         }
170       }
171     }
172     elseif (!$body->hasAttribute('data')) {
173       throw new \Twig_Error_Syntax('{% trans %} tag cannot be empty');
174     }
175     else {
176       $text = $body->getAttribute('data');
177     }
178
179     return [new \Twig_Node([new \Twig_Node_Expression_Constant(trim($text), $body->getLine())]), $tokens];
180   }
181
182 }