Remember this diagram from a couple of posts ago:
After our previous exciting instalment some of this should start to make a bit more sense. In particular, that post explains why there’s a double-ended arrow going between the ASP.Net layer and the DTD files. In particular:
- If the DTD files don’t exist, or if the translations in the database change, the ASP.Net layer re-creates the files.
- Once they do exist, the ASP.Net layer is responsible for picking the appropriate DTD for the user’s language, and injecting it directly into the top of the XUL code.
Using our DTD entities in XUL, XHTML, SVG or any other XML-based language is easy:
<button id="btn1" label="&menu.file.save;" />
In each case the text appears literally as “&menu.file.save;” and never gets translated to “Save Changes”.
- Try to find a special hidden XUL element which maps the entity reference to the translated string.
- If that fails, try to find the translated string by parsing the DTD file directly.
- Finally, if that also fails, use some simple rules to guess at a human readable name for the entity reference.
<tfs_string id="menu.file.save" value="&menu.file.save;" >
In other words the ID is the entity reference but without the ampersand and semicolon (which means it doesn’t get translated when the XUL is initially parsed), whereas the Value is the full, translatable entity reference (which does get translated when the XUL is initially parsed). What we end up with when our XUL document is parsed is something like this:
<tfs_string id="menu.file.save" value="Save Changes" >
A simple CSS rule ensures that our <tfs_string> elements are hidden from view. The first approach our tfs_get_string() function takes is simply to search all the <tfs_string> elements for one with an ID that matches the supplied entity reference. If one is found, it returns the element’s Value as the translated string.
Ideally there would be no need to download another copy of the DTD – the code would simply parse the copy that’s included at the top of the XUL file. Unfortunately I’ve yet to find a way to get a handle to that copy – if anyone has any good ideas (that work with remote XUL), then please let me know. In the meantime we’re left with the browser occasionally pulling a copy of the DTD directly from the web server – hence that second arrow on the diagram.
If no translation is found in the DTD – which can happen for entity references that are dynamically generated, or for which there simply hasn’t been an entry created yet – the function makes a last attempt to return something human readable. It may not be in the correct language, but it’s better than the entity reference itself. To create this string it simply takes the text after the last full-stop and performs a few simple transformations to guess at a human readable form. So menu.file.save will end up as “Save”, whilst menu.file.save_changes would end up as “Save Changes”.
- Property files and DTD files are separate – that would mean maintaining two sets of overlapping translations and ensuring that the right bits got updated with every translation change.
- I wasn’t able to get stringBundles to work in remote XUL