i18n-js 2.1.2 → 3.0.11

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 (107) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +24 -0
  3. data/.gitignore +5 -4
  4. data/.npmignore +27 -0
  5. data/.travis.yml +37 -0
  6. data/Appraisals +20 -0
  7. data/CHANGELOG.md +354 -0
  8. data/Gemfile +1 -1
  9. data/README.md +872 -0
  10. data/Rakefile +19 -7
  11. data/app/assets/javascripts/i18n/filtered.js.erb +23 -0
  12. data/app/assets/javascripts/i18n/shims.js +208 -0
  13. data/app/assets/javascripts/i18n/translations.js +3 -0
  14. data/app/assets/javascripts/i18n.js +1077 -0
  15. data/gemfiles/i18n_0_6.gemfile +7 -0
  16. data/gemfiles/i18n_0_7.gemfile +7 -0
  17. data/gemfiles/i18n_0_8.gemfile +7 -0
  18. data/gemfiles/i18n_0_9.gemfile +7 -0
  19. data/gemfiles/i18n_1_0.gemfile +7 -0
  20. data/i18n-js.gemspec +11 -9
  21. data/lib/i18n/js/dependencies.rb +59 -0
  22. data/lib/i18n/js/engine.rb +87 -0
  23. data/lib/i18n/js/fallback_locales.rb +70 -0
  24. data/lib/{i18n-js → i18n/js}/middleware.rb +32 -9
  25. data/lib/i18n/js/private/hash_with_symbol_keys.rb +36 -0
  26. data/lib/i18n/js/segment.rb +88 -0
  27. data/lib/i18n/js/utils.rb +52 -0
  28. data/lib/i18n/js/version.rb +7 -0
  29. data/lib/i18n/js.rb +242 -0
  30. data/lib/i18n-js.rb +1 -177
  31. data/lib/rails/generators/i18n/js/config/config_generator.rb +19 -0
  32. data/{config → lib/rails/generators/i18n/js/config/templates}/i18n-js.yml +11 -6
  33. data/lib/tasks/export.rake +8 -0
  34. data/package.json +25 -0
  35. data/spec/fixtures/custom_path.yml +5 -0
  36. data/spec/fixtures/default.yml +5 -0
  37. data/spec/fixtures/erb.yml +5 -0
  38. data/spec/fixtures/except_condition.yml +7 -0
  39. data/spec/fixtures/js_export_dir_custom.yml +7 -0
  40. data/spec/fixtures/js_export_dir_none.yml +6 -0
  41. data/spec/fixtures/js_extend_parent.yml +6 -0
  42. data/spec/fixtures/js_extend_segment.yml +6 -0
  43. data/spec/fixtures/js_file_per_locale.yml +7 -0
  44. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml +4 -0
  45. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_hash.yml +6 -0
  46. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale.yml +4 -0
  47. data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml +4 -0
  48. data/spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml +4 -0
  49. data/spec/fixtures/js_file_per_locale_without_fallbacks.yml +4 -0
  50. data/spec/fixtures/js_file_with_namespace_and_pretty_print.yml +7 -0
  51. data/spec/fixtures/js_sort_translation_keys_false.yml +6 -0
  52. data/spec/fixtures/js_sort_translation_keys_true.yml +6 -0
  53. data/spec/{resources → fixtures}/locales.yml +13 -1
  54. data/spec/fixtures/multiple_conditions.yml +7 -0
  55. data/spec/fixtures/multiple_conditions_per_locale.yml +7 -0
  56. data/spec/fixtures/multiple_files.yml +7 -0
  57. data/spec/{resources → fixtures}/no_config.yml +0 -0
  58. data/spec/fixtures/no_scope.yml +4 -0
  59. data/spec/fixtures/simple_scope.yml +5 -0
  60. data/spec/js/currency.spec.js +62 -0
  61. data/spec/js/current_locale.spec.js +19 -0
  62. data/spec/js/dates.spec.js +265 -0
  63. data/spec/js/defaults.spec.js +31 -0
  64. data/spec/js/extend.spec.js +110 -0
  65. data/spec/js/interpolation.spec.js +124 -0
  66. data/spec/js/jasmine/MIT.LICENSE +20 -0
  67. data/spec/js/jasmine/jasmine-html.js +190 -0
  68. data/spec/js/jasmine/jasmine.css +166 -0
  69. data/spec/js/jasmine/jasmine.js +2476 -0
  70. data/spec/js/jasmine/jasmine_favicon.png +0 -0
  71. data/spec/js/locales.spec.js +31 -0
  72. data/spec/js/localization.spec.js +48 -0
  73. data/spec/js/numbers.spec.js +170 -0
  74. data/spec/js/placeholder.spec.js +24 -0
  75. data/spec/js/pluralization.spec.js +211 -0
  76. data/spec/js/prepare_options.spec.js +41 -0
  77. data/spec/js/require.js +2083 -0
  78. data/spec/js/specs.html +49 -0
  79. data/spec/js/specs_requirejs.html +72 -0
  80. data/spec/js/translate.spec.js +277 -0
  81. data/spec/js/translations.js +164 -0
  82. data/spec/js/utility_functions.spec.js +20 -0
  83. data/spec/ruby/i18n/js/fallback_locales_spec.rb +84 -0
  84. data/spec/ruby/i18n/js/segment_spec.rb +157 -0
  85. data/spec/ruby/i18n/js/utils_spec.rb +106 -0
  86. data/spec/ruby/i18n/js_spec.rb +627 -0
  87. data/spec/spec_helper.rb +55 -14
  88. data/yarn.lock +131 -0
  89. metadata +188 -96
  90. data/.rspec +0 -1
  91. data/Gemfile.lock +0 -51
  92. data/README.rdoc +0 -305
  93. data/lib/i18n-js/engine.rb +0 -62
  94. data/lib/i18n-js/railtie.rb +0 -13
  95. data/lib/i18n-js/rake.rb +0 -16
  96. data/lib/i18n-js/version.rb +0 -10
  97. data/spec/i18n_spec.js +0 -768
  98. data/spec/i18n_spec.rb +0 -205
  99. data/spec/resources/custom_path.yml +0 -4
  100. data/spec/resources/default.yml +0 -4
  101. data/spec/resources/js_file_per_locale.yml +0 -3
  102. data/spec/resources/multiple_conditions.yml +0 -6
  103. data/spec/resources/multiple_files.yml +0 -6
  104. data/spec/resources/no_scope.yml +0 -3
  105. data/spec/resources/simple_scope.yml +0 -4
  106. data/vendor/assets/javascripts/i18n/translations.js.erb +0 -7
  107. data/vendor/assets/javascripts/i18n.js +0 -450
