has_many_translations 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +22 -0
- data/README.rdoc +16 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/generators/has_many_translations/has_many_translations_generator.rb +10 -0
- data/generators/has_many_translations/templates/initializer.rb +9 -0
- data/generators/has_many_translations/templates/migration.rb +33 -0
- data/has_many_translations.gemspec +75 -0
- data/init.rb +1 -0
- data/lib/has_many_translations/configuration.rb +27 -0
- data/lib/has_many_translations/control.rb +49 -0
- data/lib/has_many_translations/creation.rb +219 -0
- data/lib/has_many_translations/hmt_settings.rb +4 -0
- data/lib/has_many_translations/options.rb +48 -0
- data/lib/has_many_translations/translated.rb +36 -0
- data/lib/has_many_translations/translation.rb +11 -0
- data/lib/has_many_translations/translation_jobs.rb +33 -0
- data/lib/has_many_translations/translations.rb +19 -0
- data/lib/has_many_translations.rb +37 -0
- data/lib/translation_spec.rb +3 -0
- data/tasks/has_many_translations_tasks.rake +7 -0
- metadata +172 -0
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
= has_many_translations
|
2
|
+
|
3
|
+
Makes models speak in tongues!
|
4
|
+
|
5
|
+
<tt>has_many_translations</tt>[http://github.com/Artforge/has_many_translations]
|
6
|
+
|
7
|
+
Installing from source
|
8
|
+
|
9
|
+
1) cd /[YOUR SOURCE DIRECTORY]/has_many_translations/
|
10
|
+
|
11
|
+
2) git pull
|
12
|
+
|
13
|
+
3) rake build
|
14
|
+
|
15
|
+
4) sudo gem install pkg/has_many_translations-0.2.x.gem (where x is latest version)
|
16
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rcov/rcovtask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |g|
|
10
|
+
g.name = 'has_many_translations'
|
11
|
+
g.summary = %(Makes models' speak in tongues)
|
12
|
+
g.description = %(Keep a DRY multilingual translation of your ActiveRecord models' textual attributes)
|
13
|
+
g.email = 'mjording@openogotham.com'
|
14
|
+
g.homepage = 'http://github.com/opengotham/has_many_translations'
|
15
|
+
g.authors = %w(opengotham)
|
16
|
+
g.add_dependency 'activerecord', '>= 2.1.0'
|
17
|
+
g.add_development_dependency 'shoulda'
|
18
|
+
g.add_development_dependency 'mocha'
|
19
|
+
g.add_runtime_dependency 'opengotham-rtranslate'
|
20
|
+
g.add_runtime_dependency 'activequeue'
|
21
|
+
g.add_runtime_dependency 'settingslogic'
|
22
|
+
end
|
23
|
+
Jeweler::GemcutterTasks.new
|
24
|
+
rescue LoadError
|
25
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
26
|
+
end
|
27
|
+
|
28
|
+
Rake::TestTask.new do |t|
|
29
|
+
t.libs = %w(test)
|
30
|
+
t.pattern = 'test/**/*_test.rb'
|
31
|
+
end
|
32
|
+
|
33
|
+
Rcov::RcovTask.new do |t|
|
34
|
+
t.libs = %w(test)
|
35
|
+
t.pattern = 'test/**/*_test.rb'
|
36
|
+
end
|
37
|
+
|
38
|
+
task :test => :check_dependencies
|
39
|
+
task :default => :test
|
40
|
+
|
41
|
+
Rake::RDocTask.new do |r|
|
42
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : nil
|
43
|
+
r.rdoc_dir = 'rdoc'
|
44
|
+
r.title = ['has_many_translations', version].compact.join(' ')
|
45
|
+
r.options << '--line-numbers' << '--inline-source'
|
46
|
+
r.rdoc_files.include('README*')
|
47
|
+
r.rdoc_files.include('lib/**/*.rb')
|
48
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.3
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class HasManyTranslationsGenerator < Rails::Generator::Base
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
m.migration_template 'migration.rb', File.join('db', 'migrate'), :migration_file_name => 'create_translations'
|
5
|
+
|
6
|
+
m.directory File.join('config', 'initializers')
|
7
|
+
m.template 'initializer.rb', File.join('config', 'initializers', 'has_many_translations.rb')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
HasManyTranslations.configure do |config|
|
2
|
+
# Place any global options here. For example, in order to specify your own translations model to use
|
3
|
+
# throughout the application, simply specify:
|
4
|
+
#
|
5
|
+
# config.class_name = "MyCustomVersion"
|
6
|
+
#
|
7
|
+
# Any options passed to the "has_translations" method in the model itself will override this global
|
8
|
+
# configuration.
|
9
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class CreateTranslations < ActiveRecord::Migration
|
2
|
+
def self.up #
|
3
|
+
create_table :translation_specs do |t|
|
4
|
+
t.belongs_to :translated, :polymorphic => true
|
5
|
+
t.string :codes
|
6
|
+
end
|
7
|
+
add_index :translation_specs, :codes
|
8
|
+
add_index :translation_specs, [:translated_id, :translated_type]
|
9
|
+
|
10
|
+
create_table :translations do |t|
|
11
|
+
t.belongs_to :translated, :polymorphic => true
|
12
|
+
t.string :attribute
|
13
|
+
t.text :value
|
14
|
+
t.string :locale_code
|
15
|
+
t.string :locale_name
|
16
|
+
t.string :origin_locale_code
|
17
|
+
t.boolean :machine_translation
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
add_index :translations, [:locale_code, :attribute]
|
21
|
+
add_index :translations, [:translated_id, :translated_type]
|
22
|
+
|
23
|
+
change_table :users do |t|
|
24
|
+
add_column :users, :prefered_language, :string
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.down
|
29
|
+
drop_table :translation_spec
|
30
|
+
drop_table :translations
|
31
|
+
remove_column :users, :prefered_language
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{has_many_translations}
|
8
|
+
s.version = "0.3.3"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["opengotham"]
|
12
|
+
s.date = %q{2010-06-02}
|
13
|
+
s.description = %q{Keep a DRY multilingual translation of your ActiveRecord models' textual attributes}
|
14
|
+
s.email = %q{mjording@openogotham.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"README.rdoc",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"generators/has_many_translations/has_many_translations_generator.rb",
|
24
|
+
"generators/has_many_translations/templates/initializer.rb",
|
25
|
+
"generators/has_many_translations/templates/migration.rb",
|
26
|
+
"has_many_translations.gemspec",
|
27
|
+
"init.rb",
|
28
|
+
"lib/has_many_translations.rb",
|
29
|
+
"lib/has_many_translations/configuration.rb",
|
30
|
+
"lib/has_many_translations/control.rb",
|
31
|
+
"lib/has_many_translations/creation.rb",
|
32
|
+
"lib/has_many_translations/hmt_settings.rb",
|
33
|
+
"lib/has_many_translations/options.rb",
|
34
|
+
"lib/has_many_translations/translated.rb",
|
35
|
+
"lib/has_many_translations/translation.rb",
|
36
|
+
"lib/has_many_translations/translation_jobs.rb",
|
37
|
+
"lib/has_many_translations/translations.rb",
|
38
|
+
"lib/translation_spec.rb",
|
39
|
+
"tasks/has_many_translations_tasks.rake"
|
40
|
+
]
|
41
|
+
s.homepage = %q{http://github.com/opengotham/has_many_translations}
|
42
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.rubygems_version = %q{1.3.7}
|
45
|
+
s.summary = %q{Makes models' speak in tongues}
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
49
|
+
s.specification_version = 3
|
50
|
+
|
51
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
52
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 2.1.0"])
|
53
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
55
|
+
s.add_runtime_dependency(%q<opengotham-rtranslate>, [">= 0"])
|
56
|
+
s.add_runtime_dependency(%q<activequeue>, [">= 0"])
|
57
|
+
s.add_runtime_dependency(%q<settingslogic>, [">= 0"])
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<activerecord>, [">= 2.1.0"])
|
60
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
61
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
62
|
+
s.add_dependency(%q<opengotham-rtranslate>, [">= 0"])
|
63
|
+
s.add_dependency(%q<activequeue>, [">= 0"])
|
64
|
+
s.add_dependency(%q<settingslogic>, [">= 0"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<activerecord>, [">= 2.1.0"])
|
68
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
69
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
70
|
+
s.add_dependency(%q<opengotham-rtranslate>, [">= 0"])
|
71
|
+
s.add_dependency(%q<activequeue>, [">= 0"])
|
72
|
+
s.add_dependency(%q<settingslogic>, [">= 0"])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib', 'has_many_translations')
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module HasManyTranslations
|
2
|
+
|
3
|
+
module Configuration
|
4
|
+
def configure
|
5
|
+
yield Configuration
|
6
|
+
end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Simply stores a hash of options given to the +configure+ block.
|
10
|
+
def options
|
11
|
+
@options ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# If given a setter method name, will assign the first argument to the +options+ hash with
|
15
|
+
# the method name (sans "=") as the key. If given a getter method name, will attempt to
|
16
|
+
# a value from the +options+ hash for that key. If the key doesn't exist, defers to +super+.
|
17
|
+
def method_missing(symbol, *args)
|
18
|
+
if (method = symbol.to_s).sub!(/\=$/, '')
|
19
|
+
options[method.to_sym] = args.first
|
20
|
+
else
|
21
|
+
options.fetch(method.to_sym, super)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module HasManyTranslations
|
2
|
+
|
3
|
+
module Control
|
4
|
+
|
5
|
+
def self.included(base) # :nodoc:
|
6
|
+
base.class_eval do
|
7
|
+
include InstanceMethods
|
8
|
+
alias_method_chain :create_translation?, :control
|
9
|
+
alias_method_chain :update_translation?, :control
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
def skip_translation
|
15
|
+
with_translation_flag(:skip_translation) do
|
16
|
+
yield if block_given?
|
17
|
+
save
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def skip_translation!
|
22
|
+
with_version_flag(:skip_translation) do
|
23
|
+
yield if block_given?
|
24
|
+
save!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def skip_translation?
|
29
|
+
!!@skip_translation
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
# Used for each control block, the +with_version_flag+ method sets a given variable to
|
34
|
+
# true and then executes the given block, ensuring that the variable is returned to a nil
|
35
|
+
# value before returning. This is useful to be certain that one of the control flag
|
36
|
+
# instance variables isn't inadvertently left in the "on" position by execution within the
|
37
|
+
# block raising an exception.
|
38
|
+
def with_translation_flag(flag)
|
39
|
+
begin
|
40
|
+
instance_variable_set("@#{flag}", true)
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
instance_variable_set("@#{flag}", nil)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'rtranslate'
|
2
|
+
module HasManyTranslations
|
3
|
+
# Adds the functionality necessary for translation actions on a has_translations instance of
|
4
|
+
# ActiveRecord::Base.
|
5
|
+
module Creation
|
6
|
+
def self.included(base) # :nodoc:
|
7
|
+
base.class_eval do
|
8
|
+
extend ClassMethods
|
9
|
+
include InstanceMethods
|
10
|
+
before_update :update_translation?
|
11
|
+
|
12
|
+
after_save :update_translations!
|
13
|
+
class << self
|
14
|
+
alias_method_chain :prepare_translated_options, :creation
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Class methods added to ActiveRecord::Base to facilitate the creation of new translations.
|
20
|
+
module ClassMethods
|
21
|
+
#@translator = Translate::RTranslate.new
|
22
|
+
# Overrides the basal +prepare_has_translations_options+ method defined in HasManyTranslations::Options
|
23
|
+
# to extract the <tt>:only</tt> and <tt>:except</tt> options into +has_many_translations_options+.
|
24
|
+
def prepare_translated_options_with_creation(options)
|
25
|
+
self.columns.map{|c|c.type == :string || c.type == :text ? c.name : nil}.compact.each{|name|
|
26
|
+
#alias_method "#{name}_before_translation", name.to_sym
|
27
|
+
#unless try(name)
|
28
|
+
define_method name, lambda { |*args|
|
29
|
+
#
|
30
|
+
unless self.translations.blank? || self.translations.first.origin_locale_code == self.hmt_locale || read_attribute(name.to_sym).nil?
|
31
|
+
trans = self.translations.first(:conditions => {:locale_code => self.hmt_locale, :attribute => name})
|
32
|
+
val = trans.nil? ? read_attribute(name.to_sym) : trans.value
|
33
|
+
#self.hmt_locale
|
34
|
+
else
|
35
|
+
#self.id
|
36
|
+
read_attribute(name.to_sym)
|
37
|
+
#try(name)
|
38
|
+
end
|
39
|
+
#HasManyTranslations.fetch(args.first || self.class.locale || I18n.locale, name)
|
40
|
+
}
|
41
|
+
|
42
|
+
alias_method "#{name}_before_type_cast", name
|
43
|
+
|
44
|
+
}
|
45
|
+
|
46
|
+
result = prepare_translated_options_without_creation(options)
|
47
|
+
|
48
|
+
self.has_many_translations_options[:only] = Array(options.delete(:only)).map(&:to_s).uniq if options[:only]
|
49
|
+
self.has_many_translations_options[:except] = Array(options.delete(:except)).map(&:to_s).uniq if options[:except]
|
50
|
+
#self.has_many_translations_options[:locales] = Array(options.delete(:locales)).map(&:to_s).uniq if options[:locales]
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
# Instance methods that determine whether to save a translation and actually perform the save.
|
57
|
+
module InstanceMethods
|
58
|
+
|
59
|
+
#private
|
60
|
+
#attr_accessor :translator
|
61
|
+
#@translator = Translate::RTranslate.new
|
62
|
+
if defined? Settings
|
63
|
+
@translator.key = Settings.google_api_key
|
64
|
+
end
|
65
|
+
def allowed_locales
|
66
|
+
t = TranslationSpec.first(:conditions => {:translated_id => self.id, :translated_type => self.class.to_s})
|
67
|
+
t.blank? ? nil : t.codes.split(',').map{|c| c.to_sym}
|
68
|
+
end
|
69
|
+
def locales=(codes)
|
70
|
+
t = TranslationSpec.first(:conditions => {:translated_id => self.id, :translated_type => self.class.to_s})
|
71
|
+
unless t.blank?
|
72
|
+
t.update_attribute('codes', codes.map{|c|c.to_s}.join(','))
|
73
|
+
else
|
74
|
+
TranslationSpec.create(:translated_id => self.id, :translated_type => self.class.to_s, :codes => codes.map{|c|c.to_s}.join(','))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
def localize=(loc)
|
78
|
+
@locale = loc
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# def hmt_default_locale
|
83
|
+
# return default_locale.to_sym if respond_to?(:default_locale)
|
84
|
+
# return self.class.default_locale.to_sym if self.class.respond_to?(:default_locale)
|
85
|
+
# I18n.default_locale
|
86
|
+
# end
|
87
|
+
def hmt_locale
|
88
|
+
@hmt_locale = respond_to?(:locale) ? self.locale.to_s : self.class.respond_to?(:locale) ? self.class.locale.to_s : I18n.locale.to_s
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns whether a new translation should be created upon updating the parent record.
|
93
|
+
def create_translation?
|
94
|
+
#determine if locale parameter is supported by this translated
|
95
|
+
# find out if we have a table created for all locales
|
96
|
+
# glob I18n.available_locales with whatever we use for the "study" available_locales
|
97
|
+
# I18n.available_locales.include?
|
98
|
+
#!translation_changes.blank?
|
99
|
+
true
|
100
|
+
end
|
101
|
+
def create_translation_for_locale?(locale)
|
102
|
+
#determine if locale parameter is supported by this translated
|
103
|
+
# find out if we have a table created for all locales
|
104
|
+
# glob I18n.available_locales with whatever we use for the "study" available_locales
|
105
|
+
# I18n.available_locales.include?
|
106
|
+
locales = Google::Language::Languages.keys & I18n.available_locales.map{|l|l.to_s}
|
107
|
+
locales.include?(locales)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Creates a new translation upon updating the parent record.
|
111
|
+
def create_translation
|
112
|
+
|
113
|
+
translation.create(translation_attributes)
|
114
|
+
#reset_translation_changes
|
115
|
+
#reset_translation
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns whether the last translation should be updated upon updating the parent record.
|
119
|
+
# This method is overridden in HasManyTranslations::Control to account for a control block that
|
120
|
+
# merges changes onto the previous translation.
|
121
|
+
def update_translations!
|
122
|
+
#translated_columns.each do |attrib|
|
123
|
+
self.locales.each do |loc|
|
124
|
+
# put this in a option check blog to determine if the job should be queued?
|
125
|
+
queue_translation(loc)
|
126
|
+
#ActiveQueue::Queue.enqueue(TranslationJobs::AutoTranslateJob,{:translated_id => self.id,:translated_type => self.type, :origin_locale => self.hmt_locale, :destination_locale => loc.to_s})
|
127
|
+
#update_all_attributes_translation(loc, self.hmt_locale)
|
128
|
+
end
|
129
|
+
#end
|
130
|
+
end
|
131
|
+
|
132
|
+
def update_translation?
|
133
|
+
unless self.translations.blank? || self.translations.first.origin_locale_code == self.hmt_locale
|
134
|
+
dirty_translations = self.translations.all(:conditions => {:translated_id => self.id, :locale_code => self.hmt_locale})
|
135
|
+
dirty_translations.each do |dt|
|
136
|
+
dt.value = try(dt.attribute)
|
137
|
+
dt.save
|
138
|
+
end
|
139
|
+
return false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def update_all_attributes_translation(loc, origin_locale)
|
144
|
+
self.translated_columns.each do |attrib|
|
145
|
+
update_translation(attrib, loc, origin_locale)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
# Updates the last translation's changes by appending the current translation changes.
|
149
|
+
def update_translation(attrib, loc, origin_locale)
|
150
|
+
unless translations.first(:conditions => {:attribute => attrib, :locale_code => loc.to_s})
|
151
|
+
update_translation!(attrib, loc, origin_locale.to_s)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def update_translation!(attrib, loc, origin_locale, options = {})
|
156
|
+
@translator ||= Translate::RTranslate.new
|
157
|
+
if defined? HmtSettings
|
158
|
+
@translator.key = HmtSettings.google_api_key
|
159
|
+
end
|
160
|
+
translation_val = @translator.translate(try(attrib), :from => origin_locale.to_s, :to => loc.to_s)
|
161
|
+
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: ')
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
# Returns an array of column names that should be included in the translation
|
166
|
+
# If <tt>has_many_translations_options[:only]</tt> is specified, only those columns
|
167
|
+
# will be translationed. Otherwise, if <tt>has_many_translations_options[:except]</tt> is specified,
|
168
|
+
# all columns will be translationed other than those specified. Without either option, the
|
169
|
+
# default is to translation all text & string columns. At any rate, the four "automagic" timestamp
|
170
|
+
# columns maintained by Rails are never translationed.
|
171
|
+
def translated_columns
|
172
|
+
textual_columns = self.class.columns.map{|c|c.type == :string || c.type == :text ? c.name : nil}.compact
|
173
|
+
textual_columns = self.has_many_translations_options[:only] ? textual_columns & self.has_many_translations_options[:only] : textual_columns
|
174
|
+
textual_columns = self.has_many_translations_options[:except] ? textual_columns - self.has_many_translations_options[:except] : textual_columns
|
175
|
+
return textual_columns
|
176
|
+
|
177
|
+
end
|
178
|
+
def queue_translation(loc)
|
179
|
+
#ActiveQueue::Queue.enqueue(TranslationJobs::AutoTranslateJob,{ :translated_id => self.id, :translated_type => self.class.to_s, :origin_locale => self.hmt_locale, :destination_locale => loc })
|
180
|
+
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
|
181
|
+
#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
|
182
|
+
end
|
183
|
+
def queue_translations
|
184
|
+
self.locales.each do |loc|
|
185
|
+
queue_translation(loc)
|
186
|
+
end
|
187
|
+
# Resque.enqueue(TranslationJobs::MachineTranslationJob.new(self.id, self.type))
|
188
|
+
#ActiveQueue::Queue.enqueue(TranslationJobs::MachineTranslationJob,{:translated_id => self.id,:translated_type => self.type, :origin_locale => self.hmt_locale})
|
189
|
+
#Delayed::Job.enqueue(TranslationJobs::MachineTranslationJob.new({ :translated_id => self.id,:translated_type => self.class.to_s, :origin_locale => self.hmt_locale.to_s })
|
190
|
+
#job = TranslationJobs::MachineTranslationJob.new(self.id, self.type, self.hmt_locale)
|
191
|
+
end
|
192
|
+
def locales
|
193
|
+
|
194
|
+
if allowed_locales
|
195
|
+
retloc = allowed_locales.map{|l|l.to_s}
|
196
|
+
elsif super_locales.present?
|
197
|
+
super_locales.each do |sloc|
|
198
|
+
retloc.nil? ? retloc = eval("self.#{sloc}.locales") : retloc | eval("self.#{sloc}.locales")
|
199
|
+
end
|
200
|
+
else
|
201
|
+
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}
|
202
|
+
end
|
203
|
+
return retloc
|
204
|
+
# I18n.available_locales.map(&:to_s)
|
205
|
+
end
|
206
|
+
|
207
|
+
def super_locales
|
208
|
+
if I18n && Google
|
209
|
+
self.class.reflect_on_all_associations(:belongs_to).map{|a|eval("#{a.name.to_s.capitalize}.translated?") ? a.name.to_s : nil}.compact
|
210
|
+
end
|
211
|
+
end
|
212
|
+
# Specifies the attributes used during translation creation. This is separated into its own
|
213
|
+
# method so that it can be overridden by the HasManyTranslations::Users feature.
|
214
|
+
# def translation_attributes
|
215
|
+
# {:changes => translation_changes, :number => last_translation + 1}
|
216
|
+
# end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module HasManyTranslations
|
2
|
+
# Provides +has_translations+ options conversion and cleanup.
|
3
|
+
|
4
|
+
module Options
|
5
|
+
|
6
|
+
def self.included(base) # :nodoc:
|
7
|
+
base.class_eval do
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Class methods that provide preparation of options passed to the +has_translations+ method.
|
13
|
+
module ClassMethods
|
14
|
+
# The +prepare_has_translations_options+ method has three purposes:
|
15
|
+
# 1. Populate the provided options with default values where needed
|
16
|
+
# 2. Prepare options for use with the +has_many+ association
|
17
|
+
# 3. Save user-configurable options in a class-level variable
|
18
|
+
#
|
19
|
+
# Options are given priority in the following order:
|
20
|
+
# 1. Those passed directly to the +translated+ method
|
21
|
+
# 2. Those specified in an initializer +configure+ block
|
22
|
+
# 3. Default values specified in +prepare_has_translations_options+
|
23
|
+
#
|
24
|
+
# The method is overridden in feature modules that require specific options outside the
|
25
|
+
# standard +has_many+ associations.
|
26
|
+
def prepare_translated_options(options)
|
27
|
+
options.symbolize_keys!
|
28
|
+
options.reverse_merge!(Configuration.options)
|
29
|
+
options.reverse_merge!(
|
30
|
+
:class_name => 'HasManyTranslations::Translation',
|
31
|
+
:dependent => :delete_all
|
32
|
+
)
|
33
|
+
class_inheritable_accessor :has_many_translations_options
|
34
|
+
class_inheritable_accessor :translator
|
35
|
+
self.translator = Translate::RTranslate.new
|
36
|
+
if defined? HmtSettings
|
37
|
+
self.translator.key = HmtSettings.google_api_key
|
38
|
+
end
|
39
|
+
self.has_many_translations_options = options.dup
|
40
|
+
|
41
|
+
options.merge!(
|
42
|
+
:as => :translated,
|
43
|
+
:extend => Array(options[:extend]).unshift(Translations)
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module HasManyTranslations
|
2
|
+
# Simply adds a flag to determine whether a model class is has_translations.
|
3
|
+
module Translated
|
4
|
+
|
5
|
+
def self.extended(base) # :nodoc:
|
6
|
+
base.class_eval do
|
7
|
+
class << self
|
8
|
+
alias_method_chain :translated, :flag
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
# Overrides the +translated?+ method to first define the +translated?+ class method before
|
13
|
+
# deferring to the original +translated+.
|
14
|
+
def translated_with_flag(*args)
|
15
|
+
translated_without_flag(*args)
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
def translated?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# For all ActiveRecord::Base models that do not call the +translated?+ method, the +has_translations?+
|
27
|
+
# method will return false.
|
28
|
+
def translated?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module HasManyTranslations
|
2
|
+
# The ActiveRecord model representing translations.
|
3
|
+
class Translation < ActiveRecord::Base
|
4
|
+
# Associate polymorphically with the parent record.
|
5
|
+
belongs_to :translated, :polymorphic => true
|
6
|
+
def initial?
|
7
|
+
number == 1
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module TranslationJobs
|
3
|
+
class MachineTranslationJob
|
4
|
+
attr_accessor :translated_id, :translated_type
|
5
|
+
def initialize(options)
|
6
|
+
@translated_id = options["translated_id"] || options[:translated_id]
|
7
|
+
@translated_type = options["translated_type"] || options[:translated_type]
|
8
|
+
end
|
9
|
+
def perform
|
10
|
+
translatable = Kernel.const_get(@translated_type).find(@translated_id)
|
11
|
+
translatable.update_translations!
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class AutoTranslateJob
|
17
|
+
attr_accessor :translated_id, :translated_type, :origin_locale, :destination_locale
|
18
|
+
|
19
|
+
def initialize(options)
|
20
|
+
@translated_id = options["translated_id"]||options[:translated_id]
|
21
|
+
@translated_type = options["translated_type"]||options[:translated_type]
|
22
|
+
@origin_locale = options["origin_locale"]||options[:origin_locale]
|
23
|
+
@destination_locale = options["destination_locale"]|| options[:destination_locale]
|
24
|
+
end
|
25
|
+
|
26
|
+
def perform(options = nil)
|
27
|
+
translatable = Kernel.const_get(@translated_type).find(@translated_id)
|
28
|
+
translatable.update_all_attributes_translation(@destination_locale, @origin_locale)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module HasManyTranslations
|
2
|
+
# An extension module for the +has_many+ association with translations.
|
3
|
+
module Translations
|
4
|
+
# Returns all translations for given language.
|
5
|
+
|
6
|
+
def by_lang(lang)
|
7
|
+
all(:conditions => "#{aliased_table_name}.locale_code eq '#{lang}'")
|
8
|
+
end
|
9
|
+
|
10
|
+
# def by_locale(locale)
|
11
|
+
# first(:conditions => { :locale => locale.to_s })
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# def by_locales(locales)
|
15
|
+
# all(:conditions => { :locale => locales.map(&:to_s) })
|
16
|
+
# end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), 'has_many_translations', '*.rb')].each{|f| require f }
|
2
|
+
|
3
|
+
require 'rtranslate'
|
4
|
+
require 'activequeue'
|
5
|
+
|
6
|
+
# The base module that gets included in ActiveRecord::Base. See the documentation for
|
7
|
+
# HasManyTranslations::ClassMethods for more useful information.
|
8
|
+
module HasManyTranslations
|
9
|
+
|
10
|
+
|
11
|
+
def self.included(base) # :nodoc:
|
12
|
+
base.class_eval do
|
13
|
+
extend ClassMethods
|
14
|
+
extend Translated
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def translated(options = {}, &block)
|
20
|
+
return if translated?
|
21
|
+
|
22
|
+
include Options
|
23
|
+
include TranslationJobs
|
24
|
+
include Creation
|
25
|
+
include Translations
|
26
|
+
prepare_translated_options(options)
|
27
|
+
has_many :translations, options, &block
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
extend Configuration
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
ActiveRecord::Base.send(:include, HasManyTranslations)
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: has_many_translations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 3
|
10
|
+
version: 0.3.3
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- opengotham
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-02 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activerecord
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 11
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
version: 2.1.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: shoulda
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: mocha
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: opengotham-rtranslate
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
type: :runtime
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: activequeue
|
81
|
+
prerelease: false
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
type: :runtime
|
92
|
+
version_requirements: *id005
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: settingslogic
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
105
|
+
type: :runtime
|
106
|
+
version_requirements: *id006
|
107
|
+
description: Keep a DRY multilingual translation of your ActiveRecord models' textual attributes
|
108
|
+
email: mjording@openogotham.com
|
109
|
+
executables: []
|
110
|
+
|
111
|
+
extensions: []
|
112
|
+
|
113
|
+
extra_rdoc_files:
|
114
|
+
- README.rdoc
|
115
|
+
files:
|
116
|
+
- .gitignore
|
117
|
+
- README.rdoc
|
118
|
+
- Rakefile
|
119
|
+
- VERSION
|
120
|
+
- generators/has_many_translations/has_many_translations_generator.rb
|
121
|
+
- generators/has_many_translations/templates/initializer.rb
|
122
|
+
- generators/has_many_translations/templates/migration.rb
|
123
|
+
- has_many_translations.gemspec
|
124
|
+
- init.rb
|
125
|
+
- lib/has_many_translations.rb
|
126
|
+
- lib/has_many_translations/configuration.rb
|
127
|
+
- lib/has_many_translations/control.rb
|
128
|
+
- lib/has_many_translations/creation.rb
|
129
|
+
- lib/has_many_translations/hmt_settings.rb
|
130
|
+
- lib/has_many_translations/options.rb
|
131
|
+
- lib/has_many_translations/translated.rb
|
132
|
+
- lib/has_many_translations/translation.rb
|
133
|
+
- lib/has_many_translations/translation_jobs.rb
|
134
|
+
- lib/has_many_translations/translations.rb
|
135
|
+
- lib/translation_spec.rb
|
136
|
+
- tasks/has_many_translations_tasks.rake
|
137
|
+
has_rdoc: true
|
138
|
+
homepage: http://github.com/opengotham/has_many_translations
|
139
|
+
licenses: []
|
140
|
+
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options:
|
143
|
+
- --charset=UTF-8
|
144
|
+
require_paths:
|
145
|
+
- lib
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
+
none: false
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
hash: 3
|
152
|
+
segments:
|
153
|
+
- 0
|
154
|
+
version: "0"
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
hash: 3
|
161
|
+
segments:
|
162
|
+
- 0
|
163
|
+
version: "0"
|
164
|
+
requirements: []
|
165
|
+
|
166
|
+
rubyforge_project:
|
167
|
+
rubygems_version: 1.3.7
|
168
|
+
signing_key:
|
169
|
+
specification_version: 3
|
170
|
+
summary: Makes models' speak in tongues
|
171
|
+
test_files: []
|
172
|
+
|