use of com.google.template.soy.soytree.MsgPlaceholderNode in project closure-templates by google.
the class MsgUtils method buildMsgPartsForChildren.
// -----------------------------------------------------------------------------------------------
// Private helpers for building the list of message parts.
/**
* Builds the list of SoyMsgParts for all the children of a given parent node.
*
* @param parent Can be MsgNode, MsgPluralCaseNode, MsgPluralDefaultNode, MsgSelectCaseNode, or
* MsgSelectDefaultNode.
* @param msgNode The MsgNode containing 'parent'.
*/
private static ImmutableList<SoyMsgPart> buildMsgPartsForChildren(BlockNode parent, MsgNode msgNode) {
ImmutableList.Builder<SoyMsgPart> msgParts = ImmutableList.builder();
for (StandaloneNode child : parent.getChildren()) {
if (child instanceof RawTextNode) {
String rawText = ((RawTextNode) child).getRawText();
msgParts.add(SoyMsgRawTextPart.of(rawText));
} else if (child instanceof MsgPlaceholderNode) {
PlaceholderInfo placeholder = msgNode.getPlaceholder((MsgPlaceholderNode) child);
msgParts.add(new SoyMsgPlaceholderPart(placeholder.name(), placeholder.example()));
} else if (child instanceof MsgPluralNode) {
msgParts.add(buildMsgPartForPlural((MsgPluralNode) child, msgNode));
} else if (child instanceof MsgSelectNode) {
msgParts.add(buildMsgPartForSelect((MsgSelectNode) child, msgNode));
}
}
return msgParts.build();
}
use of com.google.template.soy.soytree.MsgPlaceholderNode in project closure-templates by google.
the class AssistantForHtmlMsgs method generateMsgGroupCode.
/**
* Returns a code chunk of idom instructions that output the contents of a translated message as
* HTML. For example:
*
* <pre>
* {msg desc="Says hello to a person."}Hello {$name}!{/msg}
* </pre>
*
* compiles to
*
* <pre>
* /** @desc Says hello to a person. *{@literal /}
* var MSG_EXTERNAL_6936162475751860807 = goog.getMsg(
* 'Hello {$name}!',
* {'name': '\u00010\u0001'});
* var lastIndex_1153 = 0, partRe_1153 = /\x01\d+\x01/g, match_1153;
* do {
* match_1153 = partRe_1153.exec(MSG_EXTERNAL_6936162475751860807) || undefined;
* incrementalDom.text(goog.string.unescapeEntities(
* MSG_EXTERNAL_6936162475751860807.substring(
* lastIndex_1153, match_1153 && match_1153.index)));
* lastIndex_1153 = partRe_1153.lastIndex;
* switch (match_1153 && match_1153[0]) {
* case '\u00010\u0001':
* var dyn8 = opt_data.name;
* if (typeof dyn8 == 'function') dyn8();
* else if (dyn8 != null) incrementalDom.text(dyn8);
* break;
* }
* } while (match_1153);
* </pre>
*
* Each interpolated MsgPlaceholderNode (either for HTML tags or for print statements) compiles to
* a separate {@code case} statement.
*/
CodeChunk generateMsgGroupCode(MsgFallbackGroupNode node) {
Preconditions.checkState(placeholderNames.isEmpty(), "This class is not reusable.");
// Non-HTML {msg}s should be extracted into LetContentNodes and handled by jssrc.
Preconditions.checkArgument(node.getHtmlContext() == HtmlContext.HTML_PCDATA, "AssistantForHtmlMsgs is only for HTML {msg}s.");
// All of these helper variables must have uniquely-suffixed names because {msg}s can be nested.
// It'd be nice to move this codegen to a Soy template...
// The raw translated text, with placeholder placeholders.
CodeChunk.WithValue translationVar = super.generateMsgGroupVariable(node);
// If there are no placeholders, we don't need anything special (but we still need to unescape).
if (placeholderNames.isEmpty()) {
CodeChunk.WithValue unescape = GOOG_STRING_UNESCAPE_ENTITIES.call(translationVar);
return INCREMENTAL_DOM_TEXT.call(unescape);
}
// it into a fresh variable.
if (!translationVar.isCheap()) {
translationVar = translationContext.codeGenerator().declarationBuilder().setRhs(translationVar).build().ref();
}
// Declare everything.
// The mutable (tracking index of last match) regex to find the placeholder placeholders.
VariableDeclaration regexVar = VariableDeclaration.builder("partRe_" + node.getId()).setRhs(CodeChunk.regexLiteral(PLACEHOLDER_REGEX)).build();
// The current placeholder from the regex.
VariableDeclaration matchVar = VariableDeclaration.builder("match_" + node.getId()).build();
// The index of the end of the previous placeholder, where the next raw text run starts.
VariableDeclaration lastIndexVar = VariableDeclaration.builder("lastIndex_" + node.getId()).setRhs(CodeChunk.number(0)).build();
List<CodeChunk> doBody = new ArrayList<>();
// match_XXX = partRe_XXX.exec(MSG_EXTERNAL_XXX) || undefined;
doBody.add(matchVar.ref().assign(regexVar.ref().dotAccess("exec").call(translationVar).or(CodeChunk.id("undefined"), translationContext.codeGenerator())));
// Replace null with undefined. This is necessary to make substring() treat falsy as an omitted
// parameter, so that it goes until the end of the string. Otherwise, the non-numeric parameter
// would be coerced to zero.
// Emit the (possibly-empty) run of raw text since the last placeholder, until this placeholder,
// or until the end of the source string.
CodeChunk.WithValue endIndex = matchVar.ref().and(matchVar.ref().dotAccess("index"), translationContext.codeGenerator());
CodeChunk.WithValue unescape = GOOG_STRING_UNESCAPE_ENTITIES.call(translationVar.dotAccess("substring").call(lastIndexVar.ref(), endIndex));
doBody.add(INCREMENTAL_DOM_TEXT.call(unescape));
doBody.add(lastIndexVar.ref().assign(regexVar.ref().dotAccess("lastIndex")));
// switch (match_XXX && match_XXX[0]) { ... }
SwitchBuilder switchBuilder = CodeChunk.switch_(matchVar.ref().and(matchVar.ref().bracketAccess(CodeChunk.number(0)), translationContext.codeGenerator()));
for (Map.Entry<String, MsgPlaceholderNode> ph : placeholderNames.entrySet()) {
switchBuilder.case_(CodeChunk.stringLiteral(ph.getKey()), master.visitForUseByAssistantsAsCodeChunk(ph.getValue()));
}
doBody.add(switchBuilder.build());
return CodeChunk.statements(CodeChunk.statements(translationVar.initialStatements()), regexVar, lastIndexVar, matchVar, DoWhile.builder().setCondition(matchVar.ref()).setBody(CodeChunk.statements(doBody)).build());
}
use of com.google.template.soy.soytree.MsgPlaceholderNode in project closure-templates by google.
the class MsgFuncGenerator method collectVarNameListAndToPyExprMap.
/**
* Private helper to process and collect all variables used within this msg node for code
* generation.
*
* @return A Map populated with all the variables used with in this message node, using {@link
* MsgPlaceholderInitialNode#genBasePhName}.
*/
private Map<PyExpr, PyExpr> collectVarNameListAndToPyExprMap() {
Map<PyExpr, PyExpr> nodePyVarToPyExprMap = new LinkedHashMap<>();
for (Map.Entry<String, MsgSubstUnitNode> entry : msgNode.getVarNameToRepNodeMap().entrySet()) {
MsgSubstUnitNode substUnitNode = entry.getValue();
PyExpr substPyExpr = null;
if (substUnitNode instanceof MsgPlaceholderNode) {
SoyNode phInitialNode = ((AbstractParentSoyNode<?>) substUnitNode).getChild(0);
if (phInitialNode instanceof PrintNode || phInitialNode instanceof CallNode || phInitialNode instanceof RawTextNode) {
substPyExpr = PyExprUtils.concatPyExprs(genPyExprsVisitor.exec(phInitialNode)).toPyString();
}
// when the placeholder is generated by HTML tags
if (phInitialNode instanceof MsgHtmlTagNode) {
substPyExpr = PyExprUtils.concatPyExprs(genPyExprsVisitor.execOnChildren((ParentSoyNode<?>) phInitialNode)).toPyString();
}
} else if (substUnitNode instanceof MsgPluralNode) {
// Translates {@link MsgPluralNode#pluralExpr} into a Python lookup expression.
// Note that {@code pluralExpr} represents the soy expression of the {@code plural} attr,
// i.e. the {@code $numDrafts} in {@code {plural $numDrafts}...{/plural}}.
substPyExpr = translateToPyExprVisitor.exec(((MsgPluralNode) substUnitNode).getExpr());
} else if (substUnitNode instanceof MsgSelectNode) {
substPyExpr = translateToPyExprVisitor.exec(((MsgSelectNode) substUnitNode).getExpr());
}
if (substPyExpr != null) {
nodePyVarToPyExprMap.put(new PyStringExpr("'" + entry.getKey() + "'"), substPyExpr);
}
}
return nodePyVarToPyExprMap;
}
use of com.google.template.soy.soytree.MsgPlaceholderNode in project closure-templates by google.
the class TemplateParserTest method testParseMsgStmt.
@Test
public void testParseMsgStmt() throws Exception {
String templateBody = "{@param usedMb :?}{@param learnMoreUrl :?}\n" + " {msg desc=\"Tells user's quota usage.\"}\n" + " You're currently using {$usedMb} MB of your quota.{sp}\n" + " <a href=\"{$learnMoreUrl}\">Learn more</A>\n" + " <br /><br />\n" + " {/msg}\n" + " {msg meaning=\"noun\" desc=\"\" hidden=\"true\"}Archive{/msg}\n" + " {msg meaning=\"noun\" desc=\"The archive (noun).\"}Archive{/msg}\n" + " {msg meaning=\"verb\" desc=\"\"}Archive{/msg}\n" + " {msg desc=\"\"}Archive{/msg}\n";
List<StandaloneNode> nodes = parseTemplateContent(templateBody, FAIL).getChildren();
assertEquals(5, nodes.size());
MsgNode mn0 = ((MsgFallbackGroupNode) nodes.get(0)).getMsg();
assertEquals("Tells user's quota usage.", mn0.getDesc());
assertEquals(null, mn0.getMeaning());
assertEquals(false, mn0.isHidden());
assertEquals(8, mn0.numChildren());
assertEquals("You're currently using ", ((RawTextNode) mn0.getChild(0)).getRawText());
MsgPlaceholderNode mpn1 = (MsgPlaceholderNode) mn0.getChild(1);
assertEquals("$usedMb", ((PrintNode) mpn1.getChild(0)).getExpr().toSourceString());
assertEquals(" MB of your quota. ", ((RawTextNode) mn0.getChild(2)).getRawText());
MsgPlaceholderNode mpn3 = (MsgPlaceholderNode) mn0.getChild(3);
MsgHtmlTagNode mhtn3 = (MsgHtmlTagNode) mpn3.getChild(0);
assertEquals("a", mhtn3.getLcTagName());
assertEquals("START_LINK", mhtn3.genBasePhName());
assertEquals("<a href=\"{$learnMoreUrl}\">", mhtn3.toSourceString());
assertEquals(3, mhtn3.numChildren());
assertEquals("<a href=\"", ((RawTextNode) mhtn3.getChild(0)).getRawText());
assertEquals("$learnMoreUrl", ((PrintNode) mhtn3.getChild(1)).getExpr().toSourceString());
assertEquals("\">", ((RawTextNode) mhtn3.getChild(2)).getRawText());
assertEquals("Learn more", ((RawTextNode) mn0.getChild(4)).getRawText());
MsgPlaceholderNode mpn5 = (MsgPlaceholderNode) mn0.getChild(5);
MsgHtmlTagNode mhtn5 = (MsgHtmlTagNode) mpn5.getChild(0);
assertEquals("/a", mhtn5.getLcTagName());
assertEquals("END_LINK", mhtn5.genBasePhName());
assertEquals("</A>", mhtn5.toSourceString());
MsgPlaceholderNode mpn6 = (MsgPlaceholderNode) mn0.getChild(6);
MsgHtmlTagNode mhtn6 = (MsgHtmlTagNode) mpn6.getChild(0);
assertEquals("BREAK", mhtn6.genBasePhName());
assertTrue(mpn6.shouldUseSameVarNameAs((MsgPlaceholderNode) mn0.getChild(7)));
assertFalse(mpn6.shouldUseSameVarNameAs(mpn5));
assertFalse(mpn5.shouldUseSameVarNameAs(mpn3));
MsgFallbackGroupNode mfgn1 = (MsgFallbackGroupNode) nodes.get(1);
assertEquals("{msg meaning=\"noun\" desc=\"\" hidden=\"true\"}Archive{/msg}", mfgn1.toSourceString());
MsgNode mn1 = mfgn1.getMsg();
assertEquals("", mn1.getDesc());
assertEquals("noun", mn1.getMeaning());
assertEquals(true, mn1.isHidden());
assertEquals(1, mn1.numChildren());
assertEquals("Archive", ((RawTextNode) mn1.getChild(0)).getRawText());
}
use of com.google.template.soy.soytree.MsgPlaceholderNode in project closure-templates by google.
the class TemplateParserTest method testParseMsgStmtWithNestedSelects.
@Test
public void testParseMsgStmtWithNestedSelects() throws Exception {
String templateBody = "{@param gender1 : ?}{@param gender2: ?}{@param person1 : ?}{@param person2: ?}\n" + "{msg desc=\"A sample nested message\"}\n" + " {select $gender1}\n" + " {case 'female'}\n" + " {select $gender2}\n" + " {case 'female'}{$person1} added {$person2} and her friends to her circle.\n" + " {default}{$person1} added {$person2} and his friends to her circle.\n" + " {/select}\n" + " {default}\n" + " {select $gender2}\n" + " {case 'female'}{$person1} put {$person2} and her friends to his circle.\n" + " {default}{$person1} put {$person2} and his friends to his circle.\n" + " {/select}\n" + " {/select}\n" + "{/msg}\n";
List<StandaloneNode> nodes = parseTemplateContent(templateBody, FAIL).getChildren();
assertEquals(1, nodes.size());
MsgNode mn = ((MsgFallbackGroupNode) nodes.get(0)).getChild(0);
assertEquals(1, mn.numChildren());
assertEquals("A sample nested message", mn.getDesc());
// Outer select
MsgSelectNode sn = (MsgSelectNode) mn.getChild(0);
assertEquals("$gender1", sn.getExpr().toSourceString());
// female and default
assertEquals(2, sn.numChildren());
// Outer select: Case 'female'
MsgSelectCaseNode cnf = (MsgSelectCaseNode) sn.getChild(0);
assertEquals("female", cnf.getCaseValue());
// Another select
assertEquals(1, cnf.numChildren());
// Outer select: Case 'female': Inner select
MsgSelectNode sn2 = (MsgSelectNode) cnf.getChild(0);
assertEquals("$gender2", sn2.getExpr().toSourceString());
// female and default
assertEquals(2, sn2.numChildren());
// Outer select: Case 'female': Inner select: Case 'female'
MsgSelectCaseNode cnf2 = (MsgSelectCaseNode) sn2.getChild(0);
assertEquals("female", cnf2.getCaseValue());
assertEquals(4, cnf2.numChildren());
// Outer select: Case 'female': Inner select: Case 'female': Placeholder $person1
MsgPlaceholderNode phn1 = (MsgPlaceholderNode) cnf2.getChild(0);
assertEquals("{$person1}", phn1.toSourceString());
// Outer select: Case 'female': Inner select: Case 'female': RawText
RawTextNode rtn1 = (RawTextNode) cnf2.getChild(1);
assertEquals(" added ", rtn1.getRawText());
// Outer select: Case 'female': Inner select: Case 'female': Placeholder $person2
MsgPlaceholderNode phn2 = (MsgPlaceholderNode) cnf2.getChild(2);
assertEquals("{$person2}", phn2.toSourceString());
// Outer select: Case 'female': Inner select: Case 'female': RawText
RawTextNode rtn2 = (RawTextNode) cnf2.getChild(3);
assertEquals(" and her friends to her circle.", rtn2.getRawText());
// Outer select: Case 'female': Inner select: Default
MsgSelectDefaultNode dn2 = (MsgSelectDefaultNode) sn2.getChild(1);
assertEquals(4, dn2.numChildren());
// Outer select: Case 'female': Inner select: Default: Placeholder $person1
MsgPlaceholderNode phn21 = (MsgPlaceholderNode) dn2.getChild(0);
assertEquals("{$person1}", phn21.toSourceString());
// Outer select: Case 'female': Inner select: Default: RawText
RawTextNode rtn21 = (RawTextNode) dn2.getChild(1);
assertEquals(" added ", rtn21.getRawText());
// Outer select: Case 'female': Inner select: Default: Placeholder $person2
MsgPlaceholderNode phn22 = (MsgPlaceholderNode) dn2.getChild(2);
assertEquals("{$person2}", phn22.toSourceString());
// Outer select: Case 'female': Inner select: Default: RawText
RawTextNode rtn22 = (RawTextNode) dn2.getChild(3);
assertEquals(" and his friends to her circle.", rtn22.getRawText());
// Outer select: Default
MsgSelectDefaultNode dn = (MsgSelectDefaultNode) sn.getChild(1);
// Another select
assertEquals(1, dn.numChildren());
// Outer select: Default: Inner select
MsgSelectNode sn3 = (MsgSelectNode) dn.getChild(0);
assertEquals("$gender2", sn3.getExpr().toSourceString());
// female and default
assertEquals(2, sn3.numChildren());
// Outer select: Default: Inner select: Case 'female'
MsgSelectCaseNode cnf3 = (MsgSelectCaseNode) sn3.getChild(0);
assertEquals("female", cnf3.getCaseValue());
assertEquals(4, cnf3.numChildren());
// Outer select: Default: Inner select: Case 'female': Placeholder $person1
MsgPlaceholderNode phn3 = (MsgPlaceholderNode) cnf3.getChild(0);
assertEquals("{$person1}", phn3.toSourceString());
// Outer select: Default: Inner select: Case 'female': RawText
RawTextNode rtn3 = (RawTextNode) cnf3.getChild(1);
assertEquals(" put ", rtn3.getRawText());
// Outer select: Default: Inner select: Case 'female': Placeholder $person2
MsgPlaceholderNode phn4 = (MsgPlaceholderNode) cnf3.getChild(2);
assertEquals("{$person2}", phn4.toSourceString());
// Outer select: Default: Inner select: Case 'female': RawText
RawTextNode rtn4 = (RawTextNode) cnf3.getChild(3);
assertEquals(" and her friends to his circle.", rtn4.getRawText());
// Outer select: Default: Inner select: Default
MsgSelectDefaultNode dn3 = (MsgSelectDefaultNode) sn3.getChild(1);
assertEquals(4, dn3.numChildren());
// Outer select: Default: Inner select: Default: Placeholder $person1
MsgPlaceholderNode phn5 = (MsgPlaceholderNode) dn3.getChild(0);
assertEquals("{$person1}", phn5.toSourceString());
// Outer select: Default: Inner select: Default: RawText
RawTextNode rtn5 = (RawTextNode) dn3.getChild(1);
assertEquals(" put ", rtn5.getRawText());
// Outer select: Default: Inner select: Default: Placeholder $person2
MsgPlaceholderNode phn6 = (MsgPlaceholderNode) dn3.getChild(2);
assertEquals("{$person2}", phn6.toSourceString());
// Outer select: Default: Inner select: Default: RawText
RawTextNode rtn6 = (RawTextNode) dn3.getChild(3);
assertEquals(" and his friends to his circle.", rtn6.getRawText());
}
Aggregations