芝麻web文件管理V1.00
编辑当前文件:/home/mgatv524/public_html/mctv/vendor/gettext/gettext/src/Extractors/VueJs.php
[], 'functions' => [ 'gettext' => 'gettext', '__' => 'gettext', 'ngettext' => 'ngettext', 'n__' => 'ngettext', 'pgettext' => 'pgettext', 'p__' => 'pgettext', 'dgettext' => 'dgettext', 'd__' => 'dgettext', 'dngettext' => 'dngettext', 'dn__' => 'dngettext', 'dpgettext' => 'dpgettext', 'dp__' => 'dpgettext', 'npgettext' => 'npgettext', 'np__' => 'npgettext', 'dnpgettext' => 'dnpgettext', 'dnp__' => 'dnpgettext', 'noop' => 'noop', 'noop__' => 'noop', ], ]; protected static $functionsScannerClass = 'Gettext\Utils\JsFunctionsScanner'; /** * @inheritDoc * @throws Exception */ public static function fromFileMultiple($file, array $translations, array $options = []) { foreach (static::getFiles($file) as $file) { $options['file'] = $file; static::fromStringMultiple(static::readFile($file), $translations, $options); } } /** * @inheritdoc * @throws Exception */ public static function fromString($string, Translations $translations, array $options = []) { static::fromStringMultiple($string, [$translations], $options); } /** * @inheritDoc * @throws Exception */ public static function fromStringMultiple($string, array $translations, array $options = []) { $options += static::$options; $options += [ // HTML attribute prefixes we parse as JS which could contain translations (are JS expressions) 'attributePrefixes' => [ ':', 'v-bind:', 'v-on:', 'v-text', ], // HTML Tags to parse 'tagNames' => [ 'translate', ], // HTML tags to parse when attribute exists 'tagAttributes' => [ 'v-translate', ], // Comments 'commentAttributes' => [ 'translate-comment', ], 'contextAttributes' => [ 'translate-context', ], // Attribute with plural content 'pluralAttributes' => [ 'translate-plural', ], ]; // Ok, this is the weirdest hack, but let me explain: // On Linux (Mac is fine), when converting HTML to DOM, new lines get trimmed after the first tag. // So if there are new lines between
and next element, they are lost // So we insert a "." which is a text node, and it will prevent that newlines are stripped between elements. // Same thing happens between template and script tag. $string = str_replace('
', '
.', $string); $string = str_replace('
', '
.', $string); // Normalize newlines $string = str_replace(["\r\n", "\n\r", "\r"], "\n", $string); // VueJS files are valid HTML files, we will operate with the DOM here $dom = static::convertHtmlToDom($string); $script = static::extractScriptTag($string); // Parse the script part as a regular JS code if ($script) { $scriptLineNumber = $dom->getElementsByTagName('script')->item(0)->getLineNo(); static::getScriptTranslationsFromString( $script, $translations, $options, $scriptLineNumber - 1 ); } // Template part is parsed separately, all variables will be extracted // and handled as a regular JS code $template = $dom->getElementsByTagName('template')->item(0); if ($template) { static::getTemplateTranslations( $template, $translations, $options, $template->getLineNo() - 1 ); } } /** * Extracts script tag contents using regex instead of DOM operations. * If we parse using DOM, some contents may change, for example, tags within strings will be stripped * * @param $string * @return bool|string */ protected static function extractScriptTag($string) { if (preg_match('#<\s*?script\b[^>]*>(.*?)]*>#s', $string, $matches)) { return $matches[1]; } return ''; } /** * @param string $html * @return DOMDocument */ protected static function convertHtmlToDom($html) { $dom = new DOMDocument; libxml_use_internal_errors(true); // Prepend xml encoding so DOMDocument document handles UTF8 correctly. // Assuming that vue template files will not have any xml encoding tags, because duplicate tags may be ignored. $dom->loadHTML('' . $html); libxml_clear_errors(); return $dom; } /** * Extract translations from script part * * @param string $scriptContents Only script tag contents, not the whole template * @param Translations|Translations[] $translations One or multiple domain Translation objects * @param array $options * @param int $lineOffset Number of lines the script is offset in the vue template file * @throws Exception */ protected static function getScriptTranslationsFromString( $scriptContents, $translations, array $options = [], $lineOffset = 0 ) { /** @var FunctionsScanner $functions */ $functions = new static::$functionsScannerClass($scriptContents); $options['lineOffset'] = $lineOffset; $functions->saveGettextFunctions($translations, $options); } /** * Parse template to extract all translations (element content and dynamic element attributes) * * @param DOMNode $dom * @param Translations|Translations[] $translations One or multiple domain Translation objects * @param array $options * @param int $lineOffset Line number where the template part starts in the vue file * @throws Exception */ protected static function getTemplateTranslations( DOMNode $dom, $translations, array $options, $lineOffset = 0 ) { // Build a JS string from all template attribute expressions $fakeAttributeJs = static::getTemplateAttributeFakeJs($options, $dom); // 1 line offset is necessary because parent template element was ignored when converting to DOM static::getScriptTranslationsFromString($fakeAttributeJs, $translations, $options, $lineOffset); // Build a JS string from template element content expressions $fakeTemplateJs = static::getTemplateFakeJs($dom); static::getScriptTranslationsFromString($fakeTemplateJs, $translations, $options, $lineOffset); static::getTagTranslations($options, $dom, $translations); } /** * @param array $options * @param DOMNode $dom * @param Translations|Translations[] $translations */ protected static function getTagTranslations(array $options, DOMNode $dom, $translations) { // Since tag scanning does not support domains, we always use the first translation given $translations = is_array($translations) ? reset($translations) : $translations; $children = $dom->childNodes; for ($i = 0; $i < $children->length; $i++) { $node = $children->item($i); if (!($node instanceof DOMElement)) { continue; } $translatable = false; if (in_array($node->tagName, $options['tagNames'], true)) { $translatable = true; } $attrList = $node->attributes; $context = null; $plural = ""; $comment = null; for ($j = 0; $j < $attrList->length; $j++) { /** @var DOMAttr $domAttr */ $domAttr = $attrList->item($j); // Check if this is a dynamic vue attribute if (in_array($domAttr->name, $options['tagAttributes'])) { $translatable = true; } if (in_array($domAttr->name, $options['contextAttributes'])) { $context = $domAttr->value; } if (in_array($domAttr->name, $options['pluralAttributes'])) { $plural = $domAttr->value; } if (in_array($domAttr->name, $options['commentAttributes'])) { $comment = $domAttr->value; } } if ($translatable) { $translation = $translations->insert($context, trim($node->textContent), $plural); $translation->addReference($options['file'], $node->getLineNo()); if ($comment) { $translation->addExtractedComment($comment); } } if ($node->hasChildNodes()) { static::getTagTranslations($options, $node, $translations); } } } /** * Extract JS expressions from element attribute bindings (excluding text within elements) * For example:
skip element content
* * @param array $options * @param DOMNode $dom * @return string JS code */ protected static function getTemplateAttributeFakeJs(array $options, DOMNode $dom) { $expressionsByLine = static::getVueAttributeExpressions($options['attributePrefixes'], $dom); if (empty($expressionsByLine)) { return ''; } $maxLines = max(array_keys($expressionsByLine)); $fakeJs = ''; for ($line = 1; $line <= $maxLines; $line++) { if (isset($expressionsByLine[$line])) { $fakeJs .= implode("; ", $expressionsByLine[$line]); } $fakeJs .= "\n"; } return $fakeJs; } /** * Loop DOM element recursively and parse out all dynamic vue attributes which are basically JS expressions * * @param array $attributePrefixes List of attribute prefixes we parse as JS (may contain translations) * @param DOMNode $dom * @param array $expressionByLine [lineNumber => [jsExpression, ..], ..] * @return array [lineNumber => [jsExpression, ..], ..] */ protected static function getVueAttributeExpressions( array $attributePrefixes, DOMNode $dom, array &$expressionByLine = [] ) { $children = $dom->childNodes; for ($i = 0; $i < $children->length; $i++) { $node = $children->item($i); if (!($node instanceof DOMElement)) { continue; } $attrList = $node->attributes; for ($j = 0; $j < $attrList->length; $j++) { /** @var DOMAttr $domAttr */ $domAttr = $attrList->item($j); // Check if this is a dynamic vue attribute if (static::isAttributeMatching($domAttr->name, $attributePrefixes)) { $line = $domAttr->getLineNo(); $expressionByLine += [$line => []]; $expressionByLine[$line][] = $domAttr->value; } } if ($node->hasChildNodes()) { $expressionByLine = static::getVueAttributeExpressions($attributePrefixes, $node, $expressionByLine); } } return $expressionByLine; } /** * Check if this attribute name should be parsed for translations * * @param string $attributeName * @param string[] $attributePrefixes * @return bool */ protected static function isAttributeMatching($attributeName, $attributePrefixes) { foreach ($attributePrefixes as $prefix) { if (strpos($attributeName, $prefix) === 0) { return true; } } return false; } /** * Extract JS expressions from within template elements (excluding attributes) * For example:
{{__("extract element content")}}
* * @param DOMNode $dom * @return string JS code */ protected static function getTemplateFakeJs(DOMNode $dom) { $fakeJs = ''; $lines = explode("\n", $dom->textContent); // Build a fake JS file from template by extracting JS expressions within each template line foreach ($lines as $line) { $expressionMatched = static::parseOneTemplateLine($line); $fakeJs .= implode("; ", $expressionMatched) . "\n"; } return $fakeJs; } /** * Match JS expressions in a template line * * @param string $line * @return string[] */ protected static function parseOneTemplateLine($line) { $line = trim($line); if (!$line) { return []; } $regex = '#\{\{(.*?)\}\}#'; preg_match_all($regex, $line, $matches); $matched = array_map(function ($v) { return trim($v, '\'"{}'); }, $matches[1]); return $matched; } }