Finally getting GetText to work with Rails 2.2

I18n layer, introduced in Rails 2.2, is said to be “the simplest thing that ever could work”. Too bad they didn’t go for “the simplest thing that could be practical to use”. These two can be very far apart. I have a hard time imagining a real-world app using only bare Rails’ I18n engine.

Another unpleasant thing is that Rails 2.2 broke GetText compatibility, which prevents many projects from migrating to Rails 2.2 or later versions.

Well, not anymore. Thanks to clever guys Masao Mutoh and Michael Grosser, it’s finally possible to get Rails 2.2 running with GetText. Read on for details.

This post is only a compilation of information that can be found at http://github.com/mutoh/gettext/tree/master, http://github.com/grosser/fast_gettext/tree/master, http://github.com/grosser/gettext_i18n_rails/tree/master and some other pages. These are by all means excellent pages, yet at some places imprecise or contradicting each other. It took me quite some time to make it work together, hence this post.

Warning: I assume basic knowledge of how GetText is used in Rails project, what are .po and .mo files etc. This post is not a tutorial on using GetText in general. You can find excellent tutorials by Masao Mutoh at http://www.yotabanana.com/hiki/ruby-gettext-howto-rails.html.

Installing

GNU gettext

First, install a version of GNU gettext in a version suitable for your machine (Mutoh’s page says version 0.10.35 or later, at this moment the current stable version is 0.17 and for MSWin you can use precompiled version 0.14.4 from GnuWin32 project). Make sure gettext’s bin directory is added to your $PATH!

Racc

Racc is Ruby yacc, i.e. a parser generator. I’m not sure if it’s necessary, so you can skip it at your own risk. If you decide to install it, download http://i.loveruby.net/archive/racc/racc-1.4.5-all.tar.gz, untar and follow instructions in README.

Gems

Add following to your config/environment.rb:

Rails::Initializer.run do |config|
  #...
  config.gem "locale"
  config.gem "grosser-gettext",
     :version => '2.0.0', :lib => 'gettext', :source => "http://gems.github.com"
  config.gem "grosser-fast_gettext",
     :version => '0.2.8', :lib => 'fast_gettext', :source => "http://gems.github.com"
end

and run

rake gems:install

If anything fails, try installing manually, i.e. with

sudo gem install ...

Warning: installing GetText 2.0 gem breaks earlier versions of GetText gem you might have installed. You can probably get the following to work with GetText 1.93, but I didn’t try it.

gettext_i18n_rails plugin

Install it with

script/plugin install git://github.com/grosser/gettext_i18n_rails.git

Configuring

Initialization

Create a new file config/initializers/fast_gettext.rb and put this in it:

FastGettext.add_text_domain 'app',
    :path => File.join(RAILS_ROOT, 'locale')

(or stick it at the end of config/environment.rb if you’re lazy).

RAILS_ROOT/locale is where GetText assumes to find its .po and .mo files and it’s better to leave it this way. The text domain name ('app' above) can be any other name — preferably the name of your project — but remember to change it in the following examples, too.

ApplicationController

Add this to your ApplicationController:

  include FastGettext::Translation
  before_filter :set_gettext_locale
protected
  def set_gettext_locale
    FastGettext.text_domain = 'app'
    FastGettext.available_locales = ['en','pl'] #all you want to allow
    super
  end

Line 1 makes all the GetText functions (the _, n_, N_ etc.) available to your controllers. Line 2 installs a filter to setup FastGettext for each request. Remember to edit the list of available locales in line 6. Finally, the super call in line sets the FastGettext.locale based on:

  • params[:locale],
  • session[:locale],
  • cookies[:locale],
  • request header HTTP_ACCEPT_LANGUAGE (in that order)

Once determined, locale is saved in session[:locale].

ApplicationHelper

In order to have GetText functions available in your helpers and views, add this to app/helpers/application_helper.rb:

  include FastGettext::Translation

TestHelper/SpecHelper

Add this to your test/test_helper.rb (or spec/spec_helper.rb) to make your tests/specs work:

FastGettext.text_domain = 'app'
FastGettext.available_locales = ['en','pl'] #all you want to allow

Haml support

GetText does not support Haml ‘out of the box’, so if you happen to use Haml, there’s one more thing to configure (otherwise you can skip this step). Create a new file lib/haml_parser.rb and put this in it:

require 'gettext/utils'
require 'gettext/tools/rgettext'

begin
  require "#{RAILS_ROOT}/vendor/plugins/haml/lib/haml"
rescue LoadError
  require 'haml'  # From gem
end

