releaf-i18n_database 0.2.1 → 1.0.3

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.
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