UNPKG

wuchale

Version:

Protobuf-like i18n from plain code

183 lines (182 loc) 7.29 kB
// Shared logic between adapters for handling nested / mixed elements within elements / fragments import { IndexTracker, Message } from "../adapters.js"; import { commentPrefix, nonWhitespaceText } from "./index.js"; export class MixedVisitor { constructor(props) { Object.assign(this, props); } separatelyVisitChildren = (props) => { let hasTextChild = false; let hasNonTextChild = false; let heurStr = ''; let hasCommentDirectives = false; for (const child of props.children) { if (this.isText(child)) { const strContent = this.getTextContent(child); if (!strContent.trim()) { continue; } hasTextChild = true; heurStr += strContent; } else if (this.isComment(child)) { if (this.getCommentData(child).trim().startsWith(commentPrefix)) { hasCommentDirectives = true; } } else if (!this.leaveInPlace(child)) { hasNonTextChild = true; heurStr += `#`; } } heurStr = heurStr.trimEnd(); const msg = new Message(heurStr, this.fullHeuristicDetails({ scope: props.scope, element: props.element, attribute: props.attribute, })); const heurMsgType = this.checkHeuristic(msg); if (heurMsgType) { let hasCompoundText = hasTextChild && hasNonTextChild; if (props.inCompoundText || hasCompoundText && !hasCommentDirectives) { return [false, hasTextChild, hasCompoundText, heurMsgType, []]; } } // can't be extracted as one; visit each separately if markup const msgs = []; if (props.scope === 'markup') { for (const child of props.children) { msgs.push(...this.visitFunc(child, props.inCompoundText)); } } return [true, false, false, heurMsgType || 'message', msgs]; }; visit = (props) => { if (props.children.length === 0) { return []; } const [visitedSeparately, hasTextChild, hasCompoundText, heurMsgType, separateTxts] = this.separatelyVisitChildren(props); if (visitedSeparately) { return separateTxts; } let msgStr = ''; let iArg = 0; let iTag = 0; const lastChildEnd = this.getRange(props.children.slice(-1)[0]).end; const childrenNestedRanges = []; let hasTextDescendants = false; const msgs = []; const comments = []; for (const child of props.children) { if (this.isComment(child)) { continue; } const chRange = this.getRange(child); if (this.isText(child)) { const [startWh, trimmed, endWh] = nonWhitespaceText(this.getTextContent(child)); const msgInfo = new Message(trimmed, this.fullHeuristicDetails({ scope: props.scope }), props.commentDirectives.context); if (startWh && !msgStr.endsWith(' ')) { msgStr += ' '; } if (!trimmed) { // whitespace continue; } msgStr += msgInfo.msgStr; if (endWh) { msgStr += ' '; } this.mstr.remove(chRange.start, chRange.end); continue; } if (this.isExpression(child)) { msgs.push(...this.visitExpressionTag(child)); if (!hasCompoundText) { continue; } const placeholder = `{${iArg}}`; msgStr += placeholder; comments.push(`placeholder ${placeholder}: ${this.mstr.original.slice(chRange.start + 1, chRange.end - 1)}`); let moveStart = chRange.start; if (iArg > 0) { this.mstr.update(chRange.start, chRange.start + 1, ', '); } else { moveStart++; this.mstr.remove(chRange.start, chRange.start + 1); } this.mstr.move(moveStart, chRange.end - 1, lastChildEnd); this.mstr.remove(chRange.end - 1, chRange.end); iArg++; continue; } if (this.leaveInPlace(child)) { msgs.push(...this.visitFunc(child, this.canHaveChildren(child))); continue; } // elements, components and other things as well const canHaveChildren = this.canHaveChildren(child); const childMsgs = this.visitFunc(child, canHaveChildren); let nestedNeedsCtx = false; let chTxt = ''; for (const msgInfo of childMsgs) { if (canHaveChildren && msgInfo.details.scope === props.scope) { chTxt += msgInfo.msgStr[0]; hasTextDescendants = true; nestedNeedsCtx = true; } else { // attributes, blocks msgs.push(msgInfo); } } childrenNestedRanges.push([chRange.start, chRange.end, nestedNeedsCtx]); if (canHaveChildren && chTxt) { chTxt = `<${iTag}>${chTxt}</${iTag}>`; } else { // childless elements and everything else chTxt = `<${iTag}/>`; } iTag++; msgStr += chTxt; } msgStr = msgStr.trim(); if (!msgStr) { return msgs; } const msgInfo = new Message(msgStr, this.fullHeuristicDetails({ scope: props.scope }), props.commentDirectives.context); msgInfo.type = heurMsgType; msgInfo.comments = comments; if (hasTextChild || hasTextDescendants) { msgs.push(msgInfo); } else { return msgs; } if ((props.useComponent ?? true) && props.scope === 'markup' && iArg > 0 || childrenNestedRanges.length > 0) { this.wrapNested(msgInfo, iArg > 0, childrenNestedRanges, lastChildEnd); } else { // no need for component use let begin = '{'; let end = ')}'; if (props.inCompoundText) { begin += `${this.vars().rtTransCtx}(${this.vars().nestCtx}`; } else { begin += `${this.vars().rtTrans}(${this.index.get(msgInfo.toKey())}`; } if (iArg > 0) { begin += ', ['; end = ']' + end; } if (props.scope === 'attribute' && `'"`.includes(this.mstr.original[lastChildEnd])) { const firstChild = props.children[0]; this.mstr.remove(firstChild.start - 1, firstChild.start); this.mstr.remove(lastChildEnd, lastChildEnd + 1); } this.mstr.appendLeft(lastChildEnd, begin); this.mstr.appendRight(lastChildEnd, end); } return msgs; }; }