zargony.com

#![desc = "Random thoughts of a software engineer"]

Using gettext at class level with Rails

In short: DON'T!

The long story:

Lately, I talked to a few people about using gettext in Rails applications and one of the most frequently asked questions was, why gettext behaves strange on strings at class level. My advice: don't use gettext at class level -- it logically makes no sense (at least not in Rails).

In a Rails application, an incoming request is routed to a controller instance, i.e. Rails creates a new object of a controller class for every request. To determine the language a page should be shown in, gettext needs information of the current request: the language is selected by looking at the Accept-Language HTTP header, the lang cookie, the lang GET/POST parameter, or whatever else you made it to use (e.g. a user setting or the TLD of a request).

In all cases, the selected language is determined by information from the request - information, which simply isn't available outside an instance of a controller.

If you use the gettext helper method _ at class level, it evaluates once when the application loads and results in a fixed (usually untranslated) string being used for all requests. You need to use the gettext methods at instance level to make it work.

But... why does it work sometimes? Actually it sometimes works in development environment, because the Rails application is entirely reloaded on each request and therefore the gettext statement is effectively evaluated on each request.

So, a common pitfall of using gettext with Rails is, that everything seems to translate fine during development, but strange things happen on the production server. In my opinion, it'd be a good idea to let gettext helpers raise an error if used at class level in a Rails application. Maybe I should file a gettext feature request for this ;). (note that this only applies to Rails applications; in other Ruby applications, it's usually perfectly fine to use gettext at class level since the language can be selected when the application starts)

An example: if you use will_paginate and want to translate the prev_label and next_label text in your application, you cannot use the global WillPaginate::ViewHelpers.pagination_options hash to do so. Instead, you need to override the :prev_label and :next_label options at instance level. To prevent specifying these options on every will_paginate call, I use the following helper method.

# Add localized prev/next-labels to pagination links
  def will_paginate (*args)
    options = args.extract_options!
    options[:prev_label] ||= '« ' + s_('Pagination|Previous')
    options[:next_label] ||= s_('Pagination|Next') + ' »'
    super(*(args << options))
  end

Just drop the above method into a helper module in your Rails application, and you've got a localized will_paginate helper.