Skip to content

Commit 3c5b135

Browse files
briedisoscarotero
authored andcommitted
VueJS extractor. Include "v-on:" attribute when extracting messages. (#184)
* VueJS extractor. Include "v-on:" attribute when extracting messages. * Fix phpspec error * Allow VueJs template attribute prefixes to be passed through options.
1 parent 1ad5dbb commit 3c5b135

2 files changed

Lines changed: 38 additions & 8 deletions

File tree

src/Extractors/VueJs.php

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ class VueJs extends JsCode implements ExtractorInterface
2020
public static function fromString($string, Translations $translations, array $options = [])
2121
{
2222
$options += self::$options;
23+
$options += [
24+
// HTML attribute prefixes we parse as JS which could contain translations (are JS expressions)
25+
'attributePrefixes' => [
26+
':',
27+
'v-bind:',
28+
'v-on:',
29+
],
30+
];
2331

2432
// Ok, this is the weirdest hack, but let me explain:
2533
// On Linux (Mac is fine), when converting HTML to DOM, new lines get trimmed after the first tag.
@@ -111,7 +119,7 @@ private static function getTemplateTranslations(
111119
$lineOffset = 0
112120
) {
113121
// Build a JS string from all template attribute expressions
114-
$fakeAttributeJs = self::getTemplateAttributeFakeJs($dom);
122+
$fakeAttributeJs = self::getTemplateAttributeFakeJs($options, $dom);
115123

116124
// 1 line offset is necessary because parent template element was ignored when converting to DOM
117125
self::getScriptTranslationsFromString($fakeAttributeJs, $translations, $options, $lineOffset);
@@ -125,12 +133,13 @@ private static function getTemplateTranslations(
125133
* Extract JS expressions from element attribute bindings (excluding text within elements)
126134
* For example: <span :title="__('extract this')"> skip element content </span>
127135
*
136+
* @param array $options
128137
* @param DOMElement $dom
129138
* @return string JS code
130139
*/
131-
private static function getTemplateAttributeFakeJs(DOMElement $dom)
140+
private static function getTemplateAttributeFakeJs(array $options, DOMElement $dom)
132141
{
133-
$expressionsByLine = self::getVueAttributeExpressions($dom);
142+
$expressionsByLine = self::getVueAttributeExpressions($options['attributePrefixes'], $dom);
134143

135144
$maxLines = max(array_keys($expressionsByLine));
136145
$fakeJs = '';
@@ -148,12 +157,16 @@ private static function getTemplateAttributeFakeJs(DOMElement $dom)
148157
/**
149158
* Loop DOM element recursively and parse out all dynamic vue attributes which are basically JS expressions
150159
*
160+
* @param array $attributePrefixes List of attribute prefixes we parse as JS (may contain translations)
151161
* @param DOMElement $dom
152162
* @param array $expressionByLine [lineNumber => [jsExpression, ..], ..]
153163
* @return array [lineNumber => [jsExpression, ..], ..]
154164
*/
155-
private static function getVueAttributeExpressions(DOMElement $dom, array &$expressionByLine = [])
156-
{
165+
private static function getVueAttributeExpressions(
166+
array $attributePrefixes,
167+
DOMElement $dom,
168+
array &$expressionByLine = []
169+
) {
157170
$children = $dom->childNodes;
158171

159172
for ($i = 0; $i < $children->length; $i++) {
@@ -170,21 +183,38 @@ private static function getVueAttributeExpressions(DOMElement $dom, array &$expr
170183
$domAttr = $attrList->item($j);
171184

172185
// Check if this is a dynamic vue attribute
173-
if (strpos($domAttr->name, ':') === 0 || strpos($domAttr->name, 'v-bind:') === 0) {
186+
if (self::isAttributeMatching($domAttr->name, $attributePrefixes)) {
174187
$line = $domAttr->getLineNo();
175188
$expressionByLine += [$line => []];
176189
$expressionByLine[$line][] = $domAttr->value;
177190
}
178191
}
179192

180193
if ($node->hasChildNodes()) {
181-
$expressionByLine = self::getVueAttributeExpressions($node, $expressionByLine);
194+
$expressionByLine = self::getVueAttributeExpressions($attributePrefixes, $node, $expressionByLine);
182195
}
183196
}
184197

185198
return $expressionByLine;
186199
}
187200

201+
/**
202+
* Check if this attribute name should be parsed for translations
203+
*
204+
* @param string $attributeName
205+
* @param string[] $attributePrefixes
206+
* @return bool
207+
*/
208+
private static function isAttributeMatching($attributeName, $attributePrefixes)
209+
{
210+
foreach ($attributePrefixes as $prefix) {
211+
if (strpos($attributeName, $prefix) === 0) {
212+
return true;
213+
}
214+
}
215+
return false;
216+
}
217+
188218
/**
189219
* Extract JS expressions from within template elements (excluding attributes)
190220
* For example: <span :title="skip attributes"> {{__("extract element content")}} </span>

tests/assets/vuejs/input.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{{ngettext('t-p1(parentheses)', 't-p2(parentheses)', (true || false ? 1 : (1+1)))}}
2929
regular text { test {something} test }
3030

31-
<a v-bind:title='gettext("t-v-bind")'></a>
31+
<a v-on:click='alert(gettext("t-v-bind"))'></a>
3232

3333
<a v-bind:title='pgettext("context", "t-action")'></a>
3434

0 commit comments

Comments
 (0)