El-Cheapo Internationalisation in Pelican and Jinja

This blog is mainly in German, but in particular computer-related posts I'm writing in (some version of) English, as I expect they might be useful and/or interesting to quite a few people who don't speak German. On the English-language pages, I was always a bit unhappy that the footers (and to a certain degree, menu items) were in German.

Now, the standard way to “internationalize” programs is gettext, and sure enough, Pelican's template engine Jinja supports i18n with gettext. But my actual use case is mainly to replace large blocks (like the full footer, which has little markup but quite a lot of text), and having gettext-style message catalogues for those seemed unattractive to me.

Instead, I thought I could somehow work thorugh jinja computed includes, perhaps with something like:

{% import 'messages-'+article.lang+'.html' as messages %}

For each language I want to support, I'd then have one messages file (messages-en.html, messages-de.html, …), a bit like the message catalogues in gettext, but containing jinja markup.

The first question to answer was: What jinja markup? After a few experiments, it seems to me that using jinja blocks – which initially had seemed idiomatic to me – to write these messages files is at least tricky. After a while I rather settled for macros, such that the message file could contain something like:

{% macro basefoot() -%}
 <footer id="main-footer" class="clearfix">
   <ul class="nobull">
   <li><a href="/pages/wer.html">[Wer?/Kontakt]</a>
     | <a href="/archives.html">[Archiv]</a></li>
   ...
{%- endmacro }

and the application in the template would then be:

{% block footer %}
  {{ messages.basefoot() }}
{% endblock footer %}

This works nicely for internationalising large tree fragments and is not a lot worse than gettext for single strings (though I admit message catalogues are a bit nicer to work with when translating).

The one big problem was how to select the messages. For all the infrastructure pages (archive, tags, categories), I'm happy to use the default language (who looks at them, after all?). Hence,

{% import 'messages-'+DEFAULT_LANG+'.html' as messages %}

in the base.html template sounded like a good idea and went well enough. Then I thought I could use:

{% import 'messages-'+article.lang+'.html' as messages %}

in article.html and something similar in page.html. But alas: That won't work, because, as in python, jinja imports only happen once per run and are simply namespace operations when repeated.

I then tried to put the import statement into a named block, hoping that perhaps block overriding would suppress the default import. I suspect it wouldn't have because block content, I gather, is executed unconditionally, but never mind: it won't work anyway because (at least that's what I gather) blocks have python namespaces of their own, and hence imports in a block are not visible outside of it. Hence, once I put the import statement into a jinja block, my messages object is gone from where I need it.

So, I ended up with the following hack in base.html:

{# Yeah, hard-coding the various cases here *is* lame, but
   you can't override an import in a child templates as far
   as I can see, so this seems the least ugly option #}
{% if article %}
{% import 'messages-'+article.lang+'.html' as messages %}
{% elif page %}
{% import 'messages-'+page.lang+'.html' as messages %}
{% else %}
{% import 'messages-'+DEFAULT_LANG+'.html' as messages %}
{% endif %}

– this is ugly, breaks the encapsulation of the article and page templates, and it generally sucks, but for now it's good enough for me. I suppose the clean way to do this would be through a pelican extension providing a variable main_language (perhaps), computed in much the same way as this.

Let's see if this hack falls on my feet; for now, I like the way this works out and that most of the stuff on the article pages is now English on English pages and German on German pages.

Ceterum censeo: the more template languages I use for producing XML (and yes, I'm aware jinja has a much wider scope), the more I'm convinced the low adoption of stan is another instance of IT discarding a clearly superior design. What a pity it is that, for all I can see, all the popular templating engines work against the existing XML markup rather than, as in stan, with it.

Kategorie: edv

Letzte Ergänzungen