Converting a Rails application from Gettext to I18n
Last week we had to convert our existing Rails application, which uses Gettext to the new I18n API in combination with the SimpleBackend. I personally never liked Gettext, there was simply not enough control over translations as PO/MO files are not native ruby or at least can be easily accessed by Ruby (like YAML files).
We therefore decided to switch to the brand new, not even released, I18n API. But now we had a serious problem, our code base isn’t small and all that code had to be converted in some way. We could do it by hand, but hey, that’s a lot of work and especially very error-prone. Some convertor had to be written. Here it is as a rails plugin: GettextToI18n.
What does the I18n convertor do?
It scrapes your whole application and searches for gettext calls, like this:
_("to be translated")
It will convert this gettext call to the newly I18n format:
I18n.t :message_id
Then it builds up a big hash containing all the the translations. We decided it was handy to use the scopes that are introduced in the new I18n api. So it stores the translations in the following format:
For models:
["model"]["model_name"]={:message_1 => "to be translated"}
For controller:
["controller"]["controller_name"]={:message_1 => "to be translated"}
After this hash of translations has been built up, the convertor writes it as a YAML file to: config/locales/template.yml.That’s all!
What’s supported?
It supports basic gettext calls. We have run it over our code base and it converts all gettext calls we use without any problem.
A normal gettext call
_("to be translated")
converts to:
I18n.t :message_0, :scope => [:txt, :controller, :controller_name])
A gettext call with variables
_("My name is %{name}" % {:name => "Jaap"})
converts to:
I18n.t :message_0, :name => "Jaap", :scope => [:txt, :controller, :controller_name])
A gettext call with variables that contain gettext calls
_("Click %{link} to go to the homepage" % {:link => link_to(_("Here"), root_path)})
converts to:
I18n.t :message_0, :link => link_to(I18n.t(:message_1, :scope => [:txt, :controller, :controller_name]), root_path), :scope => [:txt, :controller, :controller_name])
Installation
./script/plugin install git://github.com/japetheape/gettext_to_i18n.git
Usage
To convert your application:
rake gettext_to_i18n:transform
Please make sure you backup your complete application as it can screw things up.
Contribution
Please contribute to this plugin and make it better, as I won’t use it anymore, cause we are not going to convert another time (I think
). Things that has to be done are:
unnamed variables:
_("I play the %s" % "saxophone")
Go to the development location at github and fork this plugin!






September 17th, 2008 at 1:58 pm
Awesome. This looks like a great contribution. I love how the tools layer is already stacking up even though nothing has been released so far
I’ve listed this on the rails-i18n wiki: http://rails-i18n.org/wiki
Thanks a lot!
September 18th, 2008 at 2:10 pm
But why don’t you want to use Gibberish, or some other existing i18n plugin?
September 18th, 2008 at 3:33 pm
This tool is very interesting. I’m following with interest Rails I18n evolutions
but replacing the more concise
_(“to be translated”)
with
I18n.t :message_0, :scope => [:txt, :controller, :controller_name])
is not exactly what I would expect from a brand new API.
I’m still looking concrete advantages (performance, code clarity, etc…)
to left gettext and switch to new I18n API.
Anyway, thanks for your post.
Ciao.
– fabio
September 18th, 2008 at 6:26 pm
@Evgeny: that’s the advantage of the I18n API, we can now use this API and switch to whatever we plugin(that makes use of I18n) we want! Maybe if we find it usefull we switch to such a plugin, but for now the SimpleBackend is enough, as this is only a test for us.
September 18th, 2008 at 6:31 pm
@Fabio The call can be more simple, you can just use t(:message_1) without the I18n before it, and if you don’t use the scopes, you even don’t have to include them. So only t(:message_1) can be enough!
December 1st, 2008 at 5:16 am
Oops… I have to agree with Fabio: So far, the Rails improvements have felt just right. However, this time… It feels I’m going to stick with gettext for all of my projects :-/
Writing an application with just the labels as symbols, which have to be referenced from an external table, looks like the oldest and most awkward way to solve a problem. One of gettext’s biggest selling points, which has mad it a Unix-wide standard, is the ability to just write English applications – and use the English as the message identifiers themselves. Switching everything back to :ecannotparse (instead of a ‘cannot parse this shit’) just means more work to authors. And translators will also have a harder time, as they might not be technically skilled people, and will now have to open the to-be-translated files as well as the English ones to understand really what needs to be translated.
And of course, gettext degrades quite nicely – a missing string gets sent as English. Now… even the English translation might be missing. It is much preferrable for an i18n application to give an English string every now and then than to fail towards an internal-use-onl string.
December 3rd, 2008 at 5:38 pm
I understand the concerns you have with the new i18n API. However, I can argue against the gettext approach as well:
We tend to adjust the English strings from time to time, to correct grammar mistakes or create more fluent sentences. This is usually NOT done by the developers. Using gettext, this means that all the translations for that string are no longer valid, because the key has changed. This was a common problem for us when we were using gettext. Using i18n and translation keys, this problem doesn’t occur, and somebody other than the developers can adjust it.
Another problem is strings that are exactly the same in English but can mean different things, like “bank”. In other languages, these different meanings can be the cause of different translations.
To overcome your problems, you can make the identifiers a bit nicer than your example:
t(:cannot_parse_this_shit). This makes it much more clear for developers as well.December 3rd, 2008 at 5:39 pm
I forgot to mention that we have implemented a fallback to English if a string is not available in the current locale. So there is no fallback to the identifier, because we always have an English string available.
December 8th, 2008 at 10:20 am
Uglyness == i18n in rails 2.2
February 21st, 2009 at 2:55 pm
simply stay with gettext, its simpler
fast+threadsave+simple Gettext http://github.com/grosser/fast_gettext
2.2+ rails plugin for gettext and i18n http://github.com/grosser/gettext_i18n_rails
Example 2.3 application fast_gettext+i18n http://github.com/grosser/gettext_i18n_rails_example
May 30th, 2009 at 12:17 am
[...] by Administrator Fri, 29 May 2009 22:23:00 GMT ‘jaap’ wrote a rake task to convert your Rails application from using Gettext translation into the new I18n support, which [...]