Haml + Gettext = automagic translation
I was rather sceptic to Haml once I have first time read about it. But after recently playing a while with it I can frankly express that it is simply outstanding template engine for Ruby. What I miss about Haml is some seamless integration with some i18n framework (gem).
So I decided to create Haml “mod” that uses GetText (FastGettext alternatively) to automagically translate static texts from Haml templates during precompilation stage. So something that you don’t see and you don’t need to worry about.
Normally you would use _(text) calls to translate string in your templates as below:
.item
You can see this is pretty ugly and unreadable. If you gonna have multi-language site you want to translate all of the static texts there anyway, without doing anything extra. So you would prefer to type:
.item
Edit
Delete
And have “Items”, “Edit”, “Delete” just translated, right? This is what my mod does. It translates all of those at precompilation stage which also is much better for your application performance, since GetText routines are called only once at precompilation stage. Here it goes.
haml_gettext.rb
# Haml gettext module providing gettext translation for all Haml plain text calls
#
# http://pastie.org/445295
# Inject _ gettext into plain text and tag plain text calls
super(_(text))
end
tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
nuke_inner_whitespace, action, value = super(line)
value = _(value) unless action || value.empty?
[tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
nuke_inner_whitespace, action, value]
end
end
All you need is to choose GetText or FastGettext and load my mod later on.
include FastGettext::Translation
You probably want to know the way to your gettext pot/po files out of the tempaltes. I have prepared here Rake tasks for you as well (based on work I found somewhere over Ruby blogs) to parse Haml templates extended to include static texts in the translation.
Rakefile
desc "Update pot/po files."
task :updatepo do
GetText.update_pofiles("rbigg", Dir.glob("{lib,views}/**/*.{rb,haml}") << "rbigg.rb", "rbigg 1.0.0", :po_root => 'locale')
end
haml_parser.rb
# Haml gettext parser module
#
# http://pastie.org/445297
# Overriden function that parses Haml tags
# Injects gettext call for plain text action.
tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
nuke_inner_whitespace, action, value = super(line)
@precompiled << "_(\"\")\n" unless action || value.empty?
[tag_name, attributes, attributes_hash, object_ref, nuke_outer_whitespace,
nuke_inner_whitespace, action, value]
end
# Overriden function that producted Haml plain text
# Injects gettext call for plain text action.
@precompiled << "_(\"\")\n"
end
end
# Haml gettext parser
module_function
File.extname(file) == ".haml"
end
haml = Haml::Engine.new(IO.readlines(file).join)
code = haml.precompiled.split(/$/)
GetText::RubyParser.parse_lines(file, code, ary)
end
end
GetText::RGetText.add_parser(HamlParser)
So this is it. Have fun with nice and clean templates that are translated on the fly
See & get more…
You can find more recent and complete code at my Sinatra Hat GitHub hosted Ruby gem project.
Tags: haml templates ruby performance i18n l10n multilanguage

been looking for this information. Thank you very much…
Hi. Tried to adapt your solution (used i18n however).
However found the next flaw with your hack: HAML compiles the template and includes translated texts into the compiled form as a plain text.
This has a consequence that template only adapts to a single locale. Meaning different users (or a single user changing its locale dynamically) will see the very first compiled localization, not different.
There are basically two solutions to this problem. First – it is possible to somehow tell Rails template system to compile separate template for each locale. However that requires monkeyhacking and not quite straightforward.
Another way (which I think I’ll choose) is not to return plain text within your push_plain/parse_tag, but return a push_script(“= _(‘#{text}’)”) instead and force HAML to dynamically translate stuff all the time.
Or, is there any other solutions?
If of any use, here: http://github.com/cail/haml_i18n is practically the same code but as a rails plugin and using i18n, not gettext.
Igor: Thanks for a note. Sorry I haven’t replied your previous post. Did you managed to do it for several locales? I believe the trick was to mod HAML a bit so the stored compiled templates are separate per each locale, so adding locale code prefix to compiled template method name should make it.
Adam, in my code on github you can check the solution. I took the second approach and instead forced HAML to keep translate(“phrase”) calls within the template. So there is a single template for the whole app locales, but the keys which can be translated are stored within the template not as a plain text but as a ‘translate’ method calls.
This gives some performance degradation of course, but its fine for my project so far.