Examples
| Java | You should look at org.antlr.stringtemplate.test.TestStringTemplate.java, which contains many tests. |
|---|---|
| C# | You should look at Antlr.Stringtemplate.Tests.TestStringTemplate in the StringTemplateTests project, which contains many tests. |
| Python | You should look at stringtemplate.test.TestStringTemplate, which contains many tests. |
Fill-a-Table Example
The manner in which a template engine handles filling an HTML table with data often provides good insight into its programming and design strategy. It illustrates the interaction of the model and view via the controller. Using StringTemplate, the view may not access the model directly; rather the view is the passive recipient of data from the model.
First, imagine we have objects of type User that we will pull from a simulated database:
| Java | |
|---|---|
| C# | |
| Python |
Our database is just a static list:
| Java | |
|---|---|
| C# | |
| Python |
Here is my simple overall page design template, page.st:
The body attribute of page.st will be set to the following template users.inline.st by my web server infrastructure (part of the controller):
Again, it is the default attribute passed to a template when you apply that template to an attribute or attributes. it.name gets the name property, if it exists, from the it object. That is, StringTemplate uses reflection to call the Name property, then the get_Name() method then others including the getName() method on the incoming object. By using reflection, I avoid a type dependence between model and view.
Now, imagine the server and templates are set up to format data. My page definition is part of the controller that pulls data from the model (the database) and pushes into the view (the template). That is all the page definition should do--interpret the data and set some attributes in the view. The view only formats data and does no interpretation.
| Java | |
|---|---|
| C# | |
| Python |
Notice that the controller and model have no HTML in them at all and that the template has no code with side-effects or logic that can break the model-view separation. If you wanted to only see users with age < 30, you would filter the list in {{generateBody()} rather than alter your template. The template only displays information once the controller pulls the right data from the model.
Pushing factorization further, you could make a row.st component in order to reuse the table row HTML:
Then the user list template reduces to the more readable:
Naturally, you could go one step further and make another component for the entire table (putting it in file table.st):
then the body template would simply be:
Internationalization and localization
StringTemplate provides a simple and effective method for localizing web pages. The goal is to alter a page based upon the locale; that is, page strings or other content must change depending on a locale. This article not only illustrates how to make a pages change text depending on locale, it shows how the same site may easily have two different skins (site "looks").
This technique works well in practice for real sites. Schoolloop.com is a case in point. Click on the link that says "en espanol" to flip the site into Spanish mode. The exact same templates are used; all strings are pulled from a serious of resource bundles. There is no duplication of pages to change the strings.
Multiple skins
First let's look at multiple skins in order to show how templates are loaded for this example.
Multiple site looks are organized into their own directories. A StringTemplateGroup object rooted at that directory will load templates directly from there. In my example, I made two skins, blue and red. Here is how the group is loaded:
| Java | |
|---|---|
| C# |
Then, when you ask for an instance of a page, it pulls from whichever directory skin is set to:
| Java | |
|---|---|
| C# |
Here is page 1 in the blue skin:
and here is page 2:
For the red here is page 1:
and page 2:
The thing to note is that there is no text, just formatting in these page templates. Those strings from the strings attribute are used for all text that could change per locale.
Localizing template strings
For a more, please see http://www.cs.usfca.edu/~parrt/papers/i18n.pdf.
Once the code knows how to load templates, the locale must dictate which strings are displayed. The templates clearly have attribute references pulling from an object instance labelled strings (it could be a hash table).
| Java | Assuming that there are a few different strings and that they are stored in a Java properties file called [language-code].strings where [language-code] is the two-letter language code. Here is the en.strings file: and here is the fr.strings file: To load these per the current locale is pretty easy: The strings properties object is just a Hashtable so you can directly pass to StringTemplate templates as an attribute: To generate the page, just say: |
|---|---|
| C# | For this example, I'm assuming that I have a few different strings and that I'm storing them in resource files called Content.Strings.resx. The langauge specific alternatives are named Content.Strings[language-code].resx where language-code is the two-letter language code. Here are the name/value entries stored in Content.Strings.resx: and here are the entries in the Content.Strings.fr.resx file: To load these per the current locale is pretty easy: The strings properties object is just a wrapper around the ResourceManager so you can directly pass to StringTemplate templates as an attribute: Here's are the relevant bits of the wrapper class: And here's how this is passed directly to the StringTemplate: To generate the page, just say: |
The output will be (for the en locale):
If I change the locale to fr then without changes templates, the following is generated:
Source and compilation
| Java | Here is the code including the strings and skins: |
|---|---|
| C# | Here is the code including the strings and skins:
You naturally need ANTLR too; get it here. You can compile this example like this: |
| Python | Here is the code including the strings and skins: |
Summary
In summary, the key element to demonstrate here is that you do not have to duplicate all of your templates to change what they say. You can leave the formatting alone and, with a simple hashtable pulled from a data file, push in the proper strings per the locale. I also took the opportunity to show off just how easy it is to make multiple site skins.