@@ -0,0 +1,627 @@
1
+ require "spec_helper"
2
+
3
+ describe I18n::JS do
4
+
5
+ describe '.config_file_path' do
6
+ let(:default_path) { I18n::JS::DEFAULT_CONFIG_PATH }
7
+ let(:new_path) { File.join("tmp", default_path) }
8
+
9
+ subject { described_class.config_file_path }
10
+
11
+ context "when it is not set" do
12
+ it { should eq default_path }
13
+ end
14
+ context "when it is set already" do
15
+ before { described_class.config_file_path = new_path }
16
+
17
+ it { should eq new_path }
18
+ end
19
+ end
20
+
21
+ context "exporting" do
22
+ before do
23
+ stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
24
+ end
25
+
26
+ it "exports messages to default path when configuration file doesn't exist" do
27
+ I18n::JS.export
28
+ file_should_exist "translations.js"
29
+ end
30
+
31
+ it "exports messages using custom output path" do
32
+ set_config "custom_path.yml"
33
+ I18n::JS::Segment.should_receive(:new).with("tmp/i18n-js/all.js", translations, {js_extend: true, sort_translation_keys: true}).and_call_original
34
+ I18n::JS::Segment.any_instance.should_receive(:save!).with(no_args)
35
+ I18n::JS.export
36
+ end
37
+
38
+ it "sets default scope to * when not specified" do
39
+ set_config "no_scope.yml"
40
+ I18n::JS::Segment.should_receive(:new).with("tmp/i18n-js/no_scope.js", translations, {js_extend: true, sort_translation_keys: true}).and_call_original
41
+ I18n::JS::Segment.any_instance.should_receive(:save!).with(no_args)
42
+ I18n::JS.export
43
+ end
44
+
45
+ it "exports to multiple files" do
46
+ set_config "multiple_files.yml"
47
+ I18n::JS.export
48
+
49
+ file_should_exist "all.js"
50
+ file_should_exist "tudo.js"
51
+ end
52
+
53
+ it "ignores an empty config file" do
54
+ set_config "no_config.yml"
55
+ I18n::JS.export
56
+
57
+ file_should_exist "translations.js"
58
+ end
59
+
60
+ it "exports to a JS file per available locale" do
61
+ set_config "js_file_per_locale.yml"
62
+ I18n::JS.export
63
+
64
+ file_should_exist "en.js"
65
+ file_should_exist "fr.js"
66
+
67
+ en_output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "en.js"))
68
+ expect(en_output).to eq(<<EOS
69
+ I18n.translations || (I18n.translations = {});
70
+ I18n.translations["en"] = I18n.extend((I18n.translations["en"] || {}), {"admin":{"edit":{"title":"Edit"},"show":{"note":"more details","title":"Show"}},"date":{"abbr_day_names":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"abbr_month_names":[null,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"day_names":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"formats":{"default":"%Y-%m-%d","long":"%B %d, %Y","short":"%b %d"},"month_names":[null,"January","February","March","April","May","June","July","August","September","October","November","December"]}});
71
+ EOS
72
+ )
73
+ fr_output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "fr.js"))
74
+ expect(fr_output).to eq(<<EOS
75
+ I18n.translations || (I18n.translations = {});
76
+ I18n.translations["fr"] = I18n.extend((I18n.translations["fr"] || {}), {"admin":{"edit":{"title":"Editer"},"show":{"note":"plus de détails","title":"Visualiser"}},"date":{"abbr_day_names":["dim","lun","mar","mer","jeu","ven","sam"],"abbr_month_names":[null,"jan.","fév.","mar.","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],"day_names":["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],"formats":{"default":"%d/%m/%Y","long":"%e %B %Y","long_ordinal":"%e %B %Y","only_day":"%e","short":"%e %b"},"month_names":[null,"janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"]}});
77
+ EOS
78
+ )
79
+ end
80
+
81
+ it "exports with multiple conditions" do
82
+ set_config "multiple_conditions.yml"
83
+ I18n::JS.export
84
+
85
+ file_should_exist "bitsnpieces.js"
86
+ end
87
+
88
+ it "exports with multiple conditions to a JS file per available locale" do
89
+ allow(::I18n).to receive(:available_locales){ [:en, :fr] }
90
+
91
+ set_config "multiple_conditions_per_locale.yml"
92
+
93
+ result = I18n::JS.translation_segments
94
+ result.map(&:file).should eql(["tmp/i18n-js/bits.%{locale}.js"])
95
+
96
+ result.map(&:save!)
97
+
98
+ en_output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "bits.en.js"))
99
+ expect(en_output).to eq(<<EOS
100
+ I18n.translations || (I18n.translations = {});
101
+ I18n.translations["en"] = I18n.extend((I18n.translations["en"] || {}), {"date":{"formats":{"default":"%Y-%m-%d","long":"%B %d, %Y","short":"%b %d"}},"number":{"currency":{"format":{"delimiter":",","format":"%u%n","precision":2,"separator":".","unit":"$"}}}});
102
+ EOS
103
+ )
104
+ fr_output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "bits.fr.js"))
105
+ expect(fr_output).to eq(<<EOS
106
+ I18n.translations || (I18n.translations = {});
107
+ I18n.translations["fr"] = I18n.extend((I18n.translations["fr"] || {}), {"date":{"formats":{"default":"%d/%m/%Y","long":"%e %B %Y","long_ordinal":"%e %B %Y","only_day":"%e","short":"%e %b"}},"number":{"currency":{"format":{"format":"%n %u","precision":2,"unit":"€"}}}});
108
+ EOS
109
+ )
110
+ end
111
+
112
+ it "exports with :except condition" do
113
+ set_config "except_condition.yml"
114
+ I18n::JS.export
115
+
116
+ file_should_exist "trimmed.js"
117
+ end
118
+
119
+ it "calls .export_i18n_js" do
120
+ allow(described_class).to receive(:export_i18n_js)
121
+ I18n::JS.export
122
+ expect(described_class).to have_received(:export_i18n_js).once
123
+ end
124
+ end
125
+
126
+ context "filters" do
127
+ it "filters translations using scope *.date.formats" do
128
+ result = I18n::JS.filter(translations, "*.date.formats")
129
+ result[:en][:date].keys.should eql([:formats])
130
+ result[:fr][:date].keys.should eql([:formats])
131
+ end
132
+
133
+ it "filters translations using scope [*.date.formats, *.number.currency.format]" do
134
+ result = I18n::JS.scoped_translations(["*.date.formats", "*.number.currency.format"])
135
+ result[:en].keys.collect(&:to_s).sort.should eql(%w[ date number ])
136
+ result[:fr].keys.collect(&:to_s).sort.should eql(%w[ date number ])
137
+ end
138
+
139
+ it "filters translations using multi-star scope" do
140
+ result = I18n::JS.scoped_translations("*.*.formats")
141
+
142
+ result[:en].keys.collect(&:to_s).sort.should eql(%w[ date time ])
143
+ result[:fr].keys.collect(&:to_s).sort.should eql(%w[ date time ])
144
+
145
+ result[:en][:date].keys.should eql([:formats])
146
+ result[:en][:time].keys.should eql([:formats])
147
+
148
+ result[:fr][:date].keys.should eql([:formats])
149
+ result[:fr][:time].keys.should eql([:formats])
150
+ end
151
+
152
+ it "filters translations using alternated stars" do
153
+ result = I18n::JS.scoped_translations("*.admin.*.title")
154
+
155
+ result[:en][:admin].keys.collect(&:to_s).sort.should eql(%w[ edit show ])
156
+ result[:fr][:admin].keys.collect(&:to_s).sort.should eql(%w[ edit show ])
157
+
158
+ result[:en][:admin][:show][:title].should eql("Show")
159
+ result[:fr][:admin][:show][:title].should eql("Visualiser")
160
+
161
+ result[:en][:admin][:edit][:title].should eql("Edit")
162
+ result[:fr][:admin][:edit][:title].should eql("Editer")
163
+ end
164
+
165
+ describe ".filtered_translations" do
166
+ subject do
167
+ I18n::JS.filtered_translations
168
+ end
169
+
170
+ let!(:old_sort_translation_keys) { I18n::JS.sort_translation_keys? }
171
+ before { I18n::JS.sort_translation_keys = sort_translation_keys_value }
172
+ after { I18n::JS.sort_translation_keys = old_sort_translation_keys }
173
+ before { expect(I18n::JS.sort_translation_keys?).to eq(sort_translation_keys_value) }
174
+
175
+ let(:sorted_hash) do
176
+ {sorted: :hash}
177
+ end
178
+ before do
179
+ allow(I18n::JS::Utils).
180
+ to receive(:deep_key_sort).
181
+ and_return(sorted_hash)
182
+ end
183
+
184
+ shared_examples_for ".filtered_translations" do
185
+ subject do
186
+ I18n::JS.filtered_translations
187
+ end
188
+
189
+ # This example is to prevent the regression from
190
+ # PR https://github.com/fnando/i18n-js/pull/318
191
+ it {should be_a(Hash)}
192
+ # Might need to test the keys... or not
193
+ end
194
+
195
+ context "when translation keys SHOULD be sorted" do
196
+ let(:sort_translation_keys_value) { true }
197
+
198
+ it_behaves_like ".filtered_translations"
199
+ it {should eq(sorted_hash)}
200
+ end
201
+ context "when translation keys should NOT be sorted" do
202
+ let(:sort_translation_keys_value) { false }
203
+
204
+ it_behaves_like ".filtered_translations"
205
+ it {should_not eq(sorted_hash)}
206
+ end
207
+ end
208
+ end
209
+
210
+ context "exceptions" do
211
+ it "does not include scopes listed in the exceptions list" do
212
+ result = I18n::JS.scoped_translations("*", ['de.*', '*.admin', '*.*.currency'])
213
+
214
+ result[:de].should be_empty
215
+
216
+ result[:en][:admin].should be_nil
217
+ result[:fr][:admin].should be_nil
218
+ result[:ja][:admin].should be_nil
219
+
220
+ result[:en][:number][:currency].should be_nil
221
+ result[:fr][:number][:currency].should be_nil
222
+ end
223
+
224
+ it "does not include scopes listed in the exceptions list and respects the 'only' option" do
225
+ result = I18n::JS.scoped_translations("fr.*", ['*.admin', '*.*.currency'])
226
+
227
+ result[:en].should be_nil
228
+ result[:de].should be_nil
229
+ result[:ja].should be_nil
230
+
231
+ result[:fr][:admin].should be_nil
232
+ result[:fr][:number][:currency].should be_nil
233
+
234
+ result[:fr][:time][:am].should be_a(String)
235
+ end
236
+
237
+ it "does exclude absolute scopes listed in the exceptions list" do
238
+ result = I18n::JS.scoped_translations("*", ['de', 'en.admin', 'fr.number.currency'])
239
+
240
+ result[:de].should be_nil
241
+
242
+ result[:en].should be_a(Hash)
243
+ result[:en][:admin].should be_nil
244
+
245
+ result[:fr][:number].should be_a(Hash)
246
+ result[:fr][:number][:currency].should be_nil
247
+ end
248
+
249
+ it "does not exclude non-absolute scopes listed in the exceptions list" do
250
+ result = I18n::JS.scoped_translations("*", ['admin', 'currency'])
251
+
252
+ result[:en][:admin].should be_a(Hash)
253
+ result[:fr][:admin].should be_a(Hash)
254
+ result[:ja][:admin].should be_a(Hash)
255
+
256
+ result[:en][:number][:currency].should be_a(Hash)
257
+ result[:fr][:number][:currency].should be_a(Hash)
258
+ end
259
+ end
260
+
261
+ context "fallbacks" do
262
+ subject(:translations) do
263
+ I18n::JS.translation_segments.first.translations
264
+ end
265
+
266
+ it "exports without fallback when disabled" do
267
+ set_config "js_file_per_locale_without_fallbacks.yml"
268
+ subject[:fr][:fallback_test].should eql(nil)
269
+ subject[:fr][:null_test].should eql(nil)
270
+ subject[:de][:null_test].should eql(nil)
271
+ end
272
+
273
+ it "exports with default_locale as fallback when enabled" do
274
+ set_config "js_file_per_locale_with_fallbacks_enabled.yml"
275
+ subject[:fr][:fallback_test].should eql("Success")
276
+ subject[:fr][:null_test].should eql("fallback for null")
277
+ subject[:de][:null_test].should eql("fallback for null")
278
+ end
279
+
280
+ it "exports with default_locale as fallback when enabled with :default_locale" do
281
+ set_config "js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml"
282
+ subject[:fr][:fallback_test].should eql("Success")
283
+ subject[:fr][:null_test].should eql("fallback for null")
284
+ subject[:de][:null_test].should eql("fallback for null")
285
+ end
286
+
287
+ it "exports with given locale as fallback" do
288
+ set_config "js_file_per_locale_with_fallbacks_as_locale.yml"
289
+ subject[:fr][:fallback_test].should eql("Erfolg")
290
+ subject[:fr][:null_test].should eql(nil)
291
+ subject[:de][:null_test].should eql(nil)
292
+ end
293
+
294
+ context "when given locale is in `I18n.available_locales` but its translation is missing" do
295
+ subject { translations[:fr][:fallback_test] }
296
+
297
+ let(:new_locale) { :pirate }
298
+ let!(:old_available_locales) { I18n.config.available_locales }
299
+ let!(:new_available_locales) { I18n.config.available_locales + [new_locale] }
300
+ before do
301
+ I18n.config.available_locales = new_available_locales
302
+ set_config "js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml"
303
+ end
304
+ after do
305
+ I18n.config.available_locales = old_available_locales
306
+ end
307
+
308
+ it {should eql(nil)}
309
+ end
310
+
311
+ context "with I18n::Fallbacks enabled" do
312
+ let(:backend_with_fallbacks) { backend_class_with_fallbacks.new }
313
+ let!(:old_backebad) { I18n.backend }
314
+
315
+ before do
316
+ I18n.backend = backend_with_fallbacks
317
+ I18n.fallbacks[:fr] = [:de, :en]
318
+ end
319
+ after { I18n.backend = old_backebad }
320
+
321
+ it "exports with defined locale as fallback when enabled" do
322
+ set_config "js_file_per_locale_with_fallbacks_enabled.yml"
323
+ subject[:fr][:fallback_test].should eql("Erfolg")
324
+ end
325
+
326
+ it "exports with defined locale as fallback when enabled with :default_locale" do
327
+ set_config "js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml"
328
+ subject[:fr][:fallback_test].should eql("Success")
329
+ end
330
+
331
+ it "exports with Fallbacks as Hash" do
332
+ set_config "js_file_per_locale_with_fallbacks_as_hash.yml"
333
+ subject[:fr][:fallback_test].should eql("Erfolg")
334
+ end
335
+ end
336
+ end
337
+
338
+ context "namespace and pretty_print options" do
339
+
340
+ before do
341
+ stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
342
+ set_config "js_file_with_namespace_and_pretty_print.yml"
343
+ end
344
+
345
+ it "exports with defined locale as fallback when enabled" do
346
+ I18n::JS.export
347
+ file_should_exist "en.js"
348
+ output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "en.js"))
349
+ expect(output).to match(/^#{
350
+ <<EOS
351
+ Foo.translations || (Foo.translations = {});
352
+ Foo.translations["en"] = {
353
+ "number": {
354
+ "format": {
355
+ EOS
356
+ }.+#{
357
+ <<EOS
358
+ "edit": {
359
+ "title": "Edit"
360
+ }
361
+ },
362
+ "foo": "Foo",
363
+ "fallback_test": "Success"
364
+ };
365
+ EOS
366
+ }$/)
367
+ end
368
+ end
369
+
370
+ context "I18n.available_locales" do
371
+
372
+ context "when I18n.available_locales is not set" do
373
+ it "should allow all locales" do
374
+ result = I18n::JS.scoped_translations("*.admin.*.title")
375
+
376
+ result[:en][:admin][:show][:title].should eql("Show")
377
+ result[:fr][:admin][:show][:title].should eql("Visualiser")
378
+ result[:ja][:admin][:show][:title].should eql("Ignore me")
379
+ end
380
+ end
381
+
382
+ context "when I18n.available_locales is set" do
383
+ before { allow(::I18n).to receive(:available_locales){ [:en, :fr] } }
384
+
385
+ it "should ignore non-valid locales" do
386
+ result = I18n::JS.scoped_translations("*.admin.*.title")
387
+
388
+ result[:en][:admin][:show][:title].should eql("Show")
389
+ result[:fr][:admin][:show][:title].should eql("Visualiser")
390
+ result.keys.include?(:ja).should eql(false)
391
+ end
392
+ end
393
+ end
394
+
395
+ context "general" do
396
+ it "sets export directory" do
397
+ I18n::JS::DEFAULT_EXPORT_DIR_PATH.should eql("public/javascripts")
398
+ end
399
+
400
+ it "sets empty hash as configuration when no file is found" do
401
+ I18n::JS.config_file_exists?.should eql(false)
402
+ I18n::JS.config.should eql({})
403
+ end
404
+
405
+ it "executes erb in config file" do
406
+ set_config "erb.yml"
407
+
408
+ config_entry = I18n::JS.config[:translations].first
409
+ config_entry["only"].should eq("*.date.formats")
410
+ end
411
+ end
412
+
413
+ describe "i18n.js exporting" do
414
+ after { begin described_class.send(:remove_instance_variable, :@export_i18n_js_dir_path); rescue; end }
415
+
416
+ describe ".export_i18n_js with global variable" do
417
+ before do
418
+ allow(FileUtils).to receive(:mkdir_p).and_call_original
419
+ allow(FileUtils).to receive(:cp).and_call_original
420
+
421
+ described_class.stub(:export_i18n_js_dir_path).and_return(export_i18n_js_dir_path)
422
+ I18n::JS.export_i18n_js
423
+ end
424
+
425
+ context 'when .export_i18n_js_dir_path returns something' do
426
+ let(:export_i18n_js_dir_path) { temp_path }
427
+
428
+ it "does create the folder before copying" do
429
+ expect(FileUtils).to have_received(:mkdir_p).with(export_i18n_js_dir_path).once
430
+ end
431
+ it "does copy the file with FileUtils.cp" do
432
+ expect(FileUtils).to have_received(:cp).once
433
+ end
434
+ it "exports the file" do
435
+ File.should be_file(File.join(I18n::JS.export_i18n_js_dir_path, "i18n.js"))
436
+ end
437
+ end
438
+
439
+ context 'when .export_i18n_js_dir_path is set to nil' do
440
+ let(:export_i18n_js_dir_path) { nil }
441
+
442
+ it "does NOT create the folder before copying" do
443
+ expect(FileUtils).to_not have_received(:mkdir_p)
444
+ end
445
+ it "does NOT copy the file with FileUtils.cp" do
446
+ expect(FileUtils).to_not have_received(:cp)
447
+ end
448
+ end
449
+ end
450
+
451
+ describe ".export_i18n_js with config" do
452
+
453
+ let(:export_action) do
454
+ allow(FileUtils).to receive(:mkdir_p).and_call_original
455
+ allow(FileUtils).to receive(:cp).and_call_original
456
+ I18n::JS.export_i18n_js
457
+ end
458
+
459
+ context 'when :export_i18n_js set in config' do
460
+ before { set_config "js_export_dir_custom.yml"; export_action }
461
+ let(:export_i18n_js_dir_path) { temp_path }
462
+ let(:config_export_path) { "tmp/i18n-js/foo" }
463
+
464
+ it "does create the folder before copying" do
465
+ expect(FileUtils).to have_received(:mkdir_p).with(config_export_path).once
466
+ end
467
+ it "does copy the file with FileUtils.cp" do
468
+ expect(FileUtils).to have_received(:cp).once
469
+ end
470
+ it "exports the file" do
471
+ File.should be_file(File.join(config_export_path, "i18n.js"))
472
+ end
473
+ end
474
+
475
+ context 'when .export_i18n_js_dir_path is set to false' do
476
+ before { set_config "js_export_dir_none.yml"; export_action }
477
+
478
+ it "does NOT create the folder before copying" do
479
+ expect(FileUtils).to_not have_received(:mkdir_p)
480
+ end
481
+
482
+ it "does NOT copy the file with FileUtils.cp" do
483
+ expect(FileUtils).to_not have_received(:cp)
484
+ end
485
+ end
486
+ end
487
+
488
+ describe '.export_i18n_js_dir_path' do
489
+ let(:default_path) { I18n::JS::DEFAULT_EXPORT_DIR_PATH }
490
+ let(:new_path) { File.join("tmp", default_path) }
491
+ after { described_class.send(:remove_instance_variable, :@export_i18n_js_dir_path) }
492
+
493
+ subject { described_class.export_i18n_js_dir_path }
494
+
495
+ context "when it is not set" do
496
+ it { should eq default_path }
497
+ end
498
+ context "when it is set to another path already" do
499
+ before { described_class.export_i18n_js_dir_path = new_path }
500
+
501
+ it { should eq new_path }
502
+ end
503
+ context "when it is set to nil already" do
504
+ before { described_class.export_i18n_js_dir_path = nil }
505
+
506
+ it { should eq :none }
507
+ end
508
+ end
509
+ end
510
+
511
+ describe "translation key sorting" do
512
+
513
+ describe ".sort_translation_keys?" do
514
+ after { described_class.send(:remove_instance_variable, :@sort_translation_keys) }
515
+ subject { described_class.sort_translation_keys? }
516
+
517
+
518
+ context "set with config" do
519
+
520
+ context 'when :sort_translation_keys is not set in config' do
521
+ before :each do
522
+ set_config "default.yml"
523
+ end
524
+
525
+ it { should eq true }
526
+ end
527
+
528
+ context 'when :sort_translation_keys set to true in config' do
529
+ before :each do
530
+ set_config "js_sort_translation_keys_true.yml"
531
+ end
532
+
533
+ it { should eq true }
534
+ end
535
+
536
+ context 'when :sort_translation_keys set to false in config' do
537
+ before :each do
538
+ set_config "js_sort_translation_keys_false.yml"
539
+ end
540
+
541
+ it { should eq false }
542
+ end
543
+ end
544
+
545
+ context 'set by .sort_translation_keys' do
546
+
547
+ context "when it is not set" do
548
+ it { should eq true }
549
+ end
550
+
551
+ context "when it is set to true" do
552
+ before { described_class.sort_translation_keys = true }
553
+
554
+ it { should eq true }
555
+ end
556
+
557
+ context "when it is set to false" do
558
+ before { described_class.sort_translation_keys = false }
559
+
560
+ it { should eq false }
561
+ end
562
+ end
563
+ end
564
+
565
+ context "exporting" do
566
+ subject do
567
+ I18n::JS.export
568
+ file_should_exist "en.js"
569
+ File.read(File.join(I18n::JS.export_i18n_js_dir_path, "en.js"))
570
+ end
571
+
572
+ before do
573
+ stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
574
+ end
575
+
576
+ context 'sort_translation_keys is true' do
577
+ before :each do
578
+ set_config "js_sort_translation_keys_true.yml"
579
+ end
580
+
581
+ it "exports with the keys sorted" do
582
+ expect(subject).to eq(<<EOS
583
+ I18n.translations || (I18n.translations = {});
584
+ I18n.translations["en"] = I18n.extend((I18n.translations["en"] || {}), {"admin":{"edit":{"title":"Edit"},"show":{"note":"more details","title":"Show"}},"date":{"abbr_day_names":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"abbr_month_names":[null,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"day_names":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"formats":{"default":"%Y-%m-%d","long":"%B %d, %Y","short":"%b %d"},"month_names":[null,"January","February","March","April","May","June","July","August","September","October","November","December"]},"fallback_test":"Success","foo":"Foo","null_test":"fallback for null","number":{"currency":{"format":{"delimiter":",","format":"%u%n","precision":2,"separator":".","unit":"$"}},"format":{"delimiter":",","precision":3,"separator":"."}},"time":{"am":"am","formats":{"default":"%a, %d %b %Y %H:%M:%S %z","long":"%B %d, %Y %H:%M","short":"%d %b %H:%M"},"pm":"pm"}});
585
+ EOS
586
+ )
587
+ end
588
+ end
589
+ end
590
+ end
591
+
592
+ describe "js_extend option" do
593
+ before do
594
+ stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
595
+ end
596
+
597
+ it "exports with js_extend option at parent level" do
598
+ set_config "js_extend_parent.yml"
599
+ I18n::JS.export
600
+
601
+ file_should_exist "js_extend_parent.js"
602
+
603
+ output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "js_extend_parent.js"))
604
+ expect(output).to eq(<<EOS
605
+ I18n.translations || (I18n.translations = {});
606
+ I18n.translations[\"en\"] = {\"date\":{\"formats\":{\"default\":\"%Y-%m-%d\",\"long\":\"%B %d, %Y\",\"short\":\"%b %d\"}}};
607
+ I18n.translations[\"fr\"] = {\"date\":{\"formats\":{\"default\":\"%d/%m/%Y\",\"long\":\"%e %B %Y\",\"long_ordinal\":\"%e %B %Y\",\"only_day\":\"%e\",\"short\":\"%e %b\"}}};
608
+ EOS
609
+ )
610
+ end
611
+
612
+ it "exports with js_extend option at segment level" do
613
+ set_config "js_extend_segment.yml"
614
+ I18n::JS.export
615
+
616
+ file_should_exist "js_extend_segment.js"
617
+
618
+ output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "js_extend_segment.js"))
619
+ expect(output).to eq(<<EOS
620
+ I18n.translations || (I18n.translations = {});
621
+ I18n.translations[\"en\"] = {\"date\":{\"formats\":{\"default\":\"%Y-%m-%d\",\"long\":\"%B %d, %Y\",\"short\":\"%b %d\"}}};
622
+ I18n.translations[\"fr\"] = {\"date\":{\"formats\":{\"default\":\"%d/%m/%Y\",\"long\":\"%e %B %Y\",\"long_ordinal\":\"%e %B %Y\",\"only_day\":\"%e\",\"short\":\"%e %b\"}}};
623
+ EOS
624
+ )
625
+ end
626
+ end
627
+ end