releaf-i18n_database 0.2.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -21
  3. data/app/assets/javascripts/{releaf/controllers → controllers}/releaf/i18n_database/translations.js +0 -0
  4. data/app/assets/stylesheets/{releaf/controllers → controllers}/releaf/i18n_database/translations.scss +0 -0
  5. data/app/builders/releaf/i18n_database/translations/builders_common.rb +1 -1
  6. data/app/builders/releaf/i18n_database/translations/index_builder.rb +1 -1
  7. data/app/controllers/releaf/i18n_database/translations_controller.rb +121 -127
  8. data/app/lib/releaf/i18n_database/parse_spreadsheet_translations.rb +62 -0
  9. data/app/lib/releaf/i18n_database/translations_store.rb +149 -0
  10. data/app/lib/releaf/i18n_database/translations_utilities.rb +6 -6
  11. data/app/models/releaf/i18n_database/i18n_entry.rb +21 -0
  12. data/app/models/releaf/i18n_database/i18n_entry_translation.rb +11 -0
  13. data/app/views/releaf/i18n_database/translations/_form_fields.haml +2 -2
  14. data/lib/releaf-i18n_database.rb +14 -3
  15. data/lib/releaf/i18n_database/backend.rb +56 -131
  16. data/lib/releaf/i18n_database/configuration.rb +8 -0
  17. data/lib/releaf/i18n_database/engine.rb +1 -30
  18. data/lib/releaf/i18n_database/humanize_missing_translations.rb +1 -1
  19. data/misc/translations.xlsx +0 -0
  20. data/spec/builders/translations/builder_common_spec.rb +1 -1
  21. data/spec/builders/translations/edit_builder_spec.rb +2 -2
  22. data/spec/builders/translations/table_builder_spec.rb +1 -1
  23. data/spec/controllers/i18n_backend/translations_controller_spec.rb +10 -19
  24. data/spec/features/translations_spec.rb +235 -16
  25. data/spec/fixtures/invalid.xls +3 -0
  26. data/spec/fixtures/invalid.xlsx +3 -0
  27. data/spec/lib/releaf/i18n_database/backend_spec.rb +192 -0
  28. data/spec/lib/releaf/i18n_database/configuration_spec.rb +13 -0
  29. data/spec/lib/{i18n_database → releaf/i18n_database}/humanize_missing_translations_spec.rb +7 -1
  30. data/spec/lib/releaf/i18n_database/parse_spreadsheet_translations_spec.rb +151 -0
  31. data/spec/lib/releaf/i18n_database/translations_store_spec.rb +548 -0
  32. data/spec/lib/{i18n_database → releaf/i18n_database}/translations_utilities_spec.rb +39 -39
  33. data/spec/models/i18n_database/i18n_entry_spec.rb +50 -0
  34. data/spec/models/i18n_database/i18n_entry_translation_spec.rb +9 -0
  35. metadata +45 -30
  36. data/app/lib/releaf/i18n_database/translations_importer.rb +0 -72
  37. data/app/models/releaf/i18n_database/translation.rb +0 -17
  38. data/app/models/releaf/i18n_database/translation_data.rb +0 -11
  39. data/lib/releaf/i18n_database/builders_autoload.rb +0 -10
  40. data/releaf-i18n_database.gemspec +0 -21
  41. data/spec/lib/i18n_database/backend_spec.rb +0 -337
  42. data/spec/lib/i18n_database/translations_importer_spec.rb +0 -17
  43. data/spec/models/i18n_database/translation_data_spec.rb +0 -13
  44. data/spec/models/i18n_database/translation_spec.rb +0 -49
