has_many_translations 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|