embedded_localization 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ bundler_args: --without development
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - 2.0.0
8
+ - ruby-head
9
+ - ree
10
+ matrix:
11
+ allow_failures:
12
+ - rbx-18mode
13
+ - rbx-19mode
14
+ - rvm: ruby-head
15
+ - rvm: ree
16
+ - rvm: 1.8.7
17
+ - rvm: 1.9.2
18
+ - rvm: rbx-18mode
19
+ branches:
20
+ only:
21
+ - master
data/Gemfile CHANGED
@@ -2,3 +2,12 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in embedded_localization.gemspec
4
4
  gemspec
5
+
6
+ gem 'rake'
7
+
8
+ group :test do
9
+ gem 'rspec', "~> 2.14"
10
+ gem 'activerecord', "~> 4.0.0"
11
+ gem 'i18n'
12
+ gem 'sqlite3'
13
+ end
data/README.md ADDED
@@ -0,0 +1,372 @@
1
+ # Embedded Localization
2
+
3
+ [![Build Status](https://secure.travis-ci.org/tilo/embedded_localization.png?branch=master)](http://travis-ci.org/tilo/embedded_locallization)
4
+
5
+ `embedded_loalization` is compatible with Rails 3 and Rails 4, and adds model translations to ActiveRecord. `embedded_localization` is compatible with and builds on the new [I18n API in Ruby on Rails](http://guides.rubyonrails.org/i18n.html)
6
+
7
+ `embedded_localization` is very lightweight, and allows you to transparently store translations of attributes right inside each record — no extra database tables needed to store the localization data! Make sure that your database default encoding is UTF-8 or UFT-16.
8
+
9
+ Model translations with `embedded_localization` use default ActiveRecord features and do not limit any ActiveRecord functionality.
10
+
11
+ On top of that, you also get tools for checking into which locales an attribute was translated to, as well as for checking overall translation coverage.
12
+
13
+
14
+ ## Requirements
15
+
16
+ * ActiveRecord > 3.0.0.rc # Tested with Rails 4.0.2, 3.2.18, 3.2.2
17
+ * I18n
18
+
19
+ ## Installation
20
+
21
+ To install Embedded_Localization, use:
22
+
23
+ $ gem install embedded_localization
24
+
25
+
26
+ ## Translated Models
27
+
28
+ Adding localization to a table is very simple. Just add a text field named `i18n` to the table, and you are ready to go! This allows you to add translated fields via the helper method `translates` in the model.
29
+
30
+ Optionally, you can also keep a DB field with the same name as the translated field, which will store the values for the `I18n.default_locale`.
31
+
32
+ Model translations allow you to translate your models’ attribute values. The attribute type needs to be string or text.
33
+
34
+ ### Example 1
35
+
36
+ Let's assume you have a table for movie genres, and you want to translate the names and the descriptions of the genres. Simply define your `Genre` model as follows:
37
+
38
+ class Genre < ActiveRecord::Base
39
+ translates :name, :description
40
+ end
41
+
42
+ In the DB migration, you just need to add the `i18n` text field:
43
+
44
+ class CreateGenres < ActiveRecord::Migration
45
+ def self.change
46
+ create_table :genres do |t|
47
+ t.text :i18n # stores ALL the translated attributes; persisted as a Hash
48
+
49
+ t.timestamps
50
+ end
51
+ end
52
+ end
53
+
54
+ ### Example 2
55
+
56
+ Obviously you can't do SQL queries against tanslated fields which are stored in the `i18n` text field.
57
+ To eliviate this problem, you can also define a normal DB attribute with the same name as your translated attribute, and it will store the value for your `I18n.default_locale`.
58
+
59
+ This way you can always do SQL queries against the values in the `I18n.default_locale`.
60
+
61
+ To do this, using the same model as in example 1, you can modify your migration as follows:
62
+
63
+ class CreateGenres < ActiveRecord::Migration
64
+ def self.change
65
+ create_table :genres do |t|
66
+ t.text :i18n # stores the translated attributes; persisted as a Hash
67
+
68
+ t.string :name # allows us to do SQL queries
69
+
70
+ t.timestamps
71
+ end
72
+ end
73
+ end
74
+
75
+ # Usage
76
+
77
+ In your code you can modify the values of your translated attributes in two ways.
78
+
79
+ ## Using Setters / Getters
80
+
81
+ Using the built-in getter/setter methods you can set the values for any locale directly, even though
82
+ you are using your own locale.
83
+
84
+ I18n.locale = :en
85
+ g = Genre.first
86
+ g.name = 'science fiction'
87
+
88
+ # even though you are using the :en locale, you can still set the values for other locales:
89
+
90
+ g.set_localized_attribute( :name, :jp, "サイエンスフィクション" )
91
+ g.set_localized_attribute( :name, :ko, "공상 과학 소설" )
92
+
93
+ g.name # => 'science fiction'
94
+ g.name(:jp) # => "サイエンスフィクション"
95
+ g.name(:ko) # => "공상 과학 소설"
96
+
97
+ g.get_localized_attribute( :name, :jp ) # => "サイエンスフィクション"
98
+ g.get_localized_attribute( :name, :ko ) # => "공상 과학 소설"
99
+
100
+ ## Tweaking `I18n.locale`
101
+
102
+ By manipulating the `I18n.locale`. This is what happens if you have user's with different locales entering values into a database.
103
+
104
+ The controller is just assigning the new value via `name=` , but `embedded_localization` gem takes care of storing it for the correct given locale.
105
+
106
+ I18n.locale = :en
107
+ g = Genre.first
108
+ g.name # => 'science fiction'
109
+
110
+ I18n.locale = :jp
111
+ g.name = "サイエンスフィクション"
112
+
113
+ I18n.locale = :ko
114
+ g.name = "공상 과학 소설"
115
+ g.name # => "공상 과학 소설"
116
+
117
+ I18n.locale = :jp
118
+ g.name # => "サイエンスフィクション"
119
+
120
+ I18n.locale = :en # MAKE SURE to switch back to your default locale if you tweak it
121
+
122
+
123
+ ## SQL Queries against Translated Fields
124
+
125
+ Old `embedded_localization` implementations < 0.2.0 had the drawback that you can not do SQL queries on translated attributes.
126
+
127
+ To eliminate this limitation, you can now define any translated attribute as a first-class database column in your migration.
128
+
129
+ If you define a translated attribute as a column, `embedded_localization` will store the attribute value for I18n.default_locale in that column, so you can search for it.
130
+
131
+ After defining the column, and running the migration, you need to populate the column initially. It will auto-update every time you write while you are using I18n.default_locale .
132
+
133
+ See also Example 2 above.
134
+
135
+ I18n.locale = :en
136
+ g = Genre.first
137
+ g.name = 'science fiction' # in Example 2 this will be stored in the DB column :name as well
138
+ ...
139
+ g.set_localized_attribute( :name, :jp, "サイエンスフィクション" )
140
+ ...
141
+ scifi = Genre.where(:name => "science fiction").first
142
+
143
+ Limitation: You can not search for the translated strings other than for your default locale.
144
+
145
+
146
+ ## Data Migration
147
+
148
+
149
+ Existing data can be migrated from an existing (not-translated) column, into a translated column with the same name as follows:
150
+
151
+ Genre.record_timestamps = false # to not modify your existing timestamps
152
+ Genre.all.each do |g|
153
+ g.name = g.name # the right-hand-side fetches the translation from the i18n attribute hash
154
+ g.save # saves the :name attribute without updating the updated_at timestamp
155
+ end
156
+ Genre.record_timestamps = true
157
+
158
+ ## I18n fallbacks for empty translations
159
+
160
+ It is possible to enable fallbacks for empty translations. It will depend on the configuration setting you have set for I18n translations in your Rails config, or you can enable fallback when you define the translation fields. Currently we only support fallback to `I18n.default_locale`
161
+
162
+ You can enable them by adding the next line to `config/application.rb` (or only `config/environments/production.rb` if you only want them in production)
163
+
164
+
165
+ config.i18n.fallbacks = true # falls back to I18n.default_locale
166
+
167
+
168
+ By default, `embedded_localization` will only use fallbacks when the translation value for the item you've requested is `nil`.
169
+
170
+
171
+ class Genre < ActiveRecord::Base
172
+ translates :name, :description # , :fallbacks => true
173
+ end
174
+
175
+ I18n.locale = :en
176
+ g = Genre.first
177
+ g.name # => 'science fiction'
178
+
179
+ I18n.locale = :jp
180
+ g.name # => "サイエンスフィクション"
181
+
182
+ I18n.locale = :de
183
+ g.name # => nil
184
+
185
+ I18n.fallbacks = true
186
+ I18n.locale = :de
187
+ g.name # => 'science fiction'
188
+
189
+ ## Want some Candy?
190
+
191
+ It's nice to have the values of attributes be set or read with the current locale, but `embedded_localization` offers you a couple of additional features, which can come in handy.
192
+
193
+ ### Class Methods
194
+
195
+ Each class which uses `embedded_localization` will have these additional methods defined:
196
+
197
+ * Klass.translated_attributes
198
+ * Klass.translated?
199
+ * Klass.fallbacks?
200
+
201
+ e.g.:
202
+
203
+ Genre.translated_attributes # => [:name,:description]
204
+ Genre.translated? # => true
205
+ Genre.fallbacks? # => false
206
+
207
+
208
+ ### Instance Methods
209
+
210
+ Each model instance of a class which uses `embedded_localization` will have these additional features:
211
+
212
+ * on-the-fly translations, via `g.name(:locale)`
213
+ * list of translated locales
214
+ * list of translated attributes
215
+ * hash of translation coverage for a given record's attributes or a particular attribute
216
+ * hash of missing translations for a given record's attributes or a particular attribute
217
+ * directly setting and getting attribute values for a given locale; without having to change `I18n.locale`
218
+
219
+ e.g.:
220
+
221
+ g = Genre.where(:name => "science fiction").first
222
+
223
+ # check if an attribute is translated:
224
+ g.translated?(:name) # => true
225
+
226
+ # which attributes are translated?
227
+ g.translated_attributes # => [:description, :name]
228
+
229
+ # check for which locales we have values: (spanning all translated fields)
230
+ g.translated_locales # => [:en]
231
+
232
+ g.set_localized_attribute(:description, :de, "Ich liebe Science Fiction Filme")
233
+
234
+ # check again for which locales we have values: (spanning all translated fields)
235
+ g.translated_locales # => [:en, :de]
236
+
237
+ # show details for which locales the attributes have values for:
238
+ # for all attributes:
239
+ g.translation_coverage # => {:name=>[:en], :description=>[:de]}
240
+
241
+ # for a specific attribute:
242
+ g.translation_coverage(:name) # => [:en]
243
+ g.translation_coverage(:description) # => [:de]
244
+
245
+ # show where translations are missing:
246
+ # for all attributes:
247
+ g.translation_missing # => {:description=>[:en], :name=>[:de]}
248
+
249
+ # for a specific attribute:
250
+ g.translation_missing(:name) # => [:de]
251
+ g.translation_missing(:description) # => [:en]
252
+
253
+
254
+
255
+
256
+ #### translated_locales vs translation_coverage
257
+
258
+ ##### translated_locales
259
+
260
+ `translated_locales` lists the super-set of all locales, including the default locale, even if there is no value set for a specific attribute.
261
+ For a new empty record, this will report `I18n.default_locale`.
262
+
263
+ `translated_locales` reports which translations / languages are possible.
264
+
265
+ ##### translation_coverage
266
+
267
+ `translation_coverage` only lists locales for which a non-nil value is set.
268
+ For a new empty record, this will be empty.
269
+
270
+ `translation_coverage` reports for which languages translations exist (actual values exist).
271
+
272
+ ##### Example
273
+
274
+ I18n.locale = :jp
275
+ g = Genre.first
276
+ g.name # => "サイエンスフィクション"
277
+
278
+ g.name(:en) # => 'science fiction'
279
+ g.name(:ko) # => "공상 과학 소설"
280
+ g.name(:de) # => nil
281
+
282
+ g.translated_locales # => [:en,:jp,:ko]
283
+ g.translated_attributes # => [:name,:description]
284
+ g.translated? # => true
285
+
286
+ g.translation_coverage
287
+ # => {"name"=>["en", "ko", "jp"] , "description"=>["en", "de", "fr", "ko", "jp", "es"]}
288
+
289
+ g.translation_coverage(:name)
290
+ # => {"name"=>["en", "ko", "jp"]}
291
+
292
+ g.translation_missing
293
+ # => {"name"=>["de", "fr", "es"]}
294
+
295
+ g.translation_missing(:display)
296
+ # => {} # this indicates that there are no missing translations for the :display attribute
297
+
298
+ g.get_localized_attribute(:name, :de)
299
+ # => nil
300
+
301
+ g.set_localized_attribute(:name, :de, "Science-Fiction")
302
+ # => "Science-Fiction"
303
+
304
+ ## Motivation
305
+
306
+ A recent project needed some localization support for ActiveRecord model data, but I did not want to clutter the schema with one additional table for each translated model, as globalization3 requires. A second requirement was to allow SQL queries of the fields using the default locale.
307
+
308
+ The advantage of EmbeddedLocalization is that it does not need extra tables, and therefore no joins or additional table lookups to get to the translated data.
309
+
310
+ If your requirements are different, my approach might not work for you. In that case, I recommend to look at the alternative solutions listed below.
311
+
312
+ ## Changes
313
+
314
+ ### 1.1.0 (2014-01-12)
315
+ * adding more rspec tests.
316
+ * improving documentation and README
317
+
318
+ ### 1.0.0 (2014-01-11)
319
+ * adding rspec tests.
320
+ * fixing issue #6: translated fields for new records were not nil
321
+ * fixing issue #7: translation_missing for new records is breaking
322
+
323
+
324
+ ### 0.2.5 (2013-11-02)
325
+ * adding MIT and GPL-2 licenses to gem-spec file; contact me if you need another license
326
+
327
+ ### 0.2.4 (2012-03-02)
328
+ * Issue #5 : bugfix for attr_writer
329
+
330
+ ### 0.2.3 (2012-03-02)
331
+ * Issue #4 : bugfix for attr_writer - no longer updates attributes if value didn't change => timestamps don't change in that case
332
+
333
+ ### 0.2.2 (2012-02-06)
334
+ * bugfix for attr_writer
335
+
336
+ ### 0.2.1 (2012-01-31)
337
+ * bugfix for serialized i18n attribute
338
+
339
+ ### 0.2.0 (2012-01-31)
340
+ * added support for having DB columns for translated attributes, to enable SQL queries in `I18n.default_locale`
341
+
342
+ ### 0.1.4 (2012-01-31)
343
+ * fixed bug with dirty tracking of serialized i18n attribute
344
+ * renamed #fallback? to #fallbacks?
345
+
346
+ ### 0.1.3 Initial Version (2012-01-27)
347
+ * initial version
348
+
349
+
350
+ ## Alternative Solutions
351
+
352
+ * [Mongoid](https://github.com/mongoid/mongoid) - awesome Ruby ORM for MongoDB, which includes in-table localization of attributes (mongoid >= 2.3.0)
353
+ * [Globalize3](https://github.com/svenfuchs/globalize3) - is an awesome gem, but different approach with more tables in the schema.
354
+ * [Veger's fork of Globalize2](http://github.com/veger/globalize2) - uses default AR schema for the default locale, delegates to the translations table for other locales only
355
+ * [TranslatableColumns](http://github.com/iain/translatable_columns) - have multiple languages of the same attribute in a model (Iain Hecker)
356
+ * [localized_record](http://github.com/glennpow/localized_record) - allows records to have localized attributes without any modifications to the database (Glenn Powell)
357
+ * [model_translations](http://github.com/janne/model_translations) - Minimal implementation of Globalize2 style model translations (Jan Andersson)
358
+
359
+ ## Related Solutions
360
+
361
+ * [globalize2_versioning](http://github.com/joshmh/globalize2_versioning) - acts_as_versioned style versioning for globalize2 (Joshua Harvey)
362
+ * [i18n_multi_locales_validations](http://github.com/ZenCocoon/i18n_multi_locales_validations) - multi-locales attributes validations to validates attributes from globalize2 translations models (Sébastien Grosjean)
363
+ * [globalize2 Demo App](http://github.com/svenfuchs/globalize2-demo) - demo application for globalize2 (Sven Fuchs)
364
+ * [migrate_from_globalize1](http://gist.github.com/120867) - migrate model translations from Globalize1 to globalize2 (Tomasz Stachewicz)
365
+ * [easy_globalize2_accessors](http://github.com/astropanic/easy_globalize2_accessors) - easily access (read and write) globalize2-translated fields (astropanic, Tomasz Stachewicz)
366
+ * [globalize2-easy-translate](http://github.com/bsamman/globalize2-easy-translate) - adds methods to easily access or set translated attributes to your model (bsamman)
367
+ * [batch_translations](http://github.com/alvarezrilla/batch_translations) - allow saving multiple globalize2 translations in the same request (Jose Alvarez Rilla)
368
+
369
+
370
+
371
+
372
+
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.licenses = ['MIT','GPL-2']
24
24
  # specify any dependencies here; for example:
25
25
  s.add_development_dependency "rspec"
26
- s.add_development_dependency "activerecord", "~> 3.2.0"
26
+ s.add_development_dependency "activerecord", "~> 4.0.0"
27
27
  s.add_development_dependency "i18n"
28
28
  s.add_development_dependency "sqlite3"
29
29
  # s.add_runtime_dependency "rest-client"
@@ -1,3 +1,3 @@
1
1
  module EmbeddedLocalization
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -8,132 +8,177 @@ I18n.locale = :en
8
8
 
9
9
  describe 'model has translated field with attribute of that same name' do
10
10
  let(:movie){ Movie.new }
11
- let(:title_en){ "Blade Runner" }
12
- let(:title_ru){'аЕЦСЫХИ ОН КЕГБХЧ'}
13
- let(:title_tr){"Ölüm takibi"}
14
- let(:title_de){"Der Blade Runner"}
15
- let(:blade_runner){ Movie.new(:title => title_en) }
16
-
17
- it 'reports it translates' do
18
- Movie.translates?.should be_true
19
- end
20
-
21
- it 'correctly reports the list of translated_attributes' do
22
- Movie.translated_attributes.sort.should eq [:description, :title]
23
- end
24
-
25
- it 'correctly reports the list of translated_attribute_names' do
26
- Movie.translated_attribute_names.sort.should eq [:description, :title]
27
- end
28
-
29
- it 'correcty shows the translated attribute as translated' do
30
- Movie.translated?(:title).should be_true
31
- Movie.translated?(:description).should be_true
32
- end
33
-
34
- it 'correcty shows not translated attribute' do
35
- Movie.translated?(:other).should be_false
36
- end
37
-
38
- it 'correctly reports translated_locales for new record' do
39
- movie.translated_locales.should eq [I18n.default_locale]
40
- end
41
-
42
- it 'correctly reports translation_missing for new record' do
43
- movie.translation_missing.should be_true
44
- end
45
11
 
46
- it 'creates the accessor methods' do
47
- movie.methods.should include(:title)
48
- movie.methods.should include(:title=)
49
- movie.methods.should include(:description)
50
- movie.methods.should include(:description=)
51
- end
52
12
 
53
- it 'correctly reports translated field for new record for default locale' do
54
- movie.title.should be_nil
55
- movie.description.should be_nil
56
- movie.description( I18n.default_locale ).should be_nil
57
- end
13
+ describe "basic things that need to work" do
58
14
 
59
- it 'correctly reports translated field for new record for other locale' do
60
- movie.title(:ko).should be_nil
61
- movie.description(:de).should be_nil
62
- end
15
+ it 'reports it translates' do
16
+ Movie.translates?.should be_true
17
+ end
63
18
 
64
- it 'correctly reports translation_coverage for new record' do
65
- movie.translation_coverage.should eq Hash.new
66
- end
19
+ it 'correctly reports the list of translated_attributes' do
20
+ Movie.translated_attributes.sort.should eq [:description, :title]
21
+ end
67
22
 
68
- it 'correctly reports translation_goverate for attributes of new record' do
69
- movie.translation_coverage(:title).should eq []
70
- movie.translation_coverage(:description).should eq []
71
- end
23
+ it 'correctly reports the list of translated_attribute_names' do
24
+ Movie.translated_attribute_names.sort.should eq [:description, :title]
25
+ end
72
26
 
73
- it 'translated_coverage returns nil for not-translated attributes' do
74
- movie.translation_coverage(:other).should be_nil
75
- end
27
+ it 'correcty shows the translated attribute as translated' do
28
+ Movie.translated?(:title).should be_true
29
+ Movie.translated?(:description).should be_true
30
+ end
76
31
 
77
- it 'correctly reports translation_missing for new record' do
78
- movie.translation_missing.should == {:description=>[:en], :title=>[:en]}
79
- movie.translation_missing(:title).should eq [:en]
80
- movie.translation_missing(:description).should eq [:en]
32
+ it 'correcty shows not translated attribute' do
33
+ Movie.translated?(:other).should be_false
34
+ end
81
35
  end
82
36
 
83
- # when setting all fields in the default locale's languange:
84
- it 'correctly reports translation_missing for updated record' do
85
- movie.title = title_en
86
- movie.description = "an awesome movie"
87
- movie.save
88
- movie.translation_missing.should eq Hash.new
89
- movie.translation_missing(:title).should eq nil
90
- movie.translation_missing(:description).should eq nil
91
- end
37
+ describe "new DB records" do
92
38
 
93
- it 'correctly reports translated_coverage for updated record' do
94
- movie.title = title_en
95
- movie.description = "an awesome movie"
96
- movie.save
97
- movie.translation_coverage.should == {:title=>[:en], :description=>[:en]}
98
- movie.translation_coverage(:title).should eq [:en]
99
- movie.translation_coverage(:description).should eq [:en]
100
- end
101
-
102
- # when setting all fields in additional languanges:
103
- it 'correctly reports values, translation_coverage, translation_missing for updated record' do
104
- movie.title = title_en
105
- movie.description = "an awesome movie"
106
- I18n.locale = :ru
107
- movie.title = title_ru
108
- movie.set_localized_attribute(:title , :tr, title_tr)
109
- I18n.locale = :de
110
- movie.description = "ein grossartiger Film"
111
- I18n.locale = I18n.default_locale # MAKE SURE you switch back to your default
112
- movie.save
113
- movie.reload
114
- movie.title(:en).should eq title_en
115
- movie.title(:tr).should eq title_tr
116
- movie.get_localized_attribute(:title, :ru).should eq title_ru
117
-
118
- # what values are actually present
119
- movie.translation_coverage.should == {:title=>[:en, :ru, :tr], :description=>[:en, :de]}
120
- movie.translation_coverage(:title).should eq [:en,:ru,:tr]
121
- movie.translation_coverage(:description).should eq [:en,:de]
122
-
123
- # what values are missing
124
- movie.translation_missing.should == {:description=>[:ru, :tr], :title=>[:de]}
125
- movie.translation_missing(:title).should eq [:de]
126
- movie.translation_missing(:description).should eq [:ru,:tr]
127
- end
39
+ it 'correctly reports translated_locales for new record' do
40
+ movie.translated_locales.should eq [I18n.default_locale]
41
+ end
42
+
43
+ it 'correctly reports translation_missing for new record' do
44
+ movie.translation_missing.should be_true
45
+ end
46
+
47
+ it 'creates the accessor methods' do
48
+ movie.methods.should include(:title)
49
+ movie.methods.should include(:title=)
50
+ movie.methods.should include(:description)
51
+ movie.methods.should include(:description=)
52
+ end
53
+
54
+ it 'correctly reports translated field for new record for default locale' do
55
+ movie.title.should be_nil
56
+ movie.description.should be_nil
57
+ movie.description( I18n.default_locale ).should be_nil
58
+ end
59
+
60
+ it 'correctly reports translated field for new record for other locale' do
61
+ movie.title(:ko).should be_nil
62
+ movie.description(:de).should be_nil
63
+ end
64
+
65
+ it 'correctly reports translation_coverage for new record' do
66
+ movie.translation_coverage.should eq Hash.new
67
+ end
68
+
69
+ it 'correctly reports translation_goverate for attributes of new record' do
70
+ movie.translation_coverage(:title).should eq []
71
+ movie.translation_coverage(:description).should eq []
72
+ end
73
+
74
+ it 'translated_coverage returns nil for not-translated attributes' do
75
+ movie.translation_coverage(:other).should be_nil
76
+ end
77
+
78
+ it 'correctly reports translation_missing for new record' do
79
+ movie.translation_missing.should == {:description=>[:en], :title=>[:en]}
80
+ movie.translation_missing(:title).should eq [:en]
81
+ movie.translation_missing(:description).should eq [:en]
82
+ end
83
+ end
84
+
85
+
86
+ describe "DB record with pre-set fields" do
87
+ let(:title_en){ "Blade Runner" }
88
+ let(:blade_runner){ Movie.new(:title => title_en) }
89
+
90
+ it 'correctly shows the attribute for new record' do
91
+ blade_runner.title.should eq title_en
92
+ end
93
+ end
94
+
95
+
96
+ describe "updated DB record" do
97
+ let(:title_en){ "Blade Runner" }
98
+ let(:title_ru){'аЕЦСЫХИ ОН КЕГБХЧ'}
99
+ let(:title_tr){"Ölüm takibi"}
100
+ let(:title_de){"Der Blade Runner"}
101
+ let(:blade_runner){ Movie.new(:title => title_en) }
102
+
103
+ describe 'updating just default locale' do
104
+
105
+ before :each do
106
+ movie.title = title_en
107
+ movie.description = "an awesome movie"
108
+ movie.save
109
+ movie.reload
110
+ end
111
+
112
+ # when setting all fields in the default locale's languange:
113
+ it 'correctly reports translation_missing for updated record' do
114
+ movie.translation_missing.should eq Hash.new
115
+ end
116
+
117
+ it 'correctly reports translation_missing for attributes' do
118
+ movie.translation_missing(:title).should eq nil
119
+ movie.translation_missing(:description).should eq nil
120
+ end
121
+
122
+ it 'correctly reports translated_coverage for updated record' do
123
+ movie.translation_coverage.should == {:title=>[:en], :description=>[:en]}
124
+ end
125
+
126
+ it 'correctly reports translated_coverage for attributes' do
127
+ movie.translation_coverage(:title).should eq [:en]
128
+ movie.translation_coverage(:description).should eq [:en]
129
+ end
130
+ end
131
+
132
+ describe 'updating other locales' do
133
+ before :each do
134
+ movie.title = title_en
135
+ movie.description = "an awesome movie"
136
+ I18n.locale = :ru
137
+ movie.title = title_ru
138
+ movie.set_localized_attribute(:title , :tr, title_tr)
139
+ I18n.locale = :de
140
+ movie.description = "ein grossartiger Film"
141
+ I18n.locale = I18n.default_locale # MAKE SURE you switch back to your default loale if you tweak it
142
+ movie.save
143
+ movie.reload
144
+ end
145
+
146
+ it 'can assign the translated field' do
147
+ movie.title = title_en
148
+ movie.save.should be_true
149
+ movie.title.should eq title_en
150
+ movie.title(:en).should eq title_en
151
+ end
152
+
153
+ # when setting all fields in additional languanges:
154
+ it 'correctly reports values for updated record via attr(:locale)' do
155
+ movie.title(:tr).should eq title_tr
156
+ end
157
+
158
+ it 'correctly reports values for updated record via getter' do
159
+ movie.get_localized_attribute(:title, :ru).should eq title_ru
160
+ end
161
+
162
+ it 'correctly reports translation_coverage for updated record' do
163
+ # what values are actually present
164
+ movie.translation_coverage.should == {:title=>[:en, :ru, :tr], :description=>[:en, :de]}
165
+ end
166
+
167
+ it 'correctly reports translation_coverage for attributes' do
168
+ movie.translation_coverage(:title).should eq [:en,:ru,:tr]
169
+ movie.translation_coverage(:description).should eq [:en,:de]
170
+ end
171
+
172
+ it 'correctly reports translation_missing for updated record' do
173
+ # what values are missing
174
+ movie.translation_missing.should == {:description=>[:ru, :tr], :title=>[:de]}
175
+ end
176
+
177
+ it 'correctly reports translation_missing for attributes' do
178
+ movie.translation_missing(:title).should eq [:de]
179
+ movie.translation_missing(:description).should eq [:ru,:tr]
180
+ end
181
+ end
128
182
 
129
- it 'can assign the translated field' do
130
- movie.title = title_en
131
- movie.save.should be_true
132
- movie.title.should eq title_en
133
183
  end
134
-
135
- it 'correctly shows the attribute for new record' do
136
- blade_runner.title.should eq title_en
137
- end
138
-
139
184
  end
@@ -9,131 +9,174 @@ I18n.locale = :en
9
9
 
10
10
  describe 'model has translated field without attribute of that same name' do
11
11
  let(:genre){ Genre.new }
12
- let(:genre_name_en){"Science Fiction"}
13
- let(:genre_name_jp){"サイエンスフィクション"}
14
- let(:genre_name_ko){"공상 과학 소설"}
15
- let(:scifi){ Genre.new(:name => genre_name_en) }
16
12
 
17
- it 'reports it translates' do
18
- Genre.translates?.should be_true
19
- end
20
-
21
- it 'correctly reports the list of translated_attributes' do
22
- Genre.translated_attributes.sort.should eq [:description, :name]
23
- end
24
-
25
- it 'correctly reports the list of translated_attribute_names' do
26
- Genre.translated_attribute_names.sort.should eq [:description, :name]
27
- end
28
-
29
- it 'correcty shows the translated attribute as translated' do
30
- Genre.translated?(:name).should be_true
31
- Genre.translated?(:description).should be_true
32
- end
33
-
34
- it 'correcty shows not translated attribute' do
35
- Genre.translated?(:other).should be_false
36
- end
37
-
38
- it 'correctly reports translated_locales for new record' do
39
- genre.translated_locales.should eq [I18n.default_locale]
40
- end
13
+ describe "basic things that need to work" do
41
14
 
42
- it 'correctly reports translation_missing for new record' do
43
- genre.translation_missing.should be_true
44
- end
15
+ it 'reports it translates' do
16
+ Genre.translates?.should be_true
17
+ end
45
18
 
46
- it 'creates the accessor methods' do
47
- genre.methods.should include(:name)
48
- genre.methods.should include(:name=)
49
- genre.methods.should include(:description)
50
- genre.methods.should include(:description=)
51
- end
19
+ it 'correctly reports the list of translated_attributes' do
20
+ Genre.translated_attributes.sort.should eq [:description, :name]
21
+ end
52
22
 
53
- it 'correctly reports translated field for new record for default locale' do
54
- genre.name.should be_nil
55
- genre.description.should be_nil
56
- genre.description( I18n.default_locale ).should be_nil
57
- end
23
+ it 'correctly reports the list of translated_attribute_names' do
24
+ Genre.translated_attribute_names.sort.should eq [:description, :name]
25
+ end
58
26
 
59
- it 'correctly reports translated field for new record for other locale' do
60
- genre.name(:ko).should be_nil
61
- genre.description(:de).should be_nil
62
- end
27
+ it 'correcty shows the translated attribute as translated' do
28
+ Genre.translated?(:name).should be_true
29
+ Genre.translated?(:description).should be_true
30
+ end
63
31
 
64
- it 'correctly reports translation_coverage for new record' do
65
- genre.translation_coverage.should eq Hash.new
66
- end
32
+ it 'correcty shows not translated attribute' do
33
+ Genre.translated?(:other).should be_false
34
+ end
35
+ end
36
+
37
+ describe "new DB records" do
38
+
39
+ it 'correctly reports translated_locales for new record' do
40
+ genre.translated_locales.should eq [I18n.default_locale]
41
+ end
42
+
43
+ it 'correctly reports translation_missing for new record' do
44
+ genre.translation_missing.should be_true
45
+ end
46
+
47
+ it 'creates the accessor methods' do
48
+ genre.methods.should include(:name)
49
+ genre.methods.should include(:name=)
50
+ genre.methods.should include(:description)
51
+ genre.methods.should include(:description=)
52
+ end
53
+
54
+ it 'correctly reports translated field for new record for default locale' do
55
+ genre.name.should be_nil
56
+ genre.description.should be_nil
57
+ genre.description( I18n.default_locale ).should be_nil
58
+ end
59
+
60
+ it 'correctly reports translated field for new record for other locale' do
61
+ genre.name(:ko).should be_nil
62
+ genre.description(:de).should be_nil
63
+ end
64
+
65
+ it 'correctly reports translation_coverage for new record' do
66
+ genre.translation_coverage.should eq Hash.new
67
+ end
68
+
69
+ it 'correctly reports translation_goverate for attributes of new record' do
70
+ genre.translation_coverage(:name).should eq []
71
+ genre.translation_coverage(:description).should eq []
72
+ end
73
+
74
+ it 'translated_coverage returns nil for not-translated attributes' do
75
+ genre.translation_coverage(:other).should be_nil
76
+ end
77
+
78
+ it 'correctly reports translation_missing for new record' do
79
+ genre.translation_missing.should == {:description=>[:en], :name=>[:en]}
80
+ genre.translation_missing(:name).should eq [:en]
81
+ genre.translation_missing(:description).should eq [:en]
82
+ end
83
+ end
84
+
85
+ describe "DB record with pre-set fields" do
86
+ let(:genre_name_en){"Science Fiction"}
87
+ let(:scifi){ Genre.new(:name => genre_name_en) }
88
+
89
+ it 'correctly shows the attribute for new record' do
90
+ scifi.name.should eq genre_name_en
91
+ end
92
+ end
93
+
94
+ describe "updated DB record" do
95
+ let(:genre_name_en){"Science Fiction"}
96
+ let(:genre_name_jp){"サイエンスフィクション"}
97
+ let(:genre_name_ko){"공상 과학 소설"}
98
+ let(:scifi){ Genre.new(:name => genre_name_en) }
99
+
100
+ describe 'updating just default locale' do
101
+
102
+ before :each do
103
+ genre.name = genre_name_en
104
+ genre.description = "an awesome genre"
105
+ genre.save
106
+ genre.reload
107
+ end
108
+
109
+ # when setting all fields in the default locale's languange:
110
+ it 'correctly reports translation_missing for updated record' do
111
+ genre.translation_missing.should eq Hash.new
112
+ end
113
+
114
+ it 'correctly reports translation_missing for attributes' do
115
+ genre.translation_missing(:name).should eq nil
116
+ genre.translation_missing(:description).should eq nil
117
+ end
118
+
119
+ it 'correctly reports translated_coverage for updated record' do
120
+ genre.translation_coverage.should == {:name=>[:en], :description=>[:en]}
121
+ end
122
+
123
+ it 'correctly reports translated_coverage for attributes' do
124
+ genre.translation_coverage(:name).should eq [:en]
125
+ genre.translation_coverage(:description).should eq [:en]
126
+ end
127
+ end
128
+
129
+
130
+ describe 'updating other locales' do
131
+ before :each do
132
+ genre.name = genre_name_en
133
+ genre.description = "an awesome genre"
134
+ I18n.locale = :jp
135
+ genre.name = genre_name_jp
136
+ genre.set_localized_attribute(:name, :ko, genre_name_ko)
137
+ I18n.locale = :de
138
+ genre.description = "ein grossartiges Genre"
139
+ I18n.locale = I18n.default_locale # MAKE SURE you switch back to your default locale if you tweak it
140
+ genre.save
141
+ genre.reload
142
+ end
143
+
144
+ it 'can assign the translated field' do
145
+ genre.name = genre_name_en
146
+ genre.save.should be_true
147
+ genre.name.should eq genre_name_en
148
+ genre.name(:en).should eq genre_name_en
149
+ end
150
+
151
+ # when setting all fields in additional languanges:
152
+ it 'correctly reports values for updated record via attr(:locale)' do
153
+ genre.name(:ko).should eq genre_name_ko
154
+ end
155
+
156
+ it 'correctly reports values for updated record via getter' do
157
+ genre.get_localized_attribute(:name, :jp).should eq genre_name_jp
158
+ end
159
+
160
+ it 'correctly reports translation_coverage for updated record' do
161
+ # what values are actually present
162
+ genre.translation_coverage.should == {:name=>[:en, :jp, :ko], :description=>[:en, :de]}
163
+ end
164
+
165
+ it 'correctly reports translation_coverage for attributes' do
166
+ genre.translation_coverage(:name).should eq [:en, :jp, :ko]
167
+ genre.translation_coverage(:description).should eq [:en,:de]
168
+ end
169
+
170
+ it 'correctly reports translation_missing for updated record' do
171
+ # what values are missing
172
+ genre.translation_missing.should == {:description=>[:jp, :ko], :name=>[:de]}
173
+ end
174
+
175
+ it 'correctly reports translation_missing for attributes' do
176
+ genre.translation_missing(:name).should eq [:de]
177
+ genre.translation_missing(:description).should eq [:jp, :ko]
178
+ end
179
+ end
67
180
 
68
- it 'correctly reports translation_goverate for attributes of new record' do
69
- genre.translation_coverage(:name).should eq []
70
- genre.translation_coverage(:description).should eq []
71
181
  end
72
-
73
- it 'translated_coverage returns nil for not-translated attributes' do
74
- genre.translation_coverage(:other).should be_nil
75
- end
76
-
77
- it 'correctly reports translation_missing for new record' do
78
- genre.translation_missing.should == {:description=>[:en], :name=>[:en]}
79
- genre.translation_missing(:name).should eq [:en]
80
- genre.translation_missing(:description).should eq [:en]
81
- end
82
-
83
- # when setting all fields in the default locale's languange:
84
- it 'correctly reports translation_missing for updated record' do
85
- genre.name = genre_name_en
86
- genre.description = "an awesome genre"
87
- genre.save
88
- genre.translation_missing.should eq Hash.new
89
- genre.translation_missing(:name).should eq nil
90
- genre.translation_missing(:description).should eq nil
91
- end
92
-
93
- it 'correctly reports translated_coverage for updated record' do
94
- genre.name = genre_name_en
95
- genre.description = "an awesome genre"
96
- genre.save
97
- genre.translation_coverage.should == {:name=>[:en], :description=>[:en]}
98
- genre.translation_coverage(:name).should eq [:en]
99
- genre.translation_coverage(:description).should eq [:en]
100
- end
101
-
102
- # when setting all fields in additional languanges:
103
- it 'correctly reports values, translation_coverage, translation_missing for updated record' do
104
- genre.name = genre_name_en
105
- genre.description = "an awesome genre"
106
- I18n.locale = :jp
107
- genre.name = genre_name_jp
108
- genre.set_localized_attribute(:name, :ko, genre_name_ko)
109
- I18n.locale = :de
110
- genre.description = "ein grossartiger Film"
111
- I18n.locale = I18n.default_locale # MAKE SURE you switch back to your default
112
- genre.save
113
- genre.reload
114
- genre.name(:en).should eq genre_name_en
115
- genre.get_localized_attribute(:name, :jp).should eq genre_name_jp
116
- genre.name(:ko).should eq genre_name_ko
117
-
118
- # what values are actually present
119
- genre.translation_coverage.should == {:name=>[:en, :jp, :ko], :description=>[:en, :de]}
120
- genre.translation_coverage(:name).should eq [:en, :jp, :ko]
121
- genre.translation_coverage(:description).should eq [:en,:de]
122
-
123
- # what values are missing
124
- genre.translation_missing.should == {:description=>[:jp, :ko], :name=>[:de]}
125
- genre.translation_missing(:name).should eq [:de]
126
- genre.translation_missing(:description).should eq [:jp, :ko]
127
- end
128
-
129
- it 'can assign the translated field' do
130
- genre.name = genre_name_en
131
- genre.save.should be_true
132
- genre.name.should eq genre_name_en
133
- end
134
-
135
- it 'correctly shows the attribute for new record' do
136
- scifi.name.should eq genre_name_en
137
- end
138
-
139
182
  end
data/spec/models.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  class Genre < ActiveRecord::Base
2
2
  translates :name, :description
3
- attr_accessible :name, :description
4
3
  end
5
4
 
6
5
  class Movie < ActiveRecord::Base
7
6
  translates :title, :description
8
- attr_accessible :title, :description
9
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embedded_localization
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: 3.2.0
37
+ version: 4.0.0
38
38
  type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: 3.2.0
45
+ version: 4.0.0
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: i18n
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -85,8 +85,9 @@ extensions: []
85
85
  extra_rdoc_files: []
86
86
  files:
87
87
  - .gitignore
88
+ - .travis.yml
88
89
  - Gemfile
89
- - README.textile
90
+ - README.md
90
91
  - Rakefile
91
92
  - embedded_localization.gemspec
92
93
  - lib/embedded_localization.rb
data/README.textile DELETED
@@ -1,272 +0,0 @@
1
- h1. Embedded Localization
2
-
3
- Embedded_Localization is a Rails 3 localization gem, targeted at ActiveRecord 3. It is compatible with and builds on the new "I18n API in Ruby on Rails":http://guides.rubyonrails.org/i18n.html and adds model translations to ActiveRecord.
4
-
5
- Embedded_Localization is very lightweight, and allows you to transparently store translations of attributes right inside each record -- no extra database tables needed to store the localization data! Make sure that your database default encoding is UTF-8 or UFT-16.
6
-
7
- Model translations with Embedded_Localization use default ActiveRecord features and do not limit any ActiveRecord functionality.
8
-
9
-
10
- h2. Requirements
11
-
12
- ActiveRecord > 3.0.0.rc
13
- I18n
14
-
15
- Tested with Rails 3.2.2 , 3.2.16
16
-
17
- h2. Installation
18
-
19
- To install Embedded_Localization, use:
20
-
21
- <pre><code>
22
- $ gem install embedded_localization
23
-
24
- </code></pre>
25
-
26
- h2. Model translations
27
-
28
- Model translations allow you to translate your models' attribute values. The attribute type needs to be string or text, and you need to generate the database fields as usual with ActiveRecord database migrations. e.g.:
29
-
30
- <font color='red'>Note for version <0.2.0:</font> do not define the attributes in the DB migration, but define one text field :i18n for each table which uses Embedded_Localization
31
-
32
- <pre><code>
33
- class Genre < ActiveRecord::Base
34
- translates :name, :description
35
- end
36
-
37
- </code></pre>
38
-
39
- This allows you to translate the attributes :name and :description per locale:
40
-
41
- <pre><code>
42
- I18n.locale = :en
43
- g = Genre.first
44
- g.name # => 'science fiction'
45
-
46
- I18n.locale = :jp
47
- g.name # => "サイエンスフィクション"
48
-
49
- I18n.locale = :ko
50
- g.name # => "공상 과학 소설"
51
- </code></pre>
52
-
53
- No extra tables needed for this!
54
-
55
- h3. Rails 3.x
56
-
57
- <pre><code>
58
- class CreateGenres < ActiveRecord::Migration
59
- def self.up
60
- create_table :genres do |t|
61
- t.text :i18n # stores the translated attributes; persisted as a Hash
62
-
63
- # optional:
64
- # t.string :name # you CAN define :name as a real column in your DB (but you don't have to)
65
- # # If you define it, it will store the I18n.default_locale translation for SQL lookups
66
- # # You can do this for any of the translated attributes.
67
- t.timestamps
68
- end
69
- # -- example for a data migration: ---
70
- # Genre.record_timestamps = false
71
- # Genre.all.each do |g|
72
- # g.name = g.name # the right-hand-side fetches the translation from the i18n attribute hash
73
- # g.save # saves the :name attribute without updating the updated_at timestamp
74
- # end
75
- # Genre.record_timestamps = true
76
- # ------------------------------------
77
- end
78
- def self.down
79
- drop_table :posts
80
- end
81
- end
82
- </code></pre>
83
-
84
- h4. NOTE:
85
-
86
- EmbeddedLocalization implementations < 0.2.0 had the drawback that you can not do SQL queries on translated attributes.
87
-
88
- To eliminate this limitation, you can now define any translated attribute as a first-class database column in your migration. If you define a translated attribute as a column, EmbeddedLocalization will store the attribute value for I18n.default_locale in that column, so you can search for it. After defining the column, and running the migration, you need to populate the column initially. It will auto-update every time you write while you are using I18n.default_locale .
89
- e.g.:
90
-
91
- <pre><code>
92
- g = Genre.where(:name => "science fiction") # this only works if you define :name as a DB column, and populate it
93
- </pre></code>
94
-
95
- Note that the ActiveRecord model @Genre@ must already exist and have a @translates@ directive listing the translated fields.
96
-
97
-
98
- h2. I18n fallbacks for empty translations
99
-
100
- It is possible to enable fallbacks for empty translations. It will depend on the configuration setting you have set for I18n translations in your Rails config, or you can enable fallback when you define the translation fields. Currently we only support fallback to @I18n.default_locale@
101
-
102
- You can enable them by adding the next line to @config/application.rb@ (or only @config/environments/production.rb@ if you only want them in production)
103
-
104
- <pre><code>config.i18n.fallbacks = true # falls back to I18n.default_locale
105
- </code></pre>
106
-
107
- By default, Embedded_Localization will only use fallbacks when the translation value for the item you've requested is @nil@.
108
-
109
- <pre><code>
110
- class Genre < ActiveRecord::Base
111
- translates :name, :description # , :fallbacks => true
112
- end
113
-
114
- I18n.locale = :en
115
- g = Genre.first
116
- g.name # => 'science fiction'
117
-
118
- I18n.locale = :jp
119
- g.name # => "サイエンスフィクション"
120
-
121
- I18n.locale = :de
122
- g.name # => nil
123
-
124
- I18n.fallbacks = true
125
- I18n.locale = :de
126
- g.name # => 'science fiction'
127
-
128
- </code></pre>
129
-
130
-
131
- h2. Want some Candy?
132
-
133
- It's nice to have the values of attributes be set or read with the current locale, but Embedded_Localization offers you a couple of additional features..
134
-
135
- h3. Class Methods
136
-
137
- Each class which uses Embedded_Localization will have these additional methods defined:
138
- <ul>
139
- <li>Klass.translated_attributes
140
- <li>Klass.translated?
141
- <li>Klass.fallbacks?
142
- </ul>
143
-
144
- e.g.:
145
-
146
- <pre><code>
147
- Genre.translated_attributes # => [:name,:description]
148
- Genre.translated? # => true
149
- Genre.fallbacks? # => false
150
-
151
- </code></pre>
152
-
153
- h3. Instance Methods
154
-
155
- Each model instance of a class which uses Embedded_Localization will have these additional features:
156
- <ul>
157
- <li>on-the-fly translations, via <code>.name(:locale)</code>
158
- <li>list of translated locales
159
- <li>list of translated attributes
160
- <li>hash of translation coverage for a given record's attributes or a particular attribute
161
- <li>hash of missing translations for a given record's attributes or a particular attribute
162
- <li>directly setting and getting attribute values for a given locale; without having to change <code>I18n.locale</code>
163
- </ul>
164
-
165
- Notes:
166
-
167
- <pre><code>translated_locales</code></pre> lists the super-set of all locales, including the default locale, even if there is no value set for a specific attribute.
168
- For a new empty record, this will report I18n.default_locale.
169
-
170
- translated_locales reports which translations / languages are possible.
171
-
172
- <pre><code>translation_coverage</code></pre> only lists locales for which a non-nil value is set.
173
- For a new empty record, this will be empty.
174
-
175
- translation_coverage reports for which languages translations exist (actual values exist).
176
-
177
- e.g.:
178
-
179
- <pre><code>
180
- I18n.locale = :jp
181
- g = Genre.first
182
- g.name # => "サイエンスフィクション"
183
-
184
- g.name(:en) # => 'science fiction'
185
- g.name(:ko) # => "공상 과학 소설"
186
- g.name(:de) # => nil
187
-
188
- g.translated_locales # => [:en,:jp,:ko]
189
- g.translated_attributes # => [:name,:description]
190
- g.translated? # => true
191
-
192
- g.translation_coverage
193
- # => {"name"=>["en", "ko", "jp"] , "description"=>["en", "de", "fr", "ko", "jp", "es"]}
194
-
195
- g.translation_coverage(:name)
196
- # => {"name"=>["en", "ko", "jp"]}
197
-
198
- g.translation_missing
199
- # => {"name"=>["de", "fr", "es"]}
200
-
201
- g.translation_missing(:display)
202
- # => {} # this indicates that there are no missing translations for the :display attribute
203
-
204
- g.get_localized_attribute(:name, :de)
205
- # => nil
206
-
207
- g.set_localized_attribute(:name, :de, "Science-Fiction")
208
- # => "Science-Fiction"
209
-
210
- </code></pre>
211
-
212
- h2. Motivation
213
-
214
- A recent project needed some localization support for ActiveRecord model data, but I did not want to clutter the schema with one additional table for each translated model, as globalization3 requires. A second requirement was to allow SQL queries of the fields using the default locale.
215
-
216
- The advantage of EmbeddedLocalization is that it does not need extra tables, and therefore no joins or additional table lookups to get to the translated data.
217
-
218
- If your requirements are different, my approach might not work for you. In that case, I recommend to look at the alternative solutions listed below.
219
-
220
- h2. Changes
221
-
222
-
223
- h3. 1.0.0 (2014-01-11)
224
- * adding rspec tests.
225
- * fixing issue #6: translated fields for new records were not nil
226
- * fixing issue #7: translation_missing for new records is breaking
227
-
228
-
229
- h3. 0.2.5 (2013-11-02)
230
- * adding MIT and GPL-2 licenses to gem-spec file; contact me if you need another license
231
-
232
- h3. 0.2.4 (2012-03-02)
233
- * Issue #5 : bugfix for attr_writer
234
-
235
- h3. 0.2.3 (2012-03-02)
236
- * Issue #4 : bugfix for attr_writer - no longer updates attributes if value didn't change => timestamps don't change in that case
237
-
238
- h3. 0.2.2 (2012-02-06)
239
- * bugfix for attr_writer
240
-
241
- h3. 0.2.1 (2012-01-31)
242
- * bugfix for serialized i18n attribute
243
-
244
- h3. 0.2.0 (2012-01-31)
245
- * added support for having DB columns for translated attributes, to enable SQL queries in I18n.default_locale
246
-
247
- h3. 0.1.4 (2012-01-31)
248
- * fixed bug with dirty tracking of serialized i18n attribute
249
- * renamed #fallback? to #fallbacks?
250
-
251
- h3. 0.1.3 Initial Version (2012-01-27)
252
-
253
-
254
- h2. Alternative Solutions
255
-
256
- * "Mongoid":https://github.com/mongoid/mongoid - awesome Ruby ORM for MongoDB, which includes in-table localization of attributes (mongoid >= 2.3.0)
257
- * "Globalize3":https://github.com/svenfuchs/globalize3 - is an awesome gem, but different approach with more tables in the schema.
258
- * "Veger's fork":http://github.com/veger/globalize2 - uses default AR schema for the default locale, delegates to the translations table for other locales only
259
- * "TranslatableColumns":http://github.com/iain/translatable_columns - have multiple languages of the same attribute in a model (Iain Hecker)
260
- * "localized_record":http://github.com/glennpow/localized_record - allows records to have localized attributes without any modifications to the database (Glenn Powell)
261
- * "model_translations":http://github.com/janne/model_translations - Minimal implementation of Globalize2 style model translations (Jan Andersson)
262
-
263
- h2. Related solutions
264
-
265
- * "globalize2_versioning":http://github.com/joshmh/globalize2_versioning - acts_as_versioned style versioning for globalize2 (Joshua Harvey)
266
- * "i18n_multi_locales_validations":http://github.com/ZenCocoon/i18n_multi_locales_validations - multi-locales attributes validations to validates attributes from globalize2 translations models (Sébastien Grosjean)
267
- * "globalize2 Demo App":http://github.com/svenfuchs/globalize2-demo - demo application for globalize2 (Sven Fuchs)</li>
268
- * "migrate_from_globalize1":http://gist.github.com/120867 - migrate model translations from Globalize1 to globalize2 (Tomasz Stachewicz)</li>
269
- * "easy_globalize2_accessors":http://github.com/astropanic/easy_globalize2_accessors - easily access (read and write) globalize2-translated fields (astropanic, Tomasz Stachewicz)</li>
270
- * "globalize2-easy-translate":http://github.com/bsamman/globalize2-easy-translate - adds methods to easily access or set translated attributes to your model (bsamman)</li>
271
- * "batch_translations":http://github.com/alvarezrilla/batch_translations - allow saving multiple globalize2 translations in the same request (Jose Alvarez Rilla)</li>
272
-