module HamlParser
  module_function

  def target?(file)
    File.extname(file) == '.haml'
  end

  def parse(file, ary = [])
    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)

Now GetText is able to parse Haml, too!

Running

You’re finally ready to test if your configuration really works. Add something like:

<%= _('Hello World') %>

in any of your views. Then run:

rake gettext:find

If you get an error

Could not find RubyGem gettext (>= 2.0.0)

edit vendor/plugins/gettext_i18n_rails/tasks/gettext_rails_i18n.rake and comment out the line 3 that says

gem 'gettext', '>=2.0.0'

(you already have gettext gem, but it’s called grosser-gettext).

On successful run you should get a message that GetText created a template file locales/app.pot. Now create subdirectories in locales/ for each locale you want to support and copy this file into it, changing the extension to .po. So, for English and Polish, you would do:

$ mkdir en 
$ cp app.pot en/app.po 
$ mkdir pl 
$ cp app.po pl/app.po

After that, edit the .po files and provide translations of your text in respective languages (I assume you’re familiar with .po file format). Then:

$ rake gettext:pack 
locale/en/app.po -> locale/en/LC_MESSAGES/app.mo ... Done. 
locale/pl/app.po -> locale/pl/LC_MESSAGES/app.mo ... Done.

As a side note, you might want to add locale/*/LC_MESSAGES/*.mo files to .gitignore or svn:ignore since they are generated by GetText but be sure to add any .po files you create to your code repository.

Now, restart your web server (changes in .mo files are not automatically reloaded) and navigate to the page where you used the _() function. You should see your text translated to your default locale. If your application does not yet feature any elegant way to switch locale, add ?locale=.. to URL and reload. Your text should now be translated to the new language.

That’s all, folks. Hope it works for you. If you find anything missing or incorrect, please let me know.


14 responses to “Finally getting GetText to work with Rails 2.2

  • pragmatig

    Great tutorial!

    I added a task to gettext_i18n_rails, that will install gettext 2.0from mutohs repo, simple as rake gettext:install.

    And it is no longer necessary to use it, since it will also work with gettext 1.93(although some minor features are missing when creating po files).

    I would not recommend to have the config.gem “locale”
    and config.gem “grosser-gettext”, in environment since this will add an additional 3mb ram. Gettext features are only used while creating po and mo files.

    Also the inclusion of FastGettext::Translation is not necessary because it will be included into helper/view/controller/AR by default.

    Again, great tutorial with a lot of depth, i need to improve my documentation skills :)

  • Scolas

    I’ll try it!!
    If it works can i translate this tutorial in italian on my blog?

  • pragmatig

    Haml parser(that finds more translations) is now integrated in gettext_i18n_rails, so no more need to add it.

  • pragmatig

    so the line should read
    config.gem “locale”, :lib=>false
    config.gem “grosser-gettext”,:version => ‘2.0.0’, :lib => false, :source => “http://gems.github.com”

    this ensures they are installed, but will not load them since they are only required for translation generation and would only add needless ram/chance-for-problems otherwise

  • developer

    Seems like a big waste, when you can use the I18N engine provided by rails. What features are you adding with GetText?

  • szeryf

    @developer – many critical features:

    * backwards compatibility with existing projects – if you have projects using GetText already, now you can migrate them to Rails 2.2

    * tools for maintaining translation files – the original I18N with YAML files that have to be updated by hand is just a joke

    * speed

    * automatical selection of correct plural/singular form of messages (think “you have one message” vs. “you have 5 messages”)

    Is this enough? I could go on and on :)

  • pragmatig

    ease of use and maintainability are my top points, simply build the code and worry about translations later. For me i18n only makes sense for framework/enterprise type applications

  • Scolas

    with gettext 2.0.0 and rails 2.3.2 compatibility issue goes away, i suggest to upgrade! :)

  • szeryf

    @scolas – that’s good to hear :)

  • pragmatig

    i would not trust gettext 2.0, since it is the same pile of unmaintainable spaghetti code then before, only patched/enlarged with its fingers all inside rails/AR, update rails and it breaks again. If you want to wait 4 months for the rails 2.4 upgrade then thats the way to go.

    The idea behind FastGettext is to make a simple/clean/maintainable gettext implementation, that does not stick its fingers into every opening rails offers and it does not pollute your namespace+is faster+requires less memory+….

  • skorianez (@skorianez)

    Gret tutorial, How about upgrade for rails 3.1 ?

  • Kate

    Hello! For any Rail app translation project you might be working on, I invite you to have a look at this free converter tool http://yml2po.com/ and see if it can be of any help. The converter can make yaml to po and po to yaml, if needed. It could be a useful tool.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: