easy_translatable 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -0
- data/lib/translatable/active_record/adapter.rb +43 -0
- data/lib/translatable/active_record/attributes.rb +27 -0
- data/lib/translatable/active_record/class_methods.rb +42 -0
- data/lib/translatable/active_record/instance_methods.rb +99 -0
- data/lib/translatable/active_record/macro.rb +96 -0
- data/lib/translatable/active_record/translate.rb +5 -58
- data/lib/translatable/active_record.rb +5 -0
- data/lib/translatable/default.rb +3 -55
- data/lib/translatable/version.rb +1 -1
- data/lib/translatable.rb +57 -0
- metadata +7 -2
- data/lib/translatable/config.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b058f1009620ef66e0eb9a2425af27841e3f4d61
|
4
|
+
data.tar.gz: 97066aa3b7af92612aff25f95896be71a0f70174
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29f96b7b1d821caafea24001ba52d15350527d3df865819a60b17cfafea5fa2aab40459c04be70935b624597ed8f90b68905c79ba2703474d72e9fc63625259e
|
7
|
+
data.tar.gz: 40ddc1c5111c3b3d1ac02bf2d75d87e8349e24d043b0b345b361a28e804e227f122aa17f30e2c833eda46090b4e74b8dca7b929df4bc6084da11ea80b135444e
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Translatable
|
2
|
+
|
3
|
+
## Requirements
|
4
|
+
|
5
|
+
* ActiveRecord 3.2
|
6
|
+
* I18n
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
To install just use:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem install easy_translatable
|
14
|
+
```
|
15
|
+
|
16
|
+
When using bundler put this in your Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'easy_translatable', '~> 0.1.0'
|
20
|
+
```
|
@@ -0,0 +1,43 @@
|
|
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)
|
25
|
+
value = stash_contains?(locale, name) ? fetch_stash(locale, name) : fetch_attribute(locale, name)
|
26
|
+
|
27
|
+
return value
|
28
|
+
end
|
29
|
+
|
30
|
+
def write(locale, name, value)
|
31
|
+
stash.write(locale, name, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def fetch_attribute(locale, name)
|
37
|
+
translation = record.translation_for(locale, name, false)
|
38
|
+
return translation && translation.send(:value)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
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,42 @@
|
|
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
|
+
translated_attribute_names.include?(name.to_sym)
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def translated_attr_accessor(name)
|
15
|
+
define_method(:"#{name}=") do |value|
|
16
|
+
write_attribute(name, value)
|
17
|
+
end
|
18
|
+
define_method(name) do |*args|
|
19
|
+
self.read_attribute(name, {:locale => args.detect {|a| !a.is_a? Hash }})
|
20
|
+
end
|
21
|
+
alias_method :"#{name}_before_type_cast", name
|
22
|
+
end
|
23
|
+
|
24
|
+
def locale_from(args)
|
25
|
+
args.detect {|a| !a.is_a? Hash }
|
26
|
+
end
|
27
|
+
|
28
|
+
def translations_accessor(name)
|
29
|
+
define_method(:"#{name}_translations") do
|
30
|
+
translations.each_with_object(HashWithIndifferentAccess.new) do |translation, result|
|
31
|
+
result[translation.locale] = translation.send(name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
define_method(:"#{name}_translations=") do |value|
|
35
|
+
value.each do |(locale, value)|
|
36
|
+
write_attribute name, value, :locale => locale
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,99 @@
|
|
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
|
+
# only Rails > 3.1.x compatibility
|
12
|
+
base.class_eval %{
|
13
|
+
def attributes=(attributes, *args)
|
14
|
+
with_given_locale(attributes) { super }
|
15
|
+
end
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
#def write_attribute(name, value, options = {})
|
21
|
+
# if translated?(name)
|
22
|
+
# options = {:locale => Translatable.locale}.merge(options)
|
23
|
+
#
|
24
|
+
# # Dirty tracking, paraphrased from
|
25
|
+
# # ActiveRecord::AttributeMethods::Dirty#write_attribute.
|
26
|
+
# name_str = name.to_s
|
27
|
+
# if attribute_changed?(name_str)
|
28
|
+
# # If there's already a change, delete it if this undoes the change.
|
29
|
+
# old = changed_attributes[name_str]
|
30
|
+
# changed_attributes.delete(name_str) if value == old
|
31
|
+
# else
|
32
|
+
# # If there's not a change yet, record it.
|
33
|
+
# old = globalize.fetch(options[:locale], name)
|
34
|
+
# old = old.clone if old.duplicable?
|
35
|
+
# changed_attributes[name_str] = old if value != old
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# translatable.write(options[:locale], name, value)
|
39
|
+
# else
|
40
|
+
# super(name, value)
|
41
|
+
# end
|
42
|
+
#end
|
43
|
+
|
44
|
+
def translate
|
45
|
+
@translate_me = true
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def translate?
|
50
|
+
!@translate_me.nil? && @translate_me = true
|
51
|
+
end
|
52
|
+
|
53
|
+
def read_attribute(name, options = {})
|
54
|
+
options = {:translated => true, :locale => nil}.merge(options)
|
55
|
+
if translated?(name) and options[:translated]
|
56
|
+
if translate? && (value = translatable.fetch(options[:locale] || Translatable.locale, name))
|
57
|
+
value
|
58
|
+
else
|
59
|
+
super(name)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
super(name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def translated?(name)
|
67
|
+
self.class.translated?(name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def translation_for(locale, name, build_if_missing = false)
|
71
|
+
unless translation_caches["#{locale}_#{name}"]
|
72
|
+
# Fetch translations from database as those in the translation collection may be incomplete
|
73
|
+
_translation = translations.detect{|t| t.locale.to_s == locale.to_s && t.key.to_s == name.to_s }
|
74
|
+
_translation ||= translations.build(:locale => locale) if build_if_missing
|
75
|
+
translation_caches["#{locale}_#{name}"] = _translation if _translation
|
76
|
+
end
|
77
|
+
translation_caches["#{locale}_#{name}"]
|
78
|
+
end
|
79
|
+
|
80
|
+
def translation_caches
|
81
|
+
@translation_caches ||= {}
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def with_given_locale(attributes, &block)
|
86
|
+
attributes.symbolize_keys! if attributes.respond_to?(:symbolize_keys!)
|
87
|
+
|
88
|
+
locale = respond_to?(:locale=) ? attributes.try(:[], :locale) :
|
89
|
+
attributes.try(:delete, :locale)
|
90
|
+
|
91
|
+
if locale
|
92
|
+
Translatable.with_locale(locale, &block)
|
93
|
+
else
|
94
|
+
yield
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
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
|
+
translation_class.instance_eval %{
|
15
|
+
attr_accessible :#{attr_names.join(', :')}
|
16
|
+
}
|
17
|
+
|
18
|
+
attr_names.each do |attr_name|
|
19
|
+
# Detect and apply serialization.
|
20
|
+
serializer = self.serialized_attributes[attr_name.to_s]
|
21
|
+
if serializer.present?
|
22
|
+
if defined?(::ActiveRecord::Coders::YAMLColumn) &&
|
23
|
+
serializer.is_a?(::ActiveRecord::Coders::YAMLColumn)
|
24
|
+
|
25
|
+
serializer = serializer.object_class
|
26
|
+
end
|
27
|
+
|
28
|
+
translation_class.send :serialize, attr_name, serializer
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create accessors for the attribute.
|
32
|
+
translated_attr_accessor(attr_name)
|
33
|
+
translations_accessor(attr_name)
|
34
|
+
|
35
|
+
# Add attribute to the list.
|
36
|
+
self.translated_attribute_names << attr_name
|
37
|
+
end
|
38
|
+
|
39
|
+
Translatable.add_translatable self
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def class_name
|
45
|
+
@class_name ||= begin
|
46
|
+
class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].downcase.camelize
|
47
|
+
pluralize_table_names ? class_name.singularize : class_name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def translatable?
|
52
|
+
included_modules.include?(InstanceMethods)
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def setup_translatable!(options)
|
57
|
+
options[:table_name] ||= Translatable.translation_class.table_name
|
58
|
+
options[:foreign_key] ||= 'record_id'
|
59
|
+
options[:conditions] ||= ''
|
60
|
+
|
61
|
+
class_attribute :translated_attribute_names, :translation_options, :fallbacks_for_empty_translations
|
62
|
+
self.translated_attribute_names = []
|
63
|
+
self.translation_options = options
|
64
|
+
self.fallbacks_for_empty_translations = options[:fallbacks_for_empty_translations]
|
65
|
+
|
66
|
+
# Alias original relation method so we can safely override it in ClassMethods.
|
67
|
+
#class << self
|
68
|
+
# alias relation_without_globalize relation
|
69
|
+
#end
|
70
|
+
|
71
|
+
include InstanceMethods
|
72
|
+
extend ClassMethods
|
73
|
+
|
74
|
+
translation_class.table_name = options[:table_name]
|
75
|
+
|
76
|
+
has_many :translations, :class_name => translation_class.name,
|
77
|
+
:foreign_key => options[:foreign_key],
|
78
|
+
:conditions => options[:conditions],
|
79
|
+
:dependent => :destroy,
|
80
|
+
:extend => HasManyExtensions,
|
81
|
+
:autosave => false
|
82
|
+
|
83
|
+
#after_create :save_translations!
|
84
|
+
#after_update :save_translations!
|
85
|
+
|
86
|
+
translation_class.instance_eval %{ attr_accessible :lang }
|
87
|
+
end
|
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
|
@@ -1,66 +1,13 @@
|
|
1
1
|
module Translatable
|
2
2
|
module ActiveRecord
|
3
3
|
module Translate
|
4
|
-
def translate(lang, *attr_names)
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
# Ask for translations
|
6
|
+
def translate
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
else
|
13
|
-
to_translate = attr_names
|
14
|
-
q = self
|
15
|
-
end
|
16
|
-
|
17
|
-
index = 0
|
18
|
-
to_translate.each do |attr|
|
19
|
-
|
20
|
-
if is_in_scope? attr
|
21
|
-
index += 1
|
22
|
-
if Translatable::Config.list[model].include?(attr.to_s)
|
23
|
-
q = q.joins("LEFT JOIN snippets sn#{index} ON sn#{index}.record_id = #{model}.id AND sn#{index}.scope = '#{model}' AND sn#{index}.group_id = #{model}.group_id AND sn#{index}.lang = '#{lang}' AND sn#{index}.key = '#{attr}'").
|
24
|
-
select("COALESCE(sn#{index}.value, '') AS #{attr}")
|
25
|
-
else
|
26
|
-
q = q.select("#{model}.#{attr}")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
q.scoped
|
33
|
-
else
|
34
|
-
self.scoped
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def has_select_values?
|
42
|
-
self.scoped.select_values.length > 0
|
43
|
-
end
|
44
|
-
|
45
|
-
def is_in_scope?(attr)
|
46
|
-
|
47
|
-
if has_select_values?
|
48
|
-
select_values = self.scoped.select_values.map { |one|
|
49
|
-
if one.is_a?(String)
|
50
|
-
one.split(',').map(&:lstrip).map(&:rstrip)
|
51
|
-
elsif one.is_a?(Symbol)
|
52
|
-
one.to_s
|
53
|
-
end
|
54
|
-
}.flatten
|
55
|
-
is_present = select_values.reject{ |value| !value.is_a?(String) || !value.split('.').last == '*' && !value.split('.').last == attr }
|
56
|
-
if is_present.nil?
|
57
|
-
false
|
58
|
-
else
|
59
|
-
is_present.length > 0
|
60
|
-
end
|
61
|
-
else
|
62
|
-
true
|
63
|
-
end
|
8
|
+
q = self.scoped
|
9
|
+
q.map(&:translate)
|
10
|
+
q
|
64
11
|
|
65
12
|
end
|
66
13
|
|
@@ -1,3 +1,8 @@
|
|
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__
|
1
6
|
require File.expand_path '../active_record/translate.rb', __FILE__
|
2
7
|
|
3
8
|
module Translatable
|
data/lib/translatable/default.rb
CHANGED
@@ -1,57 +1,5 @@
|
|
1
|
-
require
|
1
|
+
require 'active_record'
|
2
2
|
require File.expand_path '../active_record.rb', __FILE__
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
include Singleton
|
8
|
-
attr_accessor :models, :clicrdv_models
|
9
|
-
|
10
|
-
CLICRDV_TYPES = %w(model customfield preprosition)
|
11
|
-
|
12
|
-
def self.is_a_customfield?(str)
|
13
|
-
str.match(/\Acustomfields/)
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.is_a_preposition?(str)
|
17
|
-
str.match(/\Apreposition/)
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.is_an_ar_model?(str)
|
21
|
-
is_it = false
|
22
|
-
begin
|
23
|
-
model = str.singularize.camelize.constantize
|
24
|
-
is_it = true if model.superclass.name == 'ActiveRecord::Base' && str != 'customfields'
|
25
|
-
rescue NameError
|
26
|
-
end
|
27
|
-
is_it
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.type str
|
31
|
-
if is_model str
|
32
|
-
'model'
|
33
|
-
elsif is_customfield str
|
34
|
-
'customfield'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Get all translatables models
|
39
|
-
# @param array of things to translate
|
40
|
-
# @return an array of three elements : ARModels, Customfields and Preprositions
|
41
|
-
def self.clicrdv_models(hash=nil)
|
42
|
-
if hash.nil? || hash.empty?
|
43
|
-
process_me = Config.list
|
44
|
-
else
|
45
|
-
process_me = hash
|
46
|
-
end
|
47
|
-
# Don't remove the v in the block parameters |k,v| this because of Ruby 1.8.7
|
48
|
-
ar_models = process_me.reject{ |k,v| !is_an_ar_model?(k) }
|
49
|
-
customfields = process_me.reject{ |k,v| !is_a_customfield?(k) }
|
50
|
-
prepositions = process_me.reject{ |k,v| !is_a_preposition?(k) }
|
51
|
-
|
52
|
-
[ar_models, customfields, prepositions]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
ActiveRecord::Base.extend(Translatable::ActiveRecord::Translate)
|
4
|
+
ActiveRecord::Base.extend(Translatable::ActiveRecord::Translate)
|
5
|
+
ActiveRecord::Base.extend(Translatable::ActiveRecord::Macro)
|
data/lib/translatable/version.rb
CHANGED
data/lib/translatable.rb
CHANGED
@@ -1 +1,58 @@
|
|
1
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
|
+
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
|
+
set_locale(locale)
|
20
|
+
result = yield(locale)
|
21
|
+
set_locale(previous_locale)
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
def translation_class_name=(klass)
|
26
|
+
@@translation_class = klass.constantize
|
27
|
+
end
|
28
|
+
def translation_class
|
29
|
+
@@translation_class ||= nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Hash of models that are translatable (values are the attrs)
|
33
|
+
def list
|
34
|
+
@@translatable ||= Hash.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_translatable(klass)
|
38
|
+
if @@translatable.has_key? klass.name
|
39
|
+
klass.translated_attribute_names.each do |attr|
|
40
|
+
@@translatable[klass.name] << attr unless @@translatable[klass.name].include?(attr)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
@@translatable[klass.name] = klass.translated_attribute_names
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def read_locale
|
50
|
+
Thread.current[:translatable_locale]
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_locale(locale)
|
54
|
+
Thread.current[:translatable_locale] = locale.try(:to_sym)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easy_translatable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Bonaud
|
@@ -60,10 +60,15 @@ executables: []
|
|
60
60
|
extensions: []
|
61
61
|
extra_rdoc_files: []
|
62
62
|
files:
|
63
|
+
- README.md
|
63
64
|
- lib/translatable.rb
|
64
65
|
- lib/translatable/active_record.rb
|
66
|
+
- lib/translatable/active_record/adapter.rb
|
67
|
+
- lib/translatable/active_record/attributes.rb
|
68
|
+
- lib/translatable/active_record/class_methods.rb
|
69
|
+
- lib/translatable/active_record/instance_methods.rb
|
70
|
+
- lib/translatable/active_record/macro.rb
|
65
71
|
- lib/translatable/active_record/translate.rb
|
66
|
-
- lib/translatable/config.rb
|
67
72
|
- lib/translatable/default.rb
|
68
73
|
- lib/translatable/version.rb
|
69
74
|
homepage: http://rubygems.org/gems/single_translatable
|