Globalize::DbTranslate with :include and no base language patch

Globalize DB translations currently doesn't support a :select find option, which breaks hasmany :trough => atranslateable_model associations.

I've refactored the Globalize::DbTranslate module to an AR::Associations pattern, override Model.find to always preload translations whilst removing all of the custom finder SQL.

Benefits

  • Supports all of the ActiveRecord::Base#find functionality
  • Plugs in with existing globalize table structures (no migrations required)
  • There is no Base Locale.Edit or create in any language, any other language always retrieves a valid facet, albeit not always in the active Locale.
  • Retrieves all facet translations for a given model at once, thus Locale switching doesn't incur a database roundtrip overhead.

Caveats

I removed a tiny bit of functionality to allow me to focus on a initial prototype.

  • Bidi strings temp. removed
  • Removed piggy backing find option
  • Works best with only one Locale per request, which is fine in most situations.
  • The no Base Locale setup might not be ideal for all use cases.
  • Only tested with Edge (production, test and development envs)
  • Globalize unit test helper chokes with the Dependencies mechanism and I couldn't yet get the unit tests to run.

The rundown

AR association and Translateable.find

Setup an association in the translates() hook:

          has_many :translations, :class_name => 'Globalize::ModelTranslation', :conditions => ['globalize_translations.table_name = ?', table_name], :foreign_key => 'item_id'  

Override Translateable.find, scoped to always prefetch translations:

   alias_method :old_find, :find
   def find(*args)
      options = args.last.is_a?(Hash) ? args.last : {}
      if (options.has_key?(:untranslated) && options[:untranslated] == true)
          old_find(*args)
      else
         with_scope( :find => { :include => :translations }) do
            old_find(*args)
         end
       end  
    end    

Cherry pick facet content

     def find_translation_for_facet( facet )
         translations ||= []
         #highest priority, find for current language
         translations << self.translations.detect{|tr| tr.facet == facet.to_s && tr.language_id == Locale.language.id }
         #do we have a non-blank attribute in the model table?
         translations << read_attribute(facet)
         #find the first translation, for any language
         translations << self.translations.detect{|tr| tr.facet == facet.to_s && !tr.text.nil? }

         #we don't want nil or empty string
         translations.reject!(&:blank?)
         translations.empty? ? nil : (translations.first.respond_to?(:text) ? translations.first.text : translations.first)                          
      end  

The patch

Grab this patch and give it a whirl.

Feedback on this? Performance implications? Backwards compatibility?


About this entry