clicrdv_easy_translatable 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/lib/translatable/active_record/adapter.rb +67 -0
- data/lib/translatable/active_record/attributes.rb +27 -0
- data/lib/translatable/active_record/class_methods.rb +60 -0
- data/lib/translatable/active_record/instance_methods.rb +195 -0
- data/lib/translatable/active_record/macro.rb +96 -0
- data/lib/translatable/active_record/relation.rb +31 -0
- data/lib/translatable/active_record/translate.rb +10 -0
- data/lib/translatable/active_record.rb +12 -0
- data/lib/translatable/default.rb +5 -0
- data/lib/translatable/version.rb +3 -0
- data/lib/translatable.rb +68 -0
- metadata +203 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 50704672bccd72c0179e2acd83114e675d7c2301c564ba81a011fa64ac6c3f8c
|
4
|
+
data.tar.gz: cf389801803b8822a553c465e9e0568dbbfb2b5a320626a600ced51181b94588
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 84a2c46f6ad7282664eaf750596f14a348e0a1a289fdac4dd0edb8deeed6af1d4b500a8b469f0614dd44492f4581818c01be9d96d12c1ab739414917078efed6
|
7
|
+
data.tar.gz: 7da00eed5cb246f1ace53df770e3887412c84854899a2189467d1c052467681239e883e7481227b07c4bebef643fbdce7776572853e12fa0cf35efc39f73c5a7
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2008, 2009, 2010 Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader, ClicRDV
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Translatable [![Gem Version](https://badge.fury.io/rb/easy_translatable.png)](http://badge.fury.io/rb/easy_translatable) [![Code Climate](https://codeclimate.com/github/clicrdv/translatable.png)](https://codeclimate.com/github/clicrdv/translatable) [![Build Status](https://travis-ci.org/clicrdv/translatable.png)](https://travis-ci.org/clicrdv/translatable) [![Coverage Status](https://coveralls.io/repos/clicrdv/translatable/badge.png)](https://coveralls.io/r/clicrdv/translatable)
|
2
|
+
## Requirements
|
3
|
+
|
4
|
+
* ActiveRecord 3.2
|
5
|
+
* I18n
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
To install just use:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem install easy_translatable
|
13
|
+
```
|
14
|
+
|
15
|
+
When using bundler put this in your Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'easy_translatable', '~> 0.1.0'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Todo
|
22
|
+
|
23
|
+
- cleanup
|
24
|
+
- doc
|
25
|
+
- migration/model for translation table
|
26
|
+
|
27
|
+
|
28
|
+
Copyright (c) 2008, 2009, 2010 Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader, ClicRDV
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Translatable
|
2
|
+
module ActiveRecord
|
3
|
+
class Adapter
|
4
|
+
# The cache caches attributes that already were looked up for read access.
|
5
|
+
# The stash keeps track of new or changed values that need to be saved.
|
6
|
+
attr_accessor :record, :stash, :translations
|
7
|
+
private :record=, :stash=
|
8
|
+
|
9
|
+
def initialize(record)
|
10
|
+
self.record = record
|
11
|
+
self.stash = Attributes.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch_stash(locale, name)
|
15
|
+
value = stash.read(locale, name)
|
16
|
+
return value if value
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def stash_contains?(locale, name)
|
21
|
+
stash.contains?(locale, name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch(locale, name, value = nil)
|
25
|
+
value = stash_contains?(locale, name) ? fetch_stash(locale, name) : fetch_attribute(locale, name, value)
|
26
|
+
|
27
|
+
return value
|
28
|
+
end
|
29
|
+
|
30
|
+
def write(locale, name, value)
|
31
|
+
stash.write(locale, name, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def save_translations!
|
35
|
+
stash.reject {|locale, attrs| attrs.empty?}.each do |locale, attrs|
|
36
|
+
translations = record.translations_by_locale[locale]
|
37
|
+
attrs.each { |name, value|
|
38
|
+
translation = translations && translations[name] ||
|
39
|
+
record.translations.build(:locale => locale.to_s, :scope => record.class.table_name, :key => name)
|
40
|
+
translation['record_id'] = record.id
|
41
|
+
translation['value'] = value
|
42
|
+
translation.save!
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
reset
|
47
|
+
end
|
48
|
+
|
49
|
+
def reset
|
50
|
+
stash.clear
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def fetch_attribute(locale, name, value = nil)
|
56
|
+
if value.nil?
|
57
|
+
translation = record.translation_for(locale, name)
|
58
|
+
return translation && translation.send(:value)
|
59
|
+
else
|
60
|
+
translation = record.translation_for_serialized(locale, name, value)
|
61
|
+
return translation
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Helper class for storing values per locale. Used by Globalize::Adapter
|
2
|
+
# to stash and cache attribute values.
|
3
|
+
|
4
|
+
module Translatable
|
5
|
+
module ActiveRecord
|
6
|
+
class Attributes < Hash # TODO: Think about using HashWithIndifferentAccess ?
|
7
|
+
def [](locale)
|
8
|
+
locale = locale.to_sym
|
9
|
+
self[locale] = {} unless has_key?(locale)
|
10
|
+
self.fetch(locale)
|
11
|
+
end
|
12
|
+
|
13
|
+
def contains?(locale, name)
|
14
|
+
self[locale].has_key?(name.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(locale, name)
|
18
|
+
self[locale][name.to_s]
|
19
|
+
end
|
20
|
+
|
21
|
+
def write(locale, name, value)
|
22
|
+
#raise 'z' if value.nil? # TODO
|
23
|
+
self[locale][name.to_s] = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Translatable
|
2
|
+
module ActiveRecord
|
3
|
+
module ClassMethods
|
4
|
+
def translation_class
|
5
|
+
Translatable.translation_class
|
6
|
+
end
|
7
|
+
|
8
|
+
def translated?(name)
|
9
|
+
included = translated_attribute_names.detect { |attr| attr.is_a?(Hash) ? attr.keys.include?(name.to_sym) : attr == name.to_sym }
|
10
|
+
!included.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def translated_attr_accessor(name)
|
16
|
+
define_method(:"#{name}=") do |value|
|
17
|
+
write_attribute(name, value)
|
18
|
+
end
|
19
|
+
define_method(name) do |*args|
|
20
|
+
self.read_attribute(name, {:locale => args.detect {|a| !a.is_a? Hash }})
|
21
|
+
end
|
22
|
+
alias_method :"#{name}_before_type_cast", name
|
23
|
+
end
|
24
|
+
|
25
|
+
def locale_from(args)
|
26
|
+
args.detect {|a| !a.is_a? Hash }
|
27
|
+
end
|
28
|
+
|
29
|
+
def translations_accessor(name)
|
30
|
+
define_method(:"#{name}_translations") do
|
31
|
+
translations.each_with_object(HashWithIndifferentAccess.new) do |translation, result|
|
32
|
+
result[translation.locale] = translation.send(name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
define_method(:"#{name}_translations=") do |value|
|
36
|
+
value.each do |(locale, value)|
|
37
|
+
write_attribute name, value, :locale => locale
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def condition_callback(attr_names, callback)
|
43
|
+
Proc.new { |rec|
|
44
|
+
attr_names.any? do |translatable_attr|
|
45
|
+
callback && rec.changes.keys.include?(translatable_attr.to_s)
|
46
|
+
end
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
%w(after_save before_save after_update before_update).each do |cb|
|
51
|
+
class_eval %{
|
52
|
+
def on_#{cb}_callback(attr_names, callback)
|
53
|
+
#{cb} callback, :if => condition_callback(attr_names, callback)
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module Translatable
|
2
|
+
module ActiveRecord
|
3
|
+
module InstanceMethods
|
4
|
+
delegate :translated_locales, :to => :translations
|
5
|
+
|
6
|
+
def translatable
|
7
|
+
@translatable ||= Adapter.new(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
# Maintian Rails 3.0.x compatibility ..
|
12
|
+
if base.method_defined?(:assign_attributes)
|
13
|
+
base.class_eval %{
|
14
|
+
def assign_attributes(attributes, *options)
|
15
|
+
with_given_locale(attributes) { super }
|
16
|
+
end
|
17
|
+
}
|
18
|
+
else
|
19
|
+
base.class_eval %{
|
20
|
+
def attributes=(attributes, *args)
|
21
|
+
with_given_locale(attributes) { super }
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_attributes!(attributes, *args)
|
25
|
+
with_given_locale(attributes) { super }
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_attributes(attributes, *args)
|
29
|
+
with_given_locale(attributes) { super }
|
30
|
+
end
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Always call the super method, the attribute is translatable and we asked a translated model
|
36
|
+
def write_attribute(name, value, options = {}, &block)
|
37
|
+
if r_translated?(name) && translate?
|
38
|
+
options = {:locale => Translatable.locale}.merge(options)
|
39
|
+
|
40
|
+
# We don't want to track any changes, but save the new value in a translation hash
|
41
|
+
|
42
|
+
translatable.write(options[:locale], name, value)
|
43
|
+
else
|
44
|
+
super(name, value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def translate
|
49
|
+
@translate_me = true
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def end_translate
|
54
|
+
@translate_me = nil
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def reload(options = nil)
|
59
|
+
@translate_me = false
|
60
|
+
translation_caches.clear
|
61
|
+
translated_attribute_names.each { |name| @attributes.reset(name.to_s) }
|
62
|
+
translatable.reset
|
63
|
+
super(options)
|
64
|
+
end
|
65
|
+
|
66
|
+
def save(*)
|
67
|
+
@translate_me = false
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
def translate?
|
72
|
+
!@translate_me.nil? && @translate_me
|
73
|
+
end
|
74
|
+
|
75
|
+
def read_attribute(name, options = {}, &block)
|
76
|
+
options = {:translated => true, :locale => Translatable.locale}.merge(options)
|
77
|
+
if r_translated?(name) and options[:translated]
|
78
|
+
serialized_value = super(name) if self.attributes.has_key?(name)
|
79
|
+
if translate? && (value = translatable.fetch(options[:locale] || Translatable.locale, name, serialized_value))
|
80
|
+
value
|
81
|
+
else
|
82
|
+
super(name)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
super(name)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def r_translated?(name)
|
90
|
+
self.class.translated?(name)
|
91
|
+
end
|
92
|
+
|
93
|
+
def translated_attributes
|
94
|
+
translated_attribute_names.inject({}) do |attributes, name|
|
95
|
+
attributes.merge(name.to_s => translation.send(name))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# This method is basically the method built into Rails
|
100
|
+
# but we have to pass {:translated => false}
|
101
|
+
def untranslated_attributes
|
102
|
+
attrs = {}
|
103
|
+
attribute_names.each do |name|
|
104
|
+
attrs[name] = read_attribute(name, {:translated => false})
|
105
|
+
end
|
106
|
+
attrs
|
107
|
+
end
|
108
|
+
|
109
|
+
def translation_for(locale, name, build_if_missing = false)
|
110
|
+
unless translation_caches["#{locale}_#{name}"]
|
111
|
+
# Fetch translations from database as those in the translation collection may be incomplete
|
112
|
+
_translation = translations.detect{|t| t.locale.to_s == locale.to_s && t.key.to_s == name.to_s }
|
113
|
+
_translation ||= translations.build(:locale => locale) if build_if_missing
|
114
|
+
translation_caches["#{locale}_#{name}"] = _translation if _translation
|
115
|
+
end
|
116
|
+
translation_caches["#{locale}_#{name}"]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Fetch translations per keys in the form
|
120
|
+
# model_attr_serialized_attribute
|
121
|
+
# E.G.1: ```Model``` has attribute ```config``` which has a key ```auto_save```
|
122
|
+
# will have a translation key ```config_auto_save```
|
123
|
+
# E.G.2: ```Model``` has attribute ```config``` which is an array of ```user_id => ([0-9]+)```
|
124
|
+
# will have translations key containing the index: ```config_user_id_0```, ```config_user_id_1```...
|
125
|
+
def translation_for_serialized(locale, name, value, build_if_missing = false)
|
126
|
+
|
127
|
+
unless translation_caches["#{locale}_#{name}"]
|
128
|
+
engine = self.attributes.has_key?(name) ? nil : JSON
|
129
|
+
deserialized_value = engine.nil? ? value : engine.load(value)
|
130
|
+
|
131
|
+
only = self.translated_serialized_attributes[name.to_sym].map(&:to_s)
|
132
|
+
if only.empty?
|
133
|
+
regex_keys = /\A#{name.to_s}_([a-z_]+[a-z])(?:_([0-9]*))?\Z/
|
134
|
+
else
|
135
|
+
regex_keys = /\A#{name.to_s}_(#{only.join('|')})(?:_([0-9]*))?\Z/
|
136
|
+
end
|
137
|
+
# Fetch translations from database as those in the translation collection may be incomplete
|
138
|
+
_translations = translations.select{|t| t.locale.to_s == locale.to_s && t.key.to_s =~ regex_keys }
|
139
|
+
_translations.each do |t|
|
140
|
+
matched = t.key.match regex_keys
|
141
|
+
unless matched.nil?
|
142
|
+
if matched.size == 3
|
143
|
+
sub_attr = matched[1]
|
144
|
+
index = matched[2].to_i
|
145
|
+
value[index][sub_attr] = t.value if value.size > index && value[index].has_key?(sub_attr)
|
146
|
+
elsif matched.size == 3
|
147
|
+
sub_attr = matched[1]
|
148
|
+
value[sub_attr] = t.value if value.has_key?(sub_attr)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
translation_caches["#{locale}_#{name}"] = value
|
154
|
+
end
|
155
|
+
translation_caches["#{locale}_#{name}"]
|
156
|
+
end
|
157
|
+
|
158
|
+
def translation_caches
|
159
|
+
@translation_caches ||= {}
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
def with_given_locale(attributes, &block)
|
164
|
+
attributes.symbolize_keys! if attributes.respond_to?(:symbolize_keys!)
|
165
|
+
|
166
|
+
locale = respond_to?(:locale=) ? attributes.try(:[], :locale) :
|
167
|
+
attributes.try(:delete, :locale)
|
168
|
+
|
169
|
+
if locale && !respond_to?(:locale=)
|
170
|
+
already = translate?
|
171
|
+
translate unless translate?
|
172
|
+
Translatable.with_locale(locale, &block)
|
173
|
+
end_translate unless already
|
174
|
+
else
|
175
|
+
yield
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def translations_by_locale(&block)
|
180
|
+
translations.each_with_object(HashWithIndifferentAccess.new) do |t, hash|
|
181
|
+
hash[t.locale] ||= HashWithIndifferentAccess.new
|
182
|
+
hash[t.locale][t.key] = block_given? ? block.call(t) : t
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
protected
|
187
|
+
|
188
|
+
def save_translations!
|
189
|
+
translatable.save_translations!
|
190
|
+
translation_caches.clear
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Translatable
|
2
|
+
module ActiveRecord
|
3
|
+
module Macro
|
4
|
+
|
5
|
+
def translatable(*attr_names)
|
6
|
+
|
7
|
+
options = attr_names.extract_options!
|
8
|
+
setup_translatable!(options) unless translatable?
|
9
|
+
|
10
|
+
attr_names = attr_names.map(&:to_sym)
|
11
|
+
attr_names -= translated_attribute_names if defined?(translated_attribute_names)
|
12
|
+
|
13
|
+
if attr_names.present?
|
14
|
+
|
15
|
+
attr_names.each do |attr_name|
|
16
|
+
# Create accessors for the attribute.
|
17
|
+
translated_attr_accessor(attr_name)
|
18
|
+
translations_accessor(attr_name)
|
19
|
+
|
20
|
+
# Add attribute to the list.
|
21
|
+
self.translated_attribute_names << attr_name
|
22
|
+
if options[:only]
|
23
|
+
self.translated_serialized_attributes[attr_name] = options[:only] ? options[:only] : []
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
on_after_save_callback(attr_names, options[:after_save]) if options[:after_save]
|
28
|
+
on_before_save_callback(attr_names, options[:before_save]) if options[:before_save]
|
29
|
+
on_after_update_callback(attr_names, options[:after_update]) if options[:after_update]
|
30
|
+
on_before_update_callback(attr_names, options[:before_update]) if options[:before_update]
|
31
|
+
|
32
|
+
Translatable.add_translatable self
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def class_name
|
38
|
+
@class_name ||= begin
|
39
|
+
class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].downcase.camelize
|
40
|
+
pluralize_table_names ? class_name.singularize : class_name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def translatable?
|
45
|
+
included_modules.include?(InstanceMethods)
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
def setup_translatable!(options)
|
50
|
+
options[:table_name] ||= Translatable.translation_class.table_name
|
51
|
+
options[:foreign_key] ||= 'record_id'
|
52
|
+
options[:conditions] ||= {}
|
53
|
+
options[:after_save] ||= false
|
54
|
+
options[:before_save] ||= false
|
55
|
+
|
56
|
+
class_attribute :translated_attribute_names, :translation_options, :fallbacks_for_empty_translations, :translated_serialized_attributes
|
57
|
+
self.translated_attribute_names = []
|
58
|
+
self.translation_options = options
|
59
|
+
self.fallbacks_for_empty_translations = options[:fallbacks_for_empty_translations]
|
60
|
+
self.translated_serialized_attributes = Hash.new
|
61
|
+
|
62
|
+
include InstanceMethods
|
63
|
+
extend ClassMethods
|
64
|
+
|
65
|
+
translation_class.table_name = options[:table_name]
|
66
|
+
|
67
|
+
has_many :translations, conditions(options), :class_name => translation_class.name,
|
68
|
+
:foreign_key => options[:foreign_key],
|
69
|
+
:dependent => :destroy,
|
70
|
+
:extend => HasManyExtensions,
|
71
|
+
:autosave => false
|
72
|
+
|
73
|
+
after_create :save_translations!
|
74
|
+
after_update :save_translations!
|
75
|
+
end
|
76
|
+
|
77
|
+
def conditions(options)
|
78
|
+
table_name = self.table_name
|
79
|
+
proc {
|
80
|
+
c = options[:conditions]
|
81
|
+
c = self.instance_eval(&c) if c.is_a?(Proc)
|
82
|
+
c.merge!(:scope => table_name, :locale => Translatable.locale)
|
83
|
+
|
84
|
+
where(c)
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
module HasManyExtensions
|
91
|
+
def find_or_initialize_by_locale(locale)
|
92
|
+
with_locale(locale.to_s).first || build(:locale => locale.to_s)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Translatable
|
2
|
+
module ActiveRecord
|
3
|
+
module Relation
|
4
|
+
def exec_queries
|
5
|
+
queries = super
|
6
|
+
|
7
|
+
return queries.map(&:translate) if @translate_records
|
8
|
+
|
9
|
+
queries
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def translate
|
15
|
+
@translate_records = true
|
16
|
+
|
17
|
+
self.eager_load(:translations)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Railtie < Rails::Railtie
|
24
|
+
|
25
|
+
initializer "easy_translatable.configure_rails_initialization" do
|
26
|
+
ActiveSupport.on_load(:active_record) do
|
27
|
+
ActiveRecord::Relation.send :prepend, Translatable::ActiveRecord::Relation
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end if defined?(Rails)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path '../active_record/class_methods.rb', __FILE__
|
2
|
+
require File.expand_path '../active_record/instance_methods.rb', __FILE__
|
3
|
+
require File.expand_path '../active_record/attributes.rb', __FILE__
|
4
|
+
require File.expand_path '../active_record/adapter.rb', __FILE__
|
5
|
+
require File.expand_path '../active_record/macro.rb', __FILE__
|
6
|
+
require File.expand_path '../active_record/translate.rb', __FILE__
|
7
|
+
require File.expand_path '../active_record/relation.rb', __FILE__
|
8
|
+
|
9
|
+
module Translatable
|
10
|
+
module ActiveRecord
|
11
|
+
end
|
12
|
+
end
|
data/lib/translatable.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'translatable/default.rb')
|
2
|
+
|
3
|
+
module Translatable
|
4
|
+
|
5
|
+
@@translatable ||= Hash.new
|
6
|
+
@@translation_class ||= nil
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def locale
|
10
|
+
read_locale || I18n.locale
|
11
|
+
end
|
12
|
+
|
13
|
+
def locale=(locale)
|
14
|
+
set_locale(locale)
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_locale(locale, &block)
|
18
|
+
previous_locale = read_locale
|
19
|
+
begin
|
20
|
+
set_locale(locale)
|
21
|
+
result = yield(locale)
|
22
|
+
ensure
|
23
|
+
set_locale(previous_locale)
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def translation_class_name=(klass)
|
29
|
+
@@translation_class = klass.constantize
|
30
|
+
end
|
31
|
+
def translation_class
|
32
|
+
@@translation_class ||= nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Hash of models that are translatable (values are the attrs)
|
36
|
+
def list
|
37
|
+
@@translatable ||= Hash.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_translatable(klass)
|
41
|
+
if @@translatable.has_key? klass.name
|
42
|
+
klass.translated_attribute_names.each do |attr|
|
43
|
+
@@translatable[klass.name] << attr unless @@translatable[klass.name].include?(attr)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
@@translatable[klass.name] = klass.translated_attribute_names
|
47
|
+
end
|
48
|
+
|
49
|
+
unless klass.translated_serialized_attributes.nil?
|
50
|
+
@@translatable[klass.name].map! do |attr|
|
51
|
+
serialized = klass.translated_serialized_attributes.reject{|k,v| k != attr}
|
52
|
+
serialized.empty? ? attr : serialized
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def read_locale
|
60
|
+
Thread.current[:translatable_locale]
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_locale(locale)
|
64
|
+
Thread.current[:translatable_locale] = locale.try(:to_sym)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clicrdv_easy_translatable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Bonaud
|
8
|
+
- Charly Poly
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2022-03-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 6.1.3
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 6.1.3
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: database_cleaner
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.6.0
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.6.0
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - "~>"
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.6.0
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.6.0
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: mocha
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pathname_local
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
type: :development
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: test_declarative
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: friendly_id
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: sqlite3
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: rake
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
type: :development
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: rdoc
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
type: :development
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
- !ruby/object:Gem::Dependency
|
147
|
+
name: coveralls
|
148
|
+
requirement: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
type: :development
|
154
|
+
prerelease: false
|
155
|
+
version_requirements: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
description: Handle translations for AR models into a single table. And provide a
|
161
|
+
helper to select translated values.
|
162
|
+
email: paul.bonaud@clicrdv.com
|
163
|
+
executables: []
|
164
|
+
extensions: []
|
165
|
+
extra_rdoc_files: []
|
166
|
+
files:
|
167
|
+
- LICENSE
|
168
|
+
- README.md
|
169
|
+
- lib/translatable.rb
|
170
|
+
- lib/translatable/active_record.rb
|
171
|
+
- lib/translatable/active_record/adapter.rb
|
172
|
+
- lib/translatable/active_record/attributes.rb
|
173
|
+
- lib/translatable/active_record/class_methods.rb
|
174
|
+
- lib/translatable/active_record/instance_methods.rb
|
175
|
+
- lib/translatable/active_record/macro.rb
|
176
|
+
- lib/translatable/active_record/relation.rb
|
177
|
+
- lib/translatable/active_record/translate.rb
|
178
|
+
- lib/translatable/default.rb
|
179
|
+
- lib/translatable/version.rb
|
180
|
+
homepage: http://rubygems.org/gems/single_translatable
|
181
|
+
licenses:
|
182
|
+
- MIT
|
183
|
+
metadata: {}
|
184
|
+
post_install_message:
|
185
|
+
rdoc_options: []
|
186
|
+
require_paths:
|
187
|
+
- lib
|
188
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: '0'
|
193
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
requirements: []
|
199
|
+
rubygems_version: 3.2.3
|
200
|
+
signing_key:
|
201
|
+
specification_version: 4
|
202
|
+
summary: Handle translations for AR models into a single table
|
203
|
+
test_files: []
|