UNPKG

@vendure/ngx-translate-extract

Version:

Extract strings from projects using ngx-translate

137 lines (136 loc) 5.93 kB
import { ASTWithSource, Binary, BindingPipe, Conditional, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, TmplAstElement as Element, TmplAstTemplate as Template, TmplAstText as Text, TmplAstIfBlock, TmplAstSwitchBlock, TmplAstForLoopBlock, TmplAstDeferredBlock, ParenthesizedExpression, } from '@angular/compiler'; import { getAST, getNodesFromSwitchBlockTmpl } from '../utils/ast-helpers.js'; import { TranslationCollection } from '../utils/translation.collection.js'; export const TRANSLATE_ATTR_NAMES = ['translate', 'marker']; export class DirectiveParser { extract(source, filePath) { let collection = new TranslationCollection(); const parsedTemplates = getAST(source, filePath).parsedTemplates; if (parsedTemplates.length === 0) { return collection; } const nodes = parsedTemplates.map((parsedTpl) => parsedTpl.nodes).flat(); const elements = this.getElementsWithTranslateAttribute(nodes); elements.forEach((element) => { const attribute = this.getAttribute(element, TRANSLATE_ATTR_NAMES); if (attribute?.value) { collection = collection.add(attribute.value, '', filePath); return; } const boundAttribute = this.getBoundAttribute(element, TRANSLATE_ATTR_NAMES); if (boundAttribute?.value) { this.getLiteralPrimitives(boundAttribute.value).forEach((literalPrimitive) => { if (!literalPrimitive.value) { return; } collection = collection.add(literalPrimitive.value.toString(), '', filePath); }); return; } const textNodes = this.getTextNodes(element); textNodes.forEach((textNode) => { collection = collection.add(textNode.value.trim(), '', filePath); }); }); return collection; } getElementsWithTranslateAttribute(nodes) { let elements = []; nodes.filter(this.isElementLike).forEach((element) => { if (this.hasAttributes(element, TRANSLATE_ATTR_NAMES)) { elements = [...elements, element]; } if (this.hasBoundAttribute(element, TRANSLATE_ATTR_NAMES)) { elements = [...elements, element]; } const childElements = this.getElementsWithTranslateAttribute(element.children); if (childElements.length) { elements = [...elements, ...childElements]; } }); nodes.filter(this.isBlockNode).forEach((node) => elements.push(...this.getElementsWithTranslateAttributeFromBlockNodes(node))); return elements; } getElementsWithTranslateAttributeFromBlockNodes(blockNode) { let blockChildren = blockNode.children ?? []; if (blockNode instanceof TmplAstIfBlock) { blockChildren = blockNode.branches.map((branch) => branch.children).flat(); } if (blockNode instanceof TmplAstSwitchBlock) { blockChildren = getNodesFromSwitchBlockTmpl(blockNode); } if (blockNode instanceof TmplAstForLoopBlock) { const emptyBlockChildren = blockNode.empty?.children ?? []; blockChildren.push(...emptyBlockChildren); } if (blockNode instanceof TmplAstDeferredBlock) { const placeholderBlockChildren = blockNode.placeholder?.children ?? []; const loadingBlockChildren = blockNode.loading?.children ?? []; const errorBlockChildren = blockNode.error?.children ?? []; blockChildren.push(...placeholderBlockChildren, ...loadingBlockChildren, ...errorBlockChildren); } return this.getElementsWithTranslateAttribute(blockChildren); } getTextNodes(element) { return element.children.filter(this.isText); } hasAttributes(element, name) { return this.getAttribute(element, name) !== undefined; } getAttribute(element, names) { return element.attributes.find((attribute) => names.includes(attribute.name)); } hasBoundAttribute(element, names) { return this.getBoundAttribute(element, names) !== undefined; } getBoundAttribute(element, names) { return element.inputs.find((input) => !input.keySpan.details?.startsWith('attr.') && names.includes(input.name)); } getLiteralPrimitives(exp) { if (exp instanceof LiteralPrimitive) { return [exp]; } let visit = []; if (exp instanceof Interpolation) { visit = exp.expressions; } else if (exp instanceof LiteralArray) { visit = exp.expressions; } else if (exp instanceof LiteralMap) { visit = exp.values; } else if (exp instanceof BindingPipe) { visit = [exp.exp]; } else if (exp instanceof Conditional) { visit = [exp.trueExp, exp.falseExp]; } else if (exp instanceof Binary) { visit = [exp.left, exp.right]; } else if (exp instanceof ASTWithSource) { visit = [exp.ast]; } else if (exp instanceof ParenthesizedExpression) { visit = [exp.expression]; } let results = []; visit.forEach((child) => { results = [...results, ...this.getLiteralPrimitives(child)]; }); return results; } isElementLike(node) { return node instanceof Element || node instanceof Template; } isBlockNode(node) { return (Object.hasOwn(node, 'nameSpan') && Object.hasOwn(node, 'sourceSpan') && Object.hasOwn(node, 'startSourceSpan') && Object.hasOwn(node, 'endSourceSpan')); } isText(node) { return node instanceof Text; } }