ActiveRecord validations and internationalization
We recently switched to Rails 2.3 in our project. This version offers a new internationalisation system, which is a clear step forward, but its yaml based translation file storage and symbol driven message selection are unfortunately unsuitable for the real enterprise environment - handling translation files and separation of roles of developers and translators. The latter are driven by each country’s very own marketing department.
So we are going to continue using Gettext for localization. The old Masao’s library we have been using before is not compatible with current Rails. So after some evaluation we desided to use fast-gettext library as a foundation for our development, which was created by Michael Grosser.
The Rails 2.3 release feels rough and the transition to the new internationalization system unfinished. You can see different generations of code in validations.rb that do not really fit together.
There are still problems with custom validation messages. The build-in backend with symbol-based message keys is not an option for us or any other big-scale development environment (role separation etc., s. above). And ActiveRecord provides no other API for custom translated messages.
validates_length_of :archives, :minimum => 1,
:message => _('Gadget requires at least one archive ')
obviously does not work, as accurately described by Michael.
The underscore method is evaluated during the class loading. So instead of string we need a proc at this point, so it can account for locale, currently selected by user. We need a possibility to define a validation like this:
validates_length_of :archives, :minimum => 1,
:message => proc {_('Gadget requires at least one archive ')}
I’ve created a monkey patch for that, which you can put into a Rails initilizer, e.g. config/initializers/gettext_hacks.rb
module ActiveRecord
class Errors
# allow a proc as a user defined message
def add(attribute, message = nil, options = {})
message = options[:default].call if options[:default].is_a?(Proc)
message ||= :invalid
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
@errors[attribute.to_s] ||= []
@errors[attribute.to_s] << message
end
end
end
Now I’m working on a more advanced version, which will solve the problem of attribute name making troubles as validation message prefix. More on that - tomorrow.