globalize2 0.1.0 → 0.2.0
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 +2 -1
- data/README.textile +27 -143
- data/VERSION +1 -1
- data/generators/templates/db_backend_migration.rb +3 -3
- data/globalize2.gemspec +30 -49
- data/init.rb +1 -8
- data/lib/globalize.rb +15 -0
- data/lib/globalize/active_record.rb +195 -0
- data/lib/globalize/active_record/adapter.rb +80 -0
- data/lib/globalize/active_record/attributes.rb +25 -0
- data/lib/globalize/active_record/migration.rb +40 -0
- data/lib/{globalize/i18n → i18n}/missing_translations_log_handler.rb +8 -8
- data/lib/{globalize/i18n → i18n}/missing_translations_raise_handler.rb +3 -5
- data/test/active_record/fallbacks_test.rb +102 -0
- data/test/{model/active_record → active_record}/migration_test.rb +21 -26
- data/test/{model/active_record → active_record}/sti_translated_test.rb +4 -30
- data/test/active_record/translates_test.rb +87 -0
- data/test/active_record/translation_class_test.rb +30 -0
- data/test/active_record/validation_tests.rb +75 -0
- data/test/active_record_test.rb +451 -0
- data/test/data/models.rb +16 -0
- data/test/data/schema.rb +23 -7
- data/test/i18n/missing_translations_test.rb +6 -6
- data/test/test_helper.rb +55 -15
- metadata +33 -46
- data/lib/globalize/backend/chain.rb +0 -102
- data/lib/globalize/backend/pluralizing.rb +0 -37
- data/lib/globalize/backend/static.rb +0 -61
- data/lib/globalize/load_path.rb +0 -63
- data/lib/globalize/locale/fallbacks.rb +0 -63
- data/lib/globalize/locale/language_tag.rb +0 -81
- data/lib/globalize/model/active_record.rb +0 -56
- data/lib/globalize/model/active_record/adapter.rb +0 -100
- data/lib/globalize/model/active_record/translated.rb +0 -174
- data/lib/globalize/translation.rb +0 -32
- data/lib/locale/root.yml +0 -3
- data/lib/rails_edge_load_path_patch.rb +0 -40
- data/notes.textile +0 -51
- data/test/backends/chained_test.rb +0 -175
- data/test/backends/pluralizing_test.rb +0 -63
- data/test/backends/static_test.rb +0 -147
- data/test/data/locale/all.yml +0 -2
- data/test/data/locale/de-DE.yml +0 -2
- data/test/data/locale/en-US.yml +0 -2
- data/test/data/locale/en-US/module.yml +0 -2
- data/test/data/locale/fi-FI/module.yml +0 -2
- data/test/data/locale/root.yml +0 -0
- data/test/load_path_test.rb +0 -49
- data/test/locale/fallbacks_test.rb +0 -154
- data/test/locale/language_tag_test.rb +0 -130
- data/test/model/active_record/translated_test.rb +0 -487
- data/test/translation_test.rb +0 -54
@@ -0,0 +1,80 @@
|
|
1
|
+
module Globalize
|
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_reader :record, :cache, :stash
|
7
|
+
|
8
|
+
def initialize(record)
|
9
|
+
@record = record
|
10
|
+
@cache = Attributes.new
|
11
|
+
@stash = Attributes.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch(locale, attr_name)
|
15
|
+
cache.contains?(locale, attr_name) ?
|
16
|
+
cache.read(locale, attr_name) :
|
17
|
+
cache.write(locale, attr_name, fetch_attribute(locale, attr_name))
|
18
|
+
end
|
19
|
+
|
20
|
+
def write(locale, attr_name, value)
|
21
|
+
stash.write(locale, attr_name, value)
|
22
|
+
cache.write(locale, attr_name, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def save_translations!
|
26
|
+
stash.each do |locale, attrs|
|
27
|
+
translation = record.translations.find_or_initialize_by_locale(locale.to_s)
|
28
|
+
attrs.each { |attr_name, value| translation[attr_name] = value }
|
29
|
+
translation.save!
|
30
|
+
end
|
31
|
+
stash.clear
|
32
|
+
end
|
33
|
+
|
34
|
+
def reset
|
35
|
+
cache.clear
|
36
|
+
# stash.clear
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def fetch_translation(locale)
|
42
|
+
locale = locale.to_sym
|
43
|
+
record.translations.loaded? ? record.translations.detect { |t| t.locale == locale } :
|
44
|
+
record.translations.by_locale(locale)
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_translations(locale)
|
48
|
+
# only query if not already included with :include => translations
|
49
|
+
record.translations.loaded? ? record.translations :
|
50
|
+
record.translations.by_locales(Globalize.fallbacks(locale))
|
51
|
+
end
|
52
|
+
|
53
|
+
def fetch_attribute(locale, attr_name)
|
54
|
+
translations = fetch_translations(locale)
|
55
|
+
value, requested_locale = nil, locale
|
56
|
+
|
57
|
+
Globalize.fallbacks(locale).each do |fallback|
|
58
|
+
translation = translations.detect { |t| t.locale == fallback }
|
59
|
+
value = translation && translation.send(attr_name)
|
60
|
+
locale = fallback && break if value
|
61
|
+
end
|
62
|
+
|
63
|
+
set_metadata(value, :locale => locale, :requested_locale => requested_locale)
|
64
|
+
value
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_metadata(object, metadata)
|
68
|
+
if object.respond_to?(:translation_metadata)
|
69
|
+
object.translation_metadata.merge!(meta_data)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def translation_metadata_accessor(object)
|
74
|
+
return if obj.respond_to?(:translation_metadata)
|
75
|
+
class << object; attr_accessor :translation_metadata end
|
76
|
+
object.translation_metadata ||= {}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Helper class for storing values per locale. Used by Globalize::Adapter
|
2
|
+
# to stash and cache attribute values.
|
3
|
+
module Globalize
|
4
|
+
module ActiveRecord
|
5
|
+
class Attributes < Hash
|
6
|
+
def [](locale)
|
7
|
+
locale = locale.to_sym
|
8
|
+
self[locale] = {} unless has_key?(locale)
|
9
|
+
self.fetch(locale)
|
10
|
+
end
|
11
|
+
|
12
|
+
def contains?(locale, attr_name)
|
13
|
+
self[locale].has_key?(attr_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def read(locale, attr_name)
|
17
|
+
self[locale][attr_name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def write(locale, attr_name, value)
|
21
|
+
self[locale][attr_name] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
module Migration
|
4
|
+
def create_translation_table!(fields)
|
5
|
+
translated_attribute_names.each do |f|
|
6
|
+
raise MigrationMissingTranslatedField, "Missing translated field #{f}" unless fields[f]
|
7
|
+
end
|
8
|
+
|
9
|
+
fields.each do |name, type|
|
10
|
+
if translated_attribute_names.include?(name) && ![:string, :text].include?(type)
|
11
|
+
raise BadMigrationFieldType, "Bad field type for #{name}, should be :string or :text"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
self.connection.create_table(translation_table_name) do |t|
|
16
|
+
t.references self.table_name.singularize
|
17
|
+
t.string :locale
|
18
|
+
fields.each do |name, type|
|
19
|
+
t.column name, type
|
20
|
+
end
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
|
24
|
+
self.connection.add_index(translation_table_name, "#{self.table_name.singularize}_id", :name => translation_index_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def translation_index_name
|
28
|
+
require 'digest/sha1'
|
29
|
+
# FIXME what's the max size of an index name?
|
30
|
+
index_name = "index_#{translation_table_name}_on_#{self.table_name.singularize}_id"
|
31
|
+
index_name.size < 50 ? index_name : "index_#{Digest::SHA1.hexdigest(index_name)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def drop_translation_table!
|
35
|
+
self.connection.remove_index(translation_table_name, :name => translation_index_name) rescue nil
|
36
|
+
self.connection.drop_table(translation_table_name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -2,10 +2,10 @@
|
|
2
2
|
# but additionally logs missing translations to a given log.
|
3
3
|
#
|
4
4
|
# Useful for identifying missing translations during testing.
|
5
|
-
#
|
6
|
-
# E.g.
|
7
5
|
#
|
8
|
-
#
|
6
|
+
# E.g.
|
7
|
+
#
|
8
|
+
# require 'globalize/i18n/missing_translations_log_handler'
|
9
9
|
# I18n.missing_translations_logger = RAILS_DEFAULT_LOGGER
|
10
10
|
# I18n.exception_handler = :missing_translations_log_handler
|
11
11
|
#
|
@@ -16,7 +16,7 @@
|
|
16
16
|
|
17
17
|
module I18n
|
18
18
|
@@missing_translations_logger = nil
|
19
|
-
|
19
|
+
|
20
20
|
class << self
|
21
21
|
def missing_translations_logger
|
22
22
|
@@missing_translations_logger ||= begin
|
@@ -24,18 +24,18 @@ module I18n
|
|
24
24
|
Logger.new(STDOUT)
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def missing_translations_logger=(logger)
|
29
29
|
@@missing_translations_logger = logger
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def missing_translations_log_handler(exception, locale, key, options)
|
33
33
|
if MissingTranslationData === exception
|
34
34
|
missing_translations_logger.warn(exception.message)
|
35
|
-
return exception.message
|
35
|
+
return exception.message
|
36
36
|
else
|
37
37
|
raise exception
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
41
|
-
end
|
41
|
+
end
|
@@ -2,10 +2,10 @@
|
|
2
2
|
# but also raises on missing translations.
|
3
3
|
#
|
4
4
|
# Useful for identifying missing translations during testing.
|
5
|
-
#
|
6
|
-
# E.g.
|
7
5
|
#
|
8
|
-
#
|
6
|
+
# E.g.
|
7
|
+
#
|
8
|
+
# require 'globalize/i18n/missing_translations_raise_handler'
|
9
9
|
# I18n.exception_handler = :missing_translations_raise_handler
|
10
10
|
module I18n
|
11
11
|
class << self
|
@@ -13,8 +13,6 @@ module I18n
|
|
13
13
|
raise exception
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
17
|
-
# self.exception_handler = :missing_translations_raise_handler
|
18
16
|
end
|
19
17
|
|
20
18
|
I18n.exception_handler = :missing_translations_raise_handler
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../data/models')
|
3
|
+
|
4
|
+
if I18n.respond_to?(:fallbacks)
|
5
|
+
class TranslatedTest < ActiveSupport::TestCase
|
6
|
+
def setup
|
7
|
+
I18n.locale = :'en-US'
|
8
|
+
I18n.fallbacks.clear
|
9
|
+
reset_db!
|
10
|
+
ActiveRecord::Base.locale = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
I18n.fallbacks.clear
|
15
|
+
end
|
16
|
+
|
17
|
+
test "keeping one field in new locale when other field is changed" do
|
18
|
+
I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
|
19
|
+
post = Post.create :subject => 'foo'
|
20
|
+
I18n.locale = 'de-DE'
|
21
|
+
post.content = 'bar'
|
22
|
+
assert_equal 'foo', post.subject
|
23
|
+
end
|
24
|
+
|
25
|
+
test "modifying non-required field in a new locale" do
|
26
|
+
I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
|
27
|
+
post = Post.create :subject => 'foo'
|
28
|
+
I18n.locale = 'de-DE'
|
29
|
+
post.content = 'bar'
|
30
|
+
assert post.save
|
31
|
+
end
|
32
|
+
|
33
|
+
test "resolves a simple fallback" do
|
34
|
+
I18n.locale = 'de-DE'
|
35
|
+
post = Post.create :subject => 'foo'
|
36
|
+
I18n.locale = 'de'
|
37
|
+
post.subject = 'baz'
|
38
|
+
post.content = 'bar'
|
39
|
+
post.save
|
40
|
+
I18n.locale = 'de-DE'
|
41
|
+
assert_equal 'foo', post.subject
|
42
|
+
assert_equal 'bar', post.content
|
43
|
+
end
|
44
|
+
|
45
|
+
test "resolves a simple fallback without reloading" do
|
46
|
+
I18n.locale = 'de-DE'
|
47
|
+
post = Post.new :subject => 'foo'
|
48
|
+
I18n.locale = 'de'
|
49
|
+
post.subject = 'baz'
|
50
|
+
post.content = 'bar'
|
51
|
+
I18n.locale = 'de-DE'
|
52
|
+
assert_equal 'foo', post.subject
|
53
|
+
assert_equal 'bar', post.content
|
54
|
+
end
|
55
|
+
|
56
|
+
test "resolves a complex fallback without reloading" do
|
57
|
+
I18n.fallbacks.map 'de' => %w(en he)
|
58
|
+
I18n.locale = 'de'
|
59
|
+
post = Post.new
|
60
|
+
I18n.locale = 'en'
|
61
|
+
post.subject = 'foo'
|
62
|
+
I18n.locale = 'he'
|
63
|
+
post.subject = 'baz'
|
64
|
+
post.content = 'bar'
|
65
|
+
I18n.locale = 'de'
|
66
|
+
assert_equal 'foo', post.subject
|
67
|
+
assert_equal 'bar', post.content
|
68
|
+
end
|
69
|
+
|
70
|
+
test 'fallbacks with lots of locale switching' do
|
71
|
+
I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
|
72
|
+
post = Post.create :subject => 'foo'
|
73
|
+
|
74
|
+
I18n.locale = :'de-DE'
|
75
|
+
assert_equal 'foo', post.subject
|
76
|
+
|
77
|
+
I18n.locale = :'en-US'
|
78
|
+
post.update_attribute :subject, 'bar'
|
79
|
+
|
80
|
+
I18n.locale = :'de-DE'
|
81
|
+
assert_equal 'bar', post.subject
|
82
|
+
end
|
83
|
+
|
84
|
+
test 'fallbacks with lots of locale switching' do
|
85
|
+
I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
|
86
|
+
child = Child.create :content => 'foo'
|
87
|
+
|
88
|
+
I18n.locale = :'de-DE'
|
89
|
+
assert_equal 'foo', child.content
|
90
|
+
|
91
|
+
I18n.locale = :'en-US'
|
92
|
+
child.update_attribute :content, 'bar'
|
93
|
+
|
94
|
+
I18n.locale = :'de-DE'
|
95
|
+
assert_equal 'bar', child.content
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# TODO should validate_presence_of take fallbacks into account? maybe we need
|
101
|
+
# an extra validation call, or more options for validate_presence_of.
|
102
|
+
|
@@ -1,36 +1,32 @@
|
|
1
|
-
require File.
|
2
|
-
require '
|
3
|
-
require 'globalize/model/active_record'
|
4
|
-
|
5
|
-
# Hook up model translation
|
6
|
-
ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
|
7
|
-
|
8
|
-
# Load Post model
|
9
|
-
require File.join( File.dirname(__FILE__), '..', '..', 'data', 'models' )
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../data/models')
|
10
3
|
|
11
4
|
class MigrationTest < ActiveSupport::TestCase
|
12
5
|
def setup
|
13
|
-
reset_db!
|
6
|
+
reset_db!
|
7
|
+
Post.drop_translation_table!
|
14
8
|
end
|
15
9
|
|
16
10
|
test 'globalize table added' do
|
17
|
-
assert !Post.connection.table_exists?(
|
18
|
-
assert !Post.connection.index_exists?(
|
19
|
-
|
20
|
-
|
21
|
-
assert Post.connection.
|
22
|
-
|
23
|
-
|
11
|
+
assert !Post.connection.table_exists?(:post_translations)
|
12
|
+
assert !Post.connection.index_exists?(:post_translations, :post_id)
|
13
|
+
|
14
|
+
Post.create_translation_table!(:subject => :string, :content => :text)
|
15
|
+
assert Post.connection.table_exists?(:post_translations)
|
16
|
+
assert Post.connection.index_exists?(:post_translations, :post_id)
|
17
|
+
|
18
|
+
columns = Post.connection.columns(:post_translations)
|
19
|
+
assert locale = columns.detect { |c| c.name == 'locale' }
|
24
20
|
assert_equal :string, locale.type
|
25
|
-
assert subject = columns.detect {|c| c.name == 'subject' }
|
21
|
+
assert subject = columns.detect { |c| c.name == 'subject' }
|
26
22
|
assert_equal :string, subject.type
|
27
|
-
assert content = columns.detect {|c| c.name == 'content' }
|
23
|
+
assert content = columns.detect { |c| c.name == 'content' }
|
28
24
|
assert_equal :text, content.type
|
29
|
-
assert post_id = columns.detect {|c| c.name == 'post_id' }
|
25
|
+
assert post_id = columns.detect { |c| c.name == 'post_id' }
|
30
26
|
assert_equal :integer, post_id.type
|
31
|
-
assert created_at = columns.detect {|c| c.name == 'created_at' }
|
27
|
+
assert created_at = columns.detect { |c| c.name == 'created_at' }
|
32
28
|
assert_equal :datetime, created_at.type
|
33
|
-
assert updated_at = columns.detect {|c| c.name == 'updated_at' }
|
29
|
+
assert updated_at = columns.detect { |c| c.name == 'updated_at' }
|
34
30
|
assert_equal :datetime, updated_at.type
|
35
31
|
end
|
36
32
|
|
@@ -46,13 +42,13 @@ class MigrationTest < ActiveSupport::TestCase
|
|
46
42
|
end
|
47
43
|
|
48
44
|
test 'exception on missing field inputs' do
|
49
|
-
assert_raise Globalize::
|
45
|
+
assert_raise Globalize::MigrationMissingTranslatedField do
|
50
46
|
Post.create_translation_table! :content => :text
|
51
47
|
end
|
52
48
|
end
|
53
49
|
|
54
50
|
test 'exception on bad input type' do
|
55
|
-
assert_raise Globalize::
|
51
|
+
assert_raise Globalize::BadMigrationFieldType do
|
56
52
|
Post.create_translation_table! :subject => :string, :content => :integer
|
57
53
|
end
|
58
54
|
end
|
@@ -104,16 +100,15 @@ class MigrationTest < ActiveSupport::TestCase
|
|
104
100
|
end
|
105
101
|
|
106
102
|
test 'globalize table dropped when table has long name' do
|
103
|
+
UltraLongModelNameWithoutProper.drop_translation_table!
|
107
104
|
UltraLongModelNameWithoutProper.create_translation_table!(
|
108
105
|
:subject => :string, :content => :text
|
109
106
|
)
|
110
|
-
|
111
107
|
UltraLongModelNameWithoutProper.drop_translation_table!
|
112
108
|
|
113
109
|
assert !UltraLongModelNameWithoutProper.connection.table_exists?(
|
114
110
|
:ultra_long_model_name_without_proper_translations
|
115
111
|
)
|
116
|
-
|
117
112
|
assert !UltraLongModelNameWithoutProper.connection.index_exists?(
|
118
113
|
:ultra_long_model_name_without_proper_translations,
|
119
114
|
:ultra_long_model_name_without_proper_id
|
@@ -1,22 +1,10 @@
|
|
1
|
-
require File.
|
2
|
-
require '
|
3
|
-
require 'globalize/model/active_record'
|
4
|
-
|
5
|
-
# Hook up model translation
|
6
|
-
ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
|
7
|
-
|
8
|
-
# Load Post model
|
9
|
-
require File.join( File.dirname(__FILE__), '..', '..', 'data', 'models' )
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../data/models')
|
10
3
|
|
11
4
|
class StiTranslatedTest < ActiveSupport::TestCase
|
12
5
|
def setup
|
13
6
|
I18n.locale = :'en-US'
|
14
|
-
|
15
|
-
reset_db! File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'schema.rb'))
|
16
|
-
end
|
17
|
-
|
18
|
-
def teardown
|
19
|
-
I18n.fallbacks.clear
|
7
|
+
reset_db!
|
20
8
|
end
|
21
9
|
|
22
10
|
test "works with simple dynamic finders" do
|
@@ -43,20 +31,6 @@ class StiTranslatedTest < ActiveSupport::TestCase
|
|
43
31
|
assert_equal [ 'content' ], child.changed
|
44
32
|
end
|
45
33
|
|
46
|
-
test 'fallbacks with lots of locale switching' do
|
47
|
-
I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
|
48
|
-
child = Child.create :content => 'foo'
|
49
|
-
|
50
|
-
I18n.locale = :'de-DE'
|
51
|
-
assert_equal 'foo', child.content
|
52
|
-
|
53
|
-
I18n.locale = :'en-US'
|
54
|
-
child.update_attribute :content, 'bar'
|
55
|
-
|
56
|
-
I18n.locale = :'de-DE'
|
57
|
-
assert_equal 'bar', child.content
|
58
|
-
end
|
59
|
-
|
60
34
|
test "saves all locales, even after locale switching" do
|
61
35
|
child = Child.new :content => 'foo'
|
62
36
|
I18n.locale = 'de-DE'
|
@@ -72,4 +46,4 @@ class StiTranslatedTest < ActiveSupport::TestCase
|
|
72
46
|
I18n.locale = 'he-IL'
|
73
47
|
assert_equal 'baz', child.content
|
74
48
|
end
|
75
|
-
end
|
49
|
+
end
|