Analysis
We now have a much better routine. We've fixed a few bugs and ambiguities on the way. It's clearly much easier to read, because we've squeezed out the duplicate code. We replaced string addition with cheaper operations in several places. We've isolated our templates from the file system; instead, they use Readers to load themselves. We still have some cleanup to do: the variable names could be better (e.g., sb and br); also, the way the alternate identifier is formed is not obvious.
The new code embodies three basic things:
How a template is represented (currently a string)
How substitution is made
What codes and values to substitute ("%CODE%" and "%ALTCODE%")
The first two items are part of how a template works; the third is the "business logic" that tells how it's used. A stand-alone Template class would address the first two points. That was not obvious from the monster routine with which we started. This shows how our sense of the code smells can change over time: I'd now argue that an overdependence on the String type keeps us working at too low a level.
Pulling the template into a separate class would allow us to address performance even more, because we would be free to change the representation of templates. The current implementation scans the template (twice) to locate the patterns that need substitution. We might be able to preprocess a template to identify the potential substitutions. We could change the interface to the substitution process to take a list or mapping of substitutions, so we could do them all in one pass.