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