As soon as you start introducing flow control constructs (if, loop... ) to your template language, you lose the ability to apply a template simply by applying a search-and-replace over your variables. You need to start parsing the template to extract the parts that are dependent on a condition, and re-insert them separately should the need happen.
What you will probably end up doing is apply an initial parsing step that turns:
Template "main" : FOO <if a> BAR </if> QUX
Into:
Template "main" : FOO {temp-if-a} QUX
Template "temp" : BAR
Then, if a is true, you would apply template temp and store it into variable {temp-if-a} while rendering template main. If a is false, you would provide no value for {temp-if-a}.
The other control flow structures can be similarly implemented with this extract-apply independently-replace sequence, including nested ones (just have your template application algorithm work recursively).