Crystal Lang

Crystal 1.0 Conference - July 8th 2021


In the last post I blogged about using gettext to internationalize strings in C#. However, using the Messages.T("Hello world") syntax in ASP.NET can be a little cumbersome. Luckily, ASP.NET controls can work as a solution for this issue.

Adding a simple control which we will name just “t” (and place it in namespace “t” as well) that handles translation of strings contained within can prevent the need of the code syntax in an ASP.NET. Therefore, we can write

<h2><t:t runat=”server”>My Title</t:t></h2>

instead of having to fall back to

<h2><%= Messages.T("My Title") %></h2>

The angle bracket syntax feels more consistent with the rest of the HTML, and is much more comfortable to write. The only drawback is the need of the run at server attribute which is unavoidable.

The code for this control is fairly straightforward:

public class t : Control
    string content;

    protected override void AddParsedSubObject(object obj)
        if (obj is LiteralControl)
            content = Strings.T(((LiteralControl)obj).Text);

    protected override void Render(HtmlTextWriter writer)


Extracting the content

The problem with this approach is that the xgettext tool only supports function calls, not tags. Therefore it is necessary to write a custom code crawler that extracts them. The easiest way to do this is having a tool run right before the invocation to xgettext and generate additional files that contain only the invocations to the Messages.T function, so they are actually recognised by xgettext.

The incredible simple regex for doing this is:


Well, maybe not that simple, but it does work. It must be formatted with the name of the tag (in our case, “t:t”) and it will extract whatever content it can find between this tags in the “text” capturing group.

We can then write a new file containing all of this captures, each of them enclosed in an invocation of Messages.T(), and this file will be easily handled by xgettext.

Handling standard C# calls

But this is not all. If you do use the <%= syntax in ASP.NET code and want xgettext to find calls to Messages.T() within them, you will find out that it throws a lot of errors. Evidently, xgettext was thought to parse standard C# code and not the one embedded within an ASP.NET page.

The best practice here is to also extract those strings into a separate well formed files. A nice regex to capture them is:


Remember to format this regex with the function name (Messages.T in this case), otherwise looking for a function named {0} will not return any matches whatsoever.

If you are interested in this processing, you may want to take a look at the tool we developed here.