Archive for the 'Ruby on Rails' Category

Using Rails’ New I18n Support in Real Life: Part the Fourth

Tuesday, December 16th, 2008 By: Daniel

So after tediously going through your entire site and extracting all displayed strings to a separate translation file, how do you know you didn’t miss something somewhere? My solution was to create a quick rake task that machine translates my English yaml file to something else. A quick sudo aptitude install bsdgames and I had pig latin at my fingertips. So I switched to that and browsed through the site and looked for anything that hadn’t changed. And I found quite a bit of stuff that I had missed.

# lib/tasks/pig.rake
desc "Creates a config/locales/pig-US.yml from config/locales/en-US.yml"
task :pig => :environment do
  DEFAULT_LOCALE = 'en-US'
  File.open(File.join(RAILS_ROOT, 'config', 'locales', 'pig-US.yml'), "w") do |fout|
    File.readlines(File.join(RAILS_ROOT, 'config', 'locales', "#{DEFAULT_LOCALE}.yml")).each do |line|
      unless line =~ /"/
        fout.puts line.sub(DEFAULT_LOCALE, 'pig-US')
      else
        key, *translation = line.split(':')
        translation = translation.join(':')
        translation.split(/(".*?")/).each do |quote|
          if quote =~ /"/
            quote.gsub!('"', '')
            string = quote.split(/(\{\{.*?\}\})/).map do |s|
              (s =~ /\{\{/) ? s : `echo "#{s.gsub('$', '\$')}" | pig`.chomp
            end
            translation.sub!(quote, string.join)
          end
        end
        fout.puts "#{key}:#{translation}"
      end
    end
  end
end

P.S. Note to anyone that actually tries to use this: This is just a quick and dirty script and will probably require some modification to work in your situation. In particular it requires all the strings you want translated to be within double quotes.

Using Rails’ New I18n Support in Real Life: Part the Third

Monday, December 15th, 2008 By: Daniel

What happens when you add a new string to your default locale file and forget about the other languages? Well, by default it’ll raise a MissingTranslationData and your users will see an ugly string the likes of “es-MX, marketing_interface, index, title“. Wouldn’t it be better to at least try and default to English? My guess is that most people would prefer to see the message in another language than some cryptic error message. And while we’re wishing, don’t you think the localize method shouldn’t die a noisy death when you happen to pass it a nil?

My solution? A custom I18n backend. I simply copy/pasted the code from the default “Simple” backend, tweaked a few lines and I was good to go!

# In some file that gets sourced on startup, like maybe in your config/initializers directory
module I18n
  module Backend
    class Moki < Simple
 
      def translate(locale, key, options = {})
        raise InvalidLocale.new(locale) if locale.nil?
        return key.map { |k| translate(locale, k, options) } if key.is_a? Array
        reserved = :scope, :default
        count, scope, default = options.values_at(:count, *reserved)
        options.delete(:default)
        values = options.reject { |name, value| reserved.include?(name) }
        entry = lookup(locale, key, scope)
        if entry.nil?
          entry = default(locale, default, options)
          entry ||= lookup(I18n.default_locale, key, scope)
          raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil?
        end
        entry = pluralize(locale, entry, count)
        entry = interpolate(locale, entry, values)
        entry
      end
 
      def localize(locale, object, format = :default)
        return nil if object.nil?
        raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
        type = object.respond_to?(:sec) ? 'time' : 'date'
        formats = translate(locale, "#{type}.formats")
        format = formats[format.to_sym] if formats && formats[format.to_sym]
        format = format.to_s.dup
        format.gsub!(/%a/, translate(locale, "date.abbr_day_names")[object.wday])
        format.gsub!(/%A/, translate(locale, "date.day_names")[object.wday])
        format.gsub!(/%b/, translate(locale, "date.abbr_month_names")[object.mon])
        format.gsub!(/%B/, translate(locale, "date.month_names")[object.mon])
        format.gsub!(/%p/, translate(locale, "time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
        object.strftime(format)
      end
 
    end
  end
end
I18n.backend = I18n::Backend::Moki.new

Using Rails’ New I18n Support in Real Life: Part the Second

Friday, December 12th, 2008 By: Daniel

A few more thoughts on handling I18n for real projects.

Read the rest of this entry »

Using Rails’ New I18n Support in Real Life: Part the First

Thursday, December 11th, 2008 By: Daniel

Well, there are plenty of nice introductions and demos to Rails’ slick new I18n features out there but I haven’t seen much on using it on a real decent-sized app. So I’ll share some of my thoughts on the subject.
Read the rest of this entry »