docs: 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.");
  
  
      // The raw translated text, with placeholder placeholders.
      Expression translationVar = super.generateMsgGroupVariable(node);
  
      // If there are no placeholders, we don't need anything special (but we still need to unescape).
      if (placeholderNames.isEmpty()) {
        Expression unescape = GOOG_STRING_UNESCAPE_ENTITIES.call(translationVar);
        return INCREMENTAL_DOM_TEXT.call(unescape);
      }
  
      // The translationVar may be non-trivial if escaping directives are applied to it, if so bounce
      // it into a fresh variable.
      if (!translationVar.isCheap()) {
        translationVar =
            translationContext
                .codeGenerator()
                .declarationBuilder()
                .setRhs(translationVar)
                .build()
                .ref();
      }
  
      // We assume at this point that the statics array has been populated with something like
      // [['hi', '\\x01\foo'], ['ho', '\\x02\foo']]
  
      // Consider this block the body of the for loop
      ImmutableList.Builder<Statement> body = ImmutableList.builder();
      String itemId = "i" + node.getId();
      // Get a handle to the ith item of the static array
      Expression item = Expression.id("i" + node.getId());
  
      // The first element contains some text that we call using itext
      body.add(
          item.bracketAccess(Expression.number(0))
              .and(
                  INCREMENTAL_DOM_TEXT.call(item.bracketAccess(Expression.number(0))),
                  translationContext.codeGenerator())
              .asStatement());
  
      // The second element contains a placeholder string. We then execute a switch statement
      // to decide which branch to execute.
      SwitchBuilder switchBuilder = Statement.switchValue(item.bracketAccess(Expression.number(1)));
      for (Map.Entry<String, MsgPlaceholderNode> ph : placeholderNames.entrySet()) {
        Statement value = idomMaster.visitForUseByAssistantsAsCodeChunk(ph.getValue());
        MsgPlaceholderNode phNode = ph.getValue();
        if (phNode.getParent() instanceof VeLogNode) {
          VeLogNode parent = (VeLogNode) phNode.getParent();
          if (parent.getChild(0) == phNode) {
            GenIncrementalDomCodeVisitor.VeLogStateHolder state = idomMaster.openVeLogNode(parent);
            // It is a compiler failure to have a logOnly in a message node.
            Preconditions.checkState(state.logOnlyConditional == null);
            value = Statement.of(state.enterStatement, value);
          }
          if (parent.getChild(parent.getChildren().size() - 1) == phNode) {
            value = Statement.of(value, idomMaster.exitVeLogNode(parent, null));
          }
        }
        switchBuilder.addCase(Expression.stringLiteral(ph.getKey()), value);
      }
      body.add(switchBuilder.build());
      // End of for loop
  
      Statement loop =
          forOf(
              itemId,
              Expression.id(staticDecl).bracketAccess(translationVar),
              Statement.of(body.build()));
  
      return Statement.of(staticsInitializer(node, translationVar), loop);
    }