require 'rtranslate' module HasManyTranslations # Adds the functionality necessary for translation actions on a has_translations instance of # ActiveRecord::Base. module Creation def self.included(base) # :nodoc: base.class_eval do extend ClassMethods include InstanceMethods before_update :update_translation? after_save :update_translations! class << self alias_method_chain :prepare_translated_options, :creation end end end # Class methods added to ActiveRecord::Base to facilitate the creation of new translations. module ClassMethods #@translator = Translate::RTranslate.new # Overrides the basal +prepare_has_translations_options+ method defined in HasManyTranslations::Options # to extract the :only and :except options into +has_many_translations_options+. def prepare_translated_options_with_creation(options) self.columns.map{|c|c.type == :string || c.type == :text ? c.name : nil}.compact.each{|name| #alias_method "#{name}_before_translation", name.to_sym #unless try(name) define_method name, lambda { |*args| # unless self.translations.blank? || self.translations.first.origin_locale_code == self.hmt_locale || read_attribute(name.to_sym).nil? trans = self.translations.first(:conditions => {:locale_code => self.hmt_locale, :attribute => name}) val = trans.nil? ? read_attribute(name.to_sym) : trans.value #self.hmt_locale else #self.id read_attribute(name.to_sym) #try(name) end #HasManyTranslations.fetch(args.first || self.class.locale || I18n.locale, name) } alias_method "#{name}_before_type_cast", name } result = prepare_translated_options_without_creation(options) self.has_many_translations_options[:only] = Array(options.delete(:only)).map(&:to_s).uniq if options[:only] self.has_many_translations_options[:except] = Array(options.delete(:except)).map(&:to_s).uniq if options[:except] #self.has_many_translations_options[:locales] = Array(options.delete(:locales)).map(&:to_s).uniq if options[:locales] result end end # Instance methods that determine whether to save a translation and actually perform the save. module InstanceMethods #private #attr_accessor :translator #@translator = Translate::RTranslate.new if defined? Settings @translator.key = Settings.google_api_key end def allowed_locales t = TranslationSpec.first(:conditions => {:translated_id => self.id, :translated_type => self.class.to_s}) t.blank? ? nil : t.codes.split(',').map{|c| c.to_sym} end def locales=(codes) t = TranslationSpec.first(:conditions => {:translated_id => self.id, :translated_type => self.class.to_s}) unless t.blank? t.update_attribute('codes', codes.map{|c|c.to_s}.join(',')) else TranslationSpec.create(:translated_id => self.id, :translated_type => self.class.to_s, :codes => codes.map{|c|c.to_s}.join(',')) end end def localize=(loc) @locale = loc end # def hmt_default_locale # return default_locale.to_sym if respond_to?(:default_locale) # return self.class.default_locale.to_sym if self.class.respond_to?(:default_locale) # I18n.default_locale # end def hmt_locale @hmt_locale = respond_to?(:locale) ? self.locale.to_s : self.class.respond_to?(:locale) ? self.class.locale.to_s : I18n.locale.to_s end # Returns whether a new translation should be created upon updating the parent record. def create_translation? #determine if locale parameter is supported by this translated # find out if we have a table created for all locales # glob I18n.available_locales with whatever we use for the "study" available_locales # I18n.available_locales.include? #!translation_changes.blank? true end def create_translation_for_locale?(locale) #determine if locale parameter is supported by this translated # find out if we have a table created for all locales # glob I18n.available_locales with whatever we use for the "study" available_locales # I18n.available_locales.include? locales = Google::Language::Languages.keys & I18n.available_locales.map{|l|l.to_s} locales.include?(locales) end # Creates a new translation upon updating the parent record. def create_translation translation.create(translation_attributes) #reset_translation_changes #reset_translation end # Returns whether the last translation should be updated upon updating the parent record. # This method is overridden in HasManyTranslations::Control to account for a control block that # merges changes onto the previous translation. def update_translations! #translated_columns.each do |attrib| self.locales.each do |loc| # put this in a option check blog to determine if the job should be queued? queue_translation(loc) #ActiveQueue::Queue.enqueue(TranslationJobs::AutoTranslateJob,{:translated_id => self.id,:translated_type => self.type, :origin_locale => self.hmt_locale, :destination_locale => loc.to_s}) #update_all_attributes_translation(loc, self.hmt_locale) end #end end def update_translation? unless self.translations.blank? || self.translations.first.origin_locale_code == self.hmt_locale dirty_translations = self.translations.all(:conditions => {:translated_id => self.id, :locale_code => self.hmt_locale}) dirty_translations.each do |dt| dt.value = try(dt.attribute) dt.save end return false end end def update_all_attributes_translation(loc, origin_locale) self.translated_columns.each do |attrib| update_translation(attrib, loc, origin_locale) end end # Updates the last translation's changes by appending the current translation changes. def update_translation(attrib, loc, origin_locale) unless translations.first(:conditions => {:attribute => attrib, :locale_code => loc.to_s}) update_translation!(attrib, loc, origin_locale.to_s) end end def update_translation!(attrib, loc, origin_locale, options = {}) @translator ||= Translate::RTranslate.new if defined? HmtSettings @translator.key = HmtSettings.google_api_key end translation_val = @translator.translate(try(attrib), :from => origin_locale.to_s, :to => loc.to_s) translations.create(:attribute => attrib, :locale_code => loc.to_s, :value => translation_val, :locale_name => Google::Language::Languages[loc.to_s], :machine_translation => true, :origin_locale_code => origin_locale ) unless translation_val.nil? || translation_val.match('Error: ') end # Returns an array of column names that should be included in the translation # If has_many_translations_options[:only] is specified, only those columns # will be translationed. Otherwise, if has_many_translations_options[:except] is specified, # all columns will be translationed other than those specified. Without either option, the # default is to translation all text & string columns. At any rate, the four "automagic" timestamp # columns maintained by Rails are never translationed. def translated_columns textual_columns = self.class.columns.map{|c|c.type == :string || c.type == :text ? c.name : nil}.compact textual_columns = self.has_many_translations_options[:only] ? textual_columns & self.has_many_translations_options[:only] : textual_columns textual_columns = self.has_many_translations_options[:except] ? textual_columns - self.has_many_translations_options[:except] : textual_columns return textual_columns end def queue_translation(loc) #ActiveQueue::Queue.enqueue(TranslationJobs::AutoTranslateJob,{ :translated_id => self.id, :translated_type => self.class.to_s, :origin_locale => self.hmt_locale, :destination_locale => loc }) ActiveQueue::Job.new(:val => { :translated_id => self.id, :translated_type => self.class.to_s, :origin_locale => self.hmt_locale, :destination_locale => loc },:job_klass => "TranslationJobs::AutoTranslateJob",:adapter => "resque").enqueue #ActiveQueue::Job.new(:value => TranslationJobs::AutoTranslateJob.new(:translated_id => self.id, :translated_type => self.class.to_s, :origin_locale => self.hmt_locale, :destination_locale => loc), :adapter => "resque", :queue_name => :file_queue).enqueue end def queue_translations self.locales.each do |loc| queue_translation(loc) end # Resque.enqueue(TranslationJobs::MachineTranslationJob.new(self.id, self.type)) #ActiveQueue::Queue.enqueue(TranslationJobs::MachineTranslationJob,{:translated_id => self.id,:translated_type => self.type, :origin_locale => self.hmt_locale}) #Delayed::Job.enqueue(TranslationJobs::MachineTranslationJob.new({ :translated_id => self.id,:translated_type => self.class.to_s, :origin_locale => self.hmt_locale.to_s }) #job = TranslationJobs::MachineTranslationJob.new(self.id, self.type, self.hmt_locale) end def locales if allowed_locales retloc = allowed_locales.map{|l|l.to_s} elsif super_locales.present? super_locales.each do |sloc| retloc.nil? ? retloc = eval("self.#{sloc}.locales") : retloc | eval("self.#{sloc}.locales") end else retloc = has_many_translations_options[:locales] && I18n && Google ? has_many_translations_options[:locales] & Google::Language::Languages.keys : Google::Language::Languages.keys & I18n.available_locales.map{|l|l.to_s} end return retloc # I18n.available_locales.map(&:to_s) end def super_locales if I18n && Google self.class.reflect_on_all_associations(:belongs_to).map{|a|eval("#{a.name.to_s.capitalize}.translated?") ? a.name.to_s : nil}.compact end end # Specifies the attributes used during translation creation. This is separated into its own # method so that it can be overridden by the HasManyTranslations::Users feature. # def translation_attributes # {:changes => translation_changes, :number => last_translation + 1} # end end end end