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