Generate I18n locale files from Rails views and models

I made a little rake task to allow you to scrape your views for translation calls, and also run through models and generate locale files. The model scraping allows the resulting file to be used with formtastic.

There are plenty of comments in the code, but if anyone has questions or problems please comment.

 

require 'find'

class Hash
  # sets value at a dot seperated key path
  # key = 'general.name' = hash[:general][:name]
  def set_value_for_key(key, value)
    key_parts = key.split('.')
    if key_parts.length > 1
      key_part = key_parts[0]
      new_key = key_parts[1..-1].join('.')
      self[key_part] ||= {}
      self[key_part].set_value_for_key(new_key, value)
    else
      self[key] = value
    end
  end
end

module I18n
  def self.translation_exception(*args)
    locale = args[1] # => :en
    key = args[2] # => 'general.name'
    #load existing locale data
    locale_file = Rails.root.join("config/locales/#{locale}.yml")
    data = YAML.load_file(locale_file) if File.exists?(locale_file)
    data ||= {}
    # normalise the key
    normalized_key = I18n.normalize_keys(locale, key, []) # => [:general,:name]
    # used for the value if
    value_text = normalized_key[-1] # => :name
    # add to the hash
    data.set_value_for_key(normalized_key.join('.'), value_text.to_s.humanize)
    # save
    File.open(locale_file, "w") {|f| f.write( data.to_yaml) }
  end
end
# override default exception for translation exceptions
# this allows us to process missing translation with the above code
I18n.exception_handler = :translation_exception

namespace :translations do
  desc "Update translation yaml from views"
  task :from_views => :environment do
    assets = []
    #get all the view files
    Find.find(Rails.configuration.view_path) do |view|
      if File.file?(view) && File.extname(view) == '.erb'
        File.readlines(view).each do |line|
          #look for t or I18n.t calls on each line, and append to assets
          assets += line.scan(/\st\(([^,\)]*)/).flatten
          assets += line.scan(/\sI18n.t\(([^,\)]*)/).flatten
        end
      end
    end
    #clean the keys up
    assets.map!{|a| a.gsub("'","").gsub(':','')}
    #sort so deep keys are last
    assets.sort!{|x,y| x.scan('.').count <=> y.scan('.').count}
    #for each locale
    I18n.available_locales.each do |locale|
      I18n.locale = locale
      #attempt to call translate for each key
      assets.each do |translation_key|
        I18n.t(translation_key)
      end
    end

  end

  desc "Update translation yaml from models"
  task :from_models => :environment do
    #require the model files so rails is aware of the model
    Find.find(Rails.root.join('app/models')) do |model_file|
      if File.file?(model_file) && File.extname(model_file) == '.rb'
        require model_file
      end
    end
    #for each available locale
    I18n.available_locales.each do |locale|
      I18n.locale = locale
      #and each available model
      ActiveRecord::Base.descendants.each do |model|
        #in my case some models are for extension only and will not have a table
        #so using a rescue block lets the code keep on going
        begin
          model_name = model.name.underscore
          #for each column, try to access the translation
          model.columns.each do |col|
            I18n.t("activerecord.attributes.#{model_name}.#{col.name}".trim)
          end
        rescue Exception => ex
          #do nothing
        end
      end
    end
  end

  desc "Update translation yaml from all sources"
  task :from_all => [:from_views, :from_models]

end
Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • StumbleUpon
  • Twitter

About Danny

Primarily a Ruby On Rails developer, but I can do other cool stuff too.
This entry was posted in Development, Ruby / Rails. Bookmark the permalink.

2 Responses to Generate I18n locale files from Rails views and models

  1. Leandro says:

    I try to run the rake task:
    $ rake translations:from_models
    but I have the next error:
    rake aborted!
    no block given

    Tasks: TOP => translations:from_models
    I use ruby 1.8.7, rubygems 1.7.2, rails 3.0.10, rake 0.9.2

  2. Leszek says:

    Remove ‘each’ from this lines

    Find.find(Rails.root.join(‘app/models’)).each do |model_file|
    and
    Find.find(Rails.configuration.view_path).each do |view|

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>