@@ -1,17 +0,0 @@
1
- module Releaf::I18nDatabase
2
- class Translation < ActiveRecord::Base
3
- self.table_name = "releaf_translations"
4
-
5
- validates_presence_of :key
6
- validates_uniqueness_of :key
7
- validates_length_of :key, maximum: 255
8
-
9
- has_many :translation_data, dependent: :destroy, class_name: 'Releaf::I18nDatabase::TranslationData', inverse_of: :translation
10
- accepts_nested_attributes_for :translation_data, allow_destroy: true
11
-
12
- def locale_value(locale)
13
- # search against all values to cache
14
- translation_data.find{ |x| x.lang == locale.to_s }.try(:localization)
15
- end
16
- end
17
- end
@@ -1,11 +0,0 @@
1
- module Releaf::I18nDatabase
2
- class TranslationData < ActiveRecord::Base
3
- self.table_name = "releaf_translation_data"
4
-
5
- validates_presence_of :translation, :lang
6
- validates_uniqueness_of :translation_id, :scope => :lang
7
- validates_length_of :lang, maximum: 5
8
-
9
- belongs_to :translation, :inverse_of => :translation_data
10
- end
11
- end
@@ -1,10 +0,0 @@
1
- root_path = File.expand_path('../..', File.dirname(__dir__))
2
- files = %w(
3
- builders_common
4
- edit_builder
5
- table_builder
6
- index_builder
7
- )
8
- files.each do|file|
9
- require "#{root_path}/app/builders/releaf/i18n_database/translations/#{file}"
10
- end
@@ -1,21 +0,0 @@
1
- require File.expand_path("../../releaf-core/lib/releaf/version.rb", __FILE__)
2
-
3
- Gem::Specification.new do |s|
4
- s.name = "releaf-i18n_database"
5
- s.version = Releaf::VERSION
6
-
7
- s.summary = "i18n database gem for releaf"
8
- s.description = "i18n database backend for releaf"
9
- s.authors = ["CubeSystems"]
10
- s.email = 'info@cubesystems.lv'
11
- s.homepage = 'https://github.com/cubesystems/releaf'
12
-
13
- s.files = `git ls-files`.split("\n")
14
- s.test_files = Dir["spec/**/*"]
15
-
16
- s.add_dependency 'releaf-core', Releaf::VERSION
17
- s.add_dependency 'twitter_cldr'
18
- s.add_dependency 'axlsx_rails', '>= 0.3.0'
19
- s.add_dependency 'roo'
20
-
21
- end
@@ -1,337 +0,0 @@
1
- require "rails_helper"
2
-
3
- describe Releaf::I18nDatabase::Backend do
4
-
5
- before do
6
- allow( Releaf::I18nDatabase ).to receive(:create_missing_translations).and_return(true)
7
- allow( I18n.backend ).to receive(:reload_cache?).and_return(true)
8
- I18n.backend.reload_cache
9
- end
10
-
11
- describe "#store_translations" do
12
- it "merges given translations to cache" do
13
- translation = FactoryGirl.create(:translation, key: "admin.content.save")
14
- FactoryGirl.create(:translation_data, translation: translation, localization: "save", lang: "en")
15
- FactoryGirl.create(:translation_data, translation: translation, localization: "saglabāt", lang: "lv")
16
- I18n.backend.reload_cache
17
- allow( I18n.backend ).to receive(:reload_cache?).and_return(false)
18
-
19
- expect{ I18n.backend.store_translations(:en, {admin: {profile: "profils"}}) }.to change{ I18n.t("admin.profile") }.
20
- from("Profile").to("profils")
21
-
22
- expect(I18n.t("admin.content.save", locale: "lv")).to eq("saglabāt")
23
- end
24
- end
25
-
26
- describe "#default" do
27
- context "when `create_default: false` option exists" do
28
- it "adds `create_default: true` option and remove `create_default` option" do
29
- expect(subject).to receive(:resolve).with("en", "aa", "bb", count: 1, fallback: true, create_missing: false)
30
- subject.send(:default, "en", "aa", "bb", count:1, default: "xxx", fallback: true, create_default: false, create_missing: false)
31
- end
32
-
33
- it "does not change given options" do
34
- options = {count:1, default: "xxx", fallback: true, create_default: false}
35
- expect{ subject.send(:default, "en", "aa", "bb", options) }.to_not change{ options }
36
- end
37
- end
38
-
39
- context "when `create_default: false` option does not exists" do
40
- it "does not modify options" do
41
- expect(subject).to receive(:resolve).with("en", "aa", "bb", count: 1, fallback: true)
42
- subject.send(:default, "en", "aa", "bb", count:1, default: "xxx", fallback: true)
43
-
44
- expect(subject).to receive(:resolve).with("en", "aa", "bb", count: 1, fallback: true, create_default: true)
45
- subject.send(:default, "en", "aa", "bb", count:1, default: "xxx", fallback: true, create_default: true)
46
- end
47
- end
48
- end
49
-
50
- describe "#create_missing_translation?" do
51
- before do
52
- Releaf::I18nDatabase.create_missing_translations = true
53
- end
54
-
55
- context "when missing translation creation is enabled globally by i18n config and not disabled by `create_missing` option" do
56
- it "returns true" do
57
- expect(subject.send(:create_missing_translation?, {})).to be true
58
- expect(subject.send(:create_missing_translation?, create_missing: true)).to be true
59
- expect(subject.send(:create_missing_translation?, create_missing: nil)).to be true
60
- end
61
- end
62
-
63
- context "when missing translation creation is disabled globally by i18n config" do
64
- it "returns false" do
65
- allow( Releaf::I18nDatabase ).to receive(:create_missing_translations).and_return(false)
66
- expect(subject.send(:create_missing_translation?, {})).to be false
67
- end
68
- end
69
-
70
- context "when missing translation creation is disabled by `create_missing` option" do
71
- it "returns false" do
72
- expect(subject.send(:create_missing_translation?, create_missing: false)).to be false
73
- end
74
- end
75
- end
76
-
77
- describe ".translations_updated_at" do
78
- it "returns translations updated_at from cached settings" do
79
- allow(Releaf::Settings).to receive(:[]).with(described_class::UPDATED_AT_KEY).and_return("x")
80
- expect(described_class.translations_updated_at).to eq("x")
81
- end
82
- end
83
-
84
- describe ".translations_updated_at=" do
85
- it "stores translations updated_at to cached settings" do
86
- expect(Releaf::Settings).to receive(:[]=).with(described_class::UPDATED_AT_KEY, "xx")
87
- described_class.translations_updated_at = "xx"
88
- end
89
- end
90
-
91
- describe "#reload_cache?" do
92
- context "when last translation update differs from last cache load" do
93
- it "returns true" do
94
- allow(described_class).to receive(:translations_updated_at).and_return(1)
95
- described_class::CACHE[:updated_at] = 2
96
- expect(subject.reload_cache?).to be true
97
- end
98
- end
99
-
100
- context "when last translation update differs from last cache load" do
101
- it "returns false" do
102
- allow(described_class).to receive(:translations_updated_at).and_return(1)
103
- described_class::CACHE[:updated_at] = 1
104
- expect(subject.reload_cache?).to be false
105
- end
106
- end
107
- end
108
-
109
- describe "#reload_cache" do
110
- it "resets missing array" do
111
- I18n.t("something")
112
- expect{ I18n.backend.reload_cache }.to change{ described_class::CACHE[:missing].blank? }.from(false).to(true)
113
- end
114
-
115
- it "writes last translations update timestamp to cache" do
116
- allow(described_class).to receive(:translations_updated_at).and_return("x")
117
- expect{ I18n.backend.reload_cache }.to change{ described_class::CACHE[:updated_at] }.to("x")
118
- end
119
-
120
- it "loads all translated data to cache as hash" do
121
- translation = FactoryGirl.create(:translation, key: "admin.xx.save")
122
- FactoryGirl.create(:translation_data, translation: translation, localization: "saglabāt", lang: "lv")
123
- FactoryGirl.create(:translation_data, translation: translation, localization: "save", lang: "en")
124
-
125
- expect{ I18n.backend.reload_cache }.to change{ described_class::CACHE[:translations].blank? }.from(true).to(false)
126
-
127
- expect(described_class::CACHE[:translations][:lv][:admin][:xx][:save]).to eq("saglabāt")
128
- expect(described_class::CACHE[:translations][:en][:admin][:xx][:save]).to eq("save")
129
- end
130
- end
131
-
132
- describe "#lookup" do
133
- describe "cache reload" do
134
- let(:timestamp){ Time.now }
135
-
136
- context "when cache timestamp differs from translations update timestamp" do
137
- it "reloads cache" do
138
- described_class::CACHE[:updated_at] = timestamp
139
- allow(described_class).to receive(:translations_updated_at).and_return(timestamp + 1.day)
140
- expect(I18n.backend).to receive(:reload_cache)
141
- I18n.t("cancel")
142
- end
143
- end
144
-
145
- context "when cache timestamp is same as translations update timestamp" do
146
- it "does not reload cache" do
147
- allow( I18n.backend ).to receive(:reload_cache?).and_call_original
148
- described_class::CACHE[:updated_at] = timestamp
149
- allow(described_class).to receive(:translations_updated_at).and_return(timestamp)
150
-
151
- expect(I18n.backend).to_not receive(:reload_cache)
152
- I18n.t("cancel")
153
- end
154
- end
155
- end
156
-
157
- context "when translation exists within higher level key (instead of being scope)" do
158
- it "returns nil (Humanize key)" do
159
- translation = FactoryGirl.create(:translation, key: "some.food")
160
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "suņi")
161
- expect(I18n.t("some.food", locale: "lv")).to eq("suņi")
162
- expect(I18n.t("some.food.asd", locale: "lv")).to eq("Asd")
163
- end
164
- end
165
-
166
- context "when pluralized translation requested" do
167
- context "when valid pluralized data matched" do
168
- it "returns pluralized translation" do
169
- translation = FactoryGirl.create(:translation, key: "dog.other")
170
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "suņi")
171
- expect(I18n.t("dog", locale: "lv", count: 2)).to eq("suņi")
172
- end
173
- end
174
-
175
- context "when invalid pluralized data matched" do
176
- it "returns nil (Humanize key)" do
177
- translation = FactoryGirl.create(:translation, key: "dog.food")
178
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "suņi")
179
- expect(I18n.t("dog", locale: "lv", count: 2)).to eq("Dog")
180
- end
181
- end
182
- end
183
-
184
- context "existing translation" do
185
- context "when translation exists with different case" do
186
- it "returns existing translation" do
187
- translation = FactoryGirl.create(:translation, key: "Save")
188
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "Saglabāt")
189
- I18n.backend.reload_cache
190
-
191
- expect(I18n.t("save", locale: "lv")).to eq("Saglabāt")
192
- expect(I18n.t("Save", locale: "lv")).to eq("Saglabāt")
193
- end
194
- end
195
-
196
- context "when translations hash exists in parent scope" do
197
- before do
198
- translation = FactoryGirl.create(:translation, key: "dog.other")
199
- FactoryGirl.create(:translation_data, translation: translation, lang: "en", localization: "dogs")
200
- end
201
-
202
- context "when pluralized translation requested" do
203
- it "returns pluralized translation" do
204
- expect(I18n.t("admin.controller.dog", count: 2)).to eq("dogs")
205
- end
206
- end
207
-
208
- context "when non pluralized translation requested" do
209
- it "returns nil" do
210
- expect(I18n.t("admin.controller.dog")).to eq("Dog")
211
- end
212
- end
213
- end
214
-
215
- context "when translation has default" do
216
- context "when default creation is disabled" do
217
- it "creates base translation" do
218
- expect{ I18n.t("xxx.test.mest", default: :"xxx.mest", create_default: false) }.to change{ Releaf::I18nDatabase::Translation.pluck(:key) }
219
- .to(["xxx.test.mest"])
220
-
221
- end
222
- end
223
-
224
- context "when default creation is not disabled" do
225
- it "creates base and default translations" do
226
- expect{ I18n.t("xxx.test.mest", default: :"xxx.mest") }.to change{ Releaf::I18nDatabase::Translation.pluck(:key) }
227
- .to(match_array(["xxx.mest", "xxx.test.mest"]))
228
- end
229
- end
230
- end
231
-
232
- context "in parent scope" do
233
- context "nonexistent translation in given scope" do
234
- it "uses parent scope" do
235
- translation = FactoryGirl.create(:translation, key: "validation.admin.blank")
236
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "Tukšs")
237
- expect(I18n.t("blank", scope: "validation.admin.roles", locale: "lv")).to eq("Tukšs")
238
- end
239
-
240
- context "when `inherit_scopes` option is `false`" do
241
- it "does not lookup upon higher level scopes" do
242
- translation = FactoryGirl.create(:translation, key: "validation.admin.blank")
243
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "Tukšs")
244
- expect(I18n.t("blank", scope: "validation.admin.roles", locale: "lv", inherit_scopes: false)).to eq("Blank")
245
- end
246
- end
247
- end
248
-
249
- context "and empty translation value in given scope" do
250
- it "uses parent scope" do
251
- parent_translation = FactoryGirl.create(:translation, key: "validation.admin.blank")
252
- FactoryGirl.create(:translation_data, translation: parent_translation, lang: "lv", localization: "Tukšs")
253
-
254
- translation = FactoryGirl.create(:translation, key: "validation.admin.roles.blank")
255
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "")
256
-
257
- expect(I18n.t("blank", scope: "validation.admin.roles", locale: "lv")).to eq("Tukšs")
258
- end
259
- end
260
-
261
- context "and existing translation value in given scope" do
262
- it "uses given scope" do
263
- parent_translation = FactoryGirl.create(:translation, key: "validation.admin.blank")
264
- FactoryGirl.create(:translation_data, translation: parent_translation, lang: "lv", localization: "Tukšs")
265
-
266
- translation = FactoryGirl.create(:translation, key: "validation.admin.roles.blank")
267
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "Tukša vērtība")
268
-
269
- expect(I18n.t("blank", scope: "validation.admin.roles", locale: "lv")).to eq("Tukša vērtība")
270
- end
271
- end
272
- end
273
-
274
- context "when scope defined" do
275
- it "uses given scope" do
276
- translation = FactoryGirl.create(:translation, key: "admin.content.cancel")
277
- FactoryGirl.create(:translation_data, translation: translation, lang: "lv", localization: "Atlikt")
278
- expect(I18n.t("cancel", scope: "admin.content", locale: "lv")).to eq("Atlikt")
279
- end
280
- end
281
- end
282
-
283
- context "nonexistent translation" do
284
- context "loading multiple times" do
285
- it "queries db only for the first time" do
286
- I18n.t("save", scope: "admin.xx")
287
- expect(Releaf::I18nDatabase::Translation).not_to receive(:where)
288
- I18n.t("save", scope: "admin.xx")
289
- end
290
- end
291
-
292
- context "with nonexistent translation" do
293
- before do
294
- allow(Releaf.application.config).to receive(:all_locales).and_return(["ru", "lv"])
295
- end
296
-
297
- it "creates empty translation" do
298
- expect { I18n.t("save") }.to change { Releaf::I18nDatabase::Translation.where(key: "save").count }.by(1)
299
- end
300
-
301
- context "when count option passed" do
302
- context "when create_plurals option not passed" do
303
- it "creates empty translation" do
304
- expect { I18n.t("animals.horse", count: 1) }.to change { Releaf::I18nDatabase::Translation.where(key: "animals.horse").count }.by(1)
305
- end
306
- end
307
-
308
- context "when negative create_plurals option passed" do
309
- it "creates empty translation" do
310
- expect { I18n.t("animals.horse", create_plurals: false, count: 1) }.to change { Releaf::I18nDatabase::Translation.where(key: "animals.horse").count }.by(1)
311
- end
312
- end
313
-
314
- context "when positive create_plurals option passed" do
315
- it "creates pluralized translations for all Releaf locales" do
316
- result = ["animals.horse.few", "animals.horse.many", "animals.horse.one", "animals.horse.other", "animals.horse.zero"]
317
- expect{ I18n.t("animals.horse", count: 1, create_plurals: true) }.to change{ Releaf::I18nDatabase::Translation.pluck(:key).sort }.
318
- from([]).to(result.sort)
319
- end
320
- end
321
- end
322
- end
323
- end
324
-
325
- context "when scope requested" do
326
- it "returns all scope translations" do
327
- translation1 = FactoryGirl.create(:translation, key: "admin.content.cancel")
328
- FactoryGirl.create(:translation_data, translation: translation1, lang: "lv", localization: "Atlikt")
329
- translation2 = FactoryGirl.create(:translation, key: "admin.content.save")
330
- FactoryGirl.create(:translation_data, translation: translation2, lang: "lv", localization: "Saglabāt")
331
-
332
- expect(I18n.t("admin.content", locale: "lv")).to eq({cancel: "Atlikt", save: "Saglabāt"})
333
- expect(I18n.t("admin.content", locale: "en")).to eq({cancel: nil, save: nil})
334
- end
335
- end
336
- end
337
- end
@@ -1,17 +0,0 @@
1
- require "rails_helper"
2
-
3
- describe Releaf::I18nDatabase::TranslationsImporter do
4
- describe "#initialize" do
5
- context "when unsupported extension given" do
6
- it "raises Releaf::TranslationsImporter::UnsupportedFileFormatError" do
7
- expect{described_class.new("xx", "xx")}.to raise_error(described_class::UnsupportedFileFormatError)
8
- end
9
- end
10
-
11
- context "when error raised from Roo" do
12
- it "raises it" do
13
- expect{described_class.new("xx", "xls")}.to raise_error(IOError)
14
- end
15
- end
16
- end
17
- end
@@ -1,13 +0,0 @@
1
- require "rails_helper"
2
-
3
- describe Releaf::I18nDatabase::TranslationData do
4
-
5
- it { is_expected.to validate_presence_of(:translation) }
6
- it { is_expected.to validate_presence_of(:lang) }
7
- it { is_expected.to validate_length_of(:lang).is_at_most(5) }
8
- it {
9
- FactoryGirl.create(:translation_data)
10
- is_expected.to validate_uniqueness_of(:translation_id).scoped_to([:lang])
11
- }
12
- it { is_expected.to belong_to(:translation) }
13
- end
@@ -1,49 +0,0 @@
1
- require "rails_helper"
2
-
3
- describe Releaf::I18nDatabase::Translation do
4
-
5
- it { is_expected.to validate_presence_of(:key) }
6
- it { is_expected.to validate_length_of(:key).is_at_most(255) }
7
- it do
8
- FactoryGirl.create(:translation)
9
- is_expected.to validate_uniqueness_of(:key)
10
- end
11
- it { is_expected.to have_many(:translation_data).dependent(:destroy) }
12
- it { is_expected.to accept_nested_attributes_for(:translation_data).allow_destroy(true) }
13
-
14
-
15
- before do
16
- allow(Releaf.application.config).to receive(:available_locales) { ["de", "en"] }
17
- allow(Releaf.application.config).to receive(:available_admin_locales) { ["lv"] }
18
-
19
- @translation = FactoryGirl.create(:translation, :key => 'test.apple')
20
- FactoryGirl.create(:translation_data, :localization => 'apple', :translation => @translation, :lang => "en")
21
- FactoryGirl.create(:translation_data, :localization => 'apfel', :translation => @translation, :lang => "de")
22
-
23
- I18n.backend.reload_cache
24
- end
25
-
26
- describe "translation" do
27
- it "has relation to translation data" do
28
- expect(@translation.translation_data.size).to eq(2)
29
- end
30
-
31
- it "destroys translation data when destroying translation itself" do
32
- expect{ @translation.destroy }.to change{ Releaf::I18nDatabase::TranslationData.all.count }.from(2).to(0)
33
- end
34
- end
35
-
36
- describe "#locale_value" do
37
- it "returns translated value for given locale" do
38
- expect(@translation.locale_value("en")).to eq("apple")
39
- expect(@translation.locale_value("de")).to eq("apfel")
40
- expect(@translation.locale_value("lt")).to eq(nil)
41
- end
42
-
43
- it "caches translated values with first call" do
44
- expect(@translation.locale_value("en")).to eq("apple")
45
- Releaf::I18nDatabase::TranslationData.destroy_all
46
- expect(@translation.locale_value("de")).to eq("apfel")
47
- end
48
- end
49
- end