i18n-js 3.7.1 → 4.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +4 -0
- data/.github/FUNDING.yml +3 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +38 -0
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/ruby-tests.yml +61 -0
- data/.gitignore +11 -7
- data/.rubocop.yml +12 -0
- data/CHANGELOG.md +12 -495
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +3 -0
- data/LICENSE.md +20 -0
- data/README.md +89 -903
- data/Rakefile +10 -20
- data/exe/i18n +5 -0
- data/i18n-js.gemspec +47 -29
- data/lib/guard/i18n-js.rb +78 -0
- data/lib/guard/i18n-js/templates/Guardfile +10 -0
- data/lib/guard/i18n-js/version.rb +13 -0
- data/lib/i18n-js.rb +66 -1
- data/lib/i18n-js/cli.rb +47 -0
- data/lib/i18n-js/cli/command.rb +43 -0
- data/lib/i18n-js/cli/export_command.rb +108 -0
- data/lib/i18n-js/cli/init_command.rb +50 -0
- data/lib/i18n-js/cli/ui.rb +30 -0
- data/lib/i18n-js/cli/version_command.rb +18 -0
- data/lib/i18n-js/listen.rb +80 -0
- data/lib/i18n-js/schema.rb +92 -0
- data/lib/i18n-js/version.rb +5 -0
- metadata +123 -190
- data/.editorconfig +0 -24
- data/.npmignore +0 -27
- data/.travis.yml +0 -39
- data/Appraisals +0 -44
- data/app/assets/javascripts/i18n.js +0 -1092
- data/app/assets/javascripts/i18n/filtered.js.erb +0 -23
- data/app/assets/javascripts/i18n/shims.js +0 -240
- data/app/assets/javascripts/i18n/translations.js +0 -3
- data/gemfiles/i18n_0_6.gemfile +0 -7
- data/gemfiles/i18n_0_7.gemfile +0 -7
- data/gemfiles/i18n_0_8.gemfile +0 -7
- data/gemfiles/i18n_0_9.gemfile +0 -7
- data/gemfiles/i18n_1_0.gemfile +0 -7
- data/gemfiles/i18n_1_1.gemfile +0 -7
- data/gemfiles/i18n_1_2.gemfile +0 -7
- data/gemfiles/i18n_1_3.gemfile +0 -7
- data/gemfiles/i18n_1_4.gemfile +0 -7
- data/gemfiles/i18n_1_5.gemfile +0 -7
- data/gemfiles/i18n_1_6.gemfile +0 -7
- data/gemfiles/i18n_1_7.gemfile +0 -7
- data/gemfiles/i18n_1_8.gemfile +0 -7
- data/lib/i18n/js.rb +0 -259
- data/lib/i18n/js/dependencies.rb +0 -63
- data/lib/i18n/js/engine.rb +0 -87
- data/lib/i18n/js/fallback_locales.rb +0 -70
- data/lib/i18n/js/formatters/base.rb +0 -25
- data/lib/i18n/js/formatters/js.rb +0 -32
- data/lib/i18n/js/formatters/json.rb +0 -13
- data/lib/i18n/js/middleware.rb +0 -82
- data/lib/i18n/js/private/hash_with_symbol_keys.rb +0 -36
- data/lib/i18n/js/segment.rb +0 -80
- data/lib/i18n/js/utils.rb +0 -78
- data/lib/i18n/js/version.rb +0 -7
- data/lib/rails/generators/i18n/js/config/config_generator.rb +0 -19
- data/lib/rails/generators/i18n/js/config/templates/i18n-js.yml +0 -27
- data/lib/tasks/export.rake +0 -8
- data/package.json +0 -25
- data/spec/fixtures/custom_path.yml +0 -5
- data/spec/fixtures/default.yml +0 -5
- data/spec/fixtures/erb.yml +0 -5
- data/spec/fixtures/except_condition.yml +0 -7
- data/spec/fixtures/js_export_dir_custom.yml +0 -7
- data/spec/fixtures/js_export_dir_none.yml +0 -6
- data/spec/fixtures/js_extend_parent.yml +0 -6
- data/spec/fixtures/js_extend_segment.yml +0 -6
- data/spec/fixtures/js_file_per_locale.yml +0 -7
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml +0 -4
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_hash.yml +0 -6
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale.yml +0 -4
- data/spec/fixtures/js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml +0 -4
- data/spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml +0 -4
- data/spec/fixtures/js_file_per_locale_without_fallbacks.yml +0 -4
- data/spec/fixtures/js_file_with_namespace_prefix_and_pretty_print.yml +0 -9
- data/spec/fixtures/js_sort_translation_keys_false.yml +0 -6
- data/spec/fixtures/js_sort_translation_keys_true.yml +0 -6
- data/spec/fixtures/json_only.yml +0 -18
- data/spec/fixtures/locales.yml +0 -133
- data/spec/fixtures/merge_plurals.yml +0 -6
- data/spec/fixtures/merge_plurals_with_no_overrides.yml +0 -4
- data/spec/fixtures/merge_plurals_with_partial_overrides.yml +0 -4
- data/spec/fixtures/millions.yml +0 -4
- data/spec/fixtures/multiple_conditions.yml +0 -7
- data/spec/fixtures/multiple_conditions_per_locale.yml +0 -7
- data/spec/fixtures/multiple_files.yml +0 -7
- data/spec/fixtures/no_config.yml +0 -2
- data/spec/fixtures/no_scope.yml +0 -4
- data/spec/fixtures/simple_scope.yml +0 -5
- data/spec/js/currency.spec.js +0 -62
- data/spec/js/current_locale.spec.js +0 -19
- data/spec/js/dates.spec.js +0 -276
- data/spec/js/defaults.spec.js +0 -31
- data/spec/js/extend.spec.js +0 -110
- data/spec/js/interpolation.spec.js +0 -124
- data/spec/js/jasmine/MIT.LICENSE +0 -20
- data/spec/js/jasmine/jasmine-html.js +0 -190
- data/spec/js/jasmine/jasmine.css +0 -166
- data/spec/js/jasmine/jasmine.js +0 -2476
- data/spec/js/jasmine/jasmine_favicon.png +0 -0
- data/spec/js/locales.spec.js +0 -31
- data/spec/js/localization.spec.js +0 -78
- data/spec/js/numbers.spec.js +0 -170
- data/spec/js/placeholder.spec.js +0 -24
- data/spec/js/pluralization.spec.js +0 -219
- data/spec/js/prepare_options.spec.js +0 -41
- data/spec/js/require.js +0 -2083
- data/spec/js/specs.html +0 -49
- data/spec/js/specs_requirejs.html +0 -72
- data/spec/js/translate.spec.js +0 -299
- data/spec/js/translations.js +0 -172
- data/spec/js/utility_functions.spec.js +0 -20
- data/spec/ruby/i18n/js/fallback_locales_spec.rb +0 -84
- data/spec/ruby/i18n/js/segment_spec.rb +0 -219
- data/spec/ruby/i18n/js/utils_spec.rb +0 -106
- data/spec/ruby/i18n/js_spec.rb +0 -748
- data/spec/spec_helper.rb +0 -79
- data/yarn.lock +0 -131
@@ -1,20 +0,0 @@
|
|
1
|
-
var I18n = require("../../app/assets/javascripts/i18n");
|
2
|
-
|
3
|
-
describe("Utility Functions", function(){
|
4
|
-
beforeEach(function(){
|
5
|
-
I18n.reset();
|
6
|
-
});
|
7
|
-
|
8
|
-
describe("I18n.lookup", function() {
|
9
|
-
it("does not change locale on failed lookup", function(){
|
10
|
-
var fallback_locales = ['fallback1', 'fallback2'];
|
11
|
-
|
12
|
-
I18n.locales['lang'] = fallback_locales.slice();
|
13
|
-
expect(I18n.locales.lang).toEqual(fallback_locales);
|
14
|
-
|
15
|
-
I18n.lookup('anything', {locale: 'lang'})
|
16
|
-
expect(I18n.locales.lang).toEqual(fallback_locales);
|
17
|
-
});
|
18
|
-
|
19
|
-
})
|
20
|
-
});
|
@@ -1,84 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe I18n::JS::FallbackLocales do
|
4
|
-
let(:locale) { :fr }
|
5
|
-
let(:default_locale) { :en }
|
6
|
-
|
7
|
-
describe "#locales" do
|
8
|
-
let(:fallbacks_locales) { described_class.new(fallbacks, locale) }
|
9
|
-
subject { fallbacks_locales.locales }
|
10
|
-
|
11
|
-
let(:fetching_locales) { proc do fallbacks_locales.locales end }
|
12
|
-
|
13
|
-
context "when given true as fallbacks" do
|
14
|
-
let(:fallbacks) { true }
|
15
|
-
it { should eq([default_locale]) }
|
16
|
-
end
|
17
|
-
|
18
|
-
context "when given false as fallbacks" do
|
19
|
-
let(:fallbacks) { false }
|
20
|
-
it { expect(fetching_locales).to raise_error(ArgumentError) }
|
21
|
-
end
|
22
|
-
|
23
|
-
context "when given a valid locale as fallbacks" do
|
24
|
-
let(:fallbacks) { :de }
|
25
|
-
it { should eq([:de]) }
|
26
|
-
end
|
27
|
-
|
28
|
-
context "when given a valid Array as fallbacks" do
|
29
|
-
let(:fallbacks) { [:de, :en] }
|
30
|
-
it { should eq([:de, :en]) }
|
31
|
-
end
|
32
|
-
|
33
|
-
context "when given a valid Hash with current locale as key as fallbacks" do
|
34
|
-
let(:fallbacks) do { :fr => [:de, :en] } end
|
35
|
-
it { should eq([:de, :en]) }
|
36
|
-
end
|
37
|
-
|
38
|
-
context "when given a valid Hash without current locale as key as fallbacks" do
|
39
|
-
let(:fallbacks) do { :de => [:fr, :en] } end
|
40
|
-
it { should eq([default_locale]) }
|
41
|
-
end
|
42
|
-
|
43
|
-
context "when given a invalid locale as fallbacks" do
|
44
|
-
let(:fallbacks) { :invalid_locale }
|
45
|
-
it { should eq([:invalid_locale]) }
|
46
|
-
end
|
47
|
-
|
48
|
-
context "when given a invalid type as fallbacks" do
|
49
|
-
let(:fallbacks) { 42 }
|
50
|
-
it { expect(fetching_locales).to raise_error(ArgumentError) }
|
51
|
-
end
|
52
|
-
|
53
|
-
# I18n::Backend::Fallbacks
|
54
|
-
context "when I18n::Backend::Fallbacks is used" do
|
55
|
-
let(:backend_with_fallbacks) { backend_class_with_fallbacks.new }
|
56
|
-
|
57
|
-
before do
|
58
|
-
I18n::JS.backend = backend_with_fallbacks
|
59
|
-
I18n.fallbacks[:fr] = [:de, :en]
|
60
|
-
end
|
61
|
-
after { I18n::JS.backend = I18n::Backend::Simple.new }
|
62
|
-
|
63
|
-
context "given true as fallbacks" do
|
64
|
-
let(:fallbacks) { true }
|
65
|
-
it { should eq([:de, :en]) }
|
66
|
-
end
|
67
|
-
|
68
|
-
context "given :default_locale as fallbacks" do
|
69
|
-
let(:fallbacks) { :default_locale }
|
70
|
-
it { should eq([:en]) }
|
71
|
-
end
|
72
|
-
|
73
|
-
context "given a Hash with current locale as fallbacks" do
|
74
|
-
let(:fallbacks) do { :fr => [:en] } end
|
75
|
-
it { should eq([:en]) }
|
76
|
-
end
|
77
|
-
|
78
|
-
context "given a Hash without current locale as fallbacks" do
|
79
|
-
let(:fallbacks) do { :de => [:en] } end
|
80
|
-
it { should eq([:de, :en]) }
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,219 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe I18n::JS::Segment do
|
4
|
-
|
5
|
-
let(:file) { "tmp/i18n-js/segment.js" }
|
6
|
-
let(:translations){ { en: { "test" => "Test" }, fr: { "test" => "Test2" } } }
|
7
|
-
let(:namespace) { "MyNamespace" }
|
8
|
-
let(:pretty_print){ nil }
|
9
|
-
let(:json_only) { nil }
|
10
|
-
let(:js_extend) { nil }
|
11
|
-
let(:sort_translation_keys){ nil }
|
12
|
-
let(:options) { { namespace: namespace,
|
13
|
-
pretty_print: pretty_print,
|
14
|
-
json_only: json_only,
|
15
|
-
js_extend: js_extend,
|
16
|
-
sort_translation_keys: sort_translation_keys }.delete_if{|k,v| v.nil?} }
|
17
|
-
subject { I18n::JS::Segment.new(file, translations, options) }
|
18
|
-
|
19
|
-
describe ".new" do
|
20
|
-
|
21
|
-
it "should persist the file path variable" do
|
22
|
-
expect(subject.file).to eql("tmp/i18n-js/segment.js")
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should persist the translations variable" do
|
26
|
-
expect(subject.translations).to eql(translations)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should persist the namespace variable" do
|
30
|
-
expect(subject.namespace).to eql("MyNamespace")
|
31
|
-
end
|
32
|
-
|
33
|
-
context "when namespace is nil" do
|
34
|
-
let(:namespace){ nil }
|
35
|
-
|
36
|
-
it "should default namespace to `I18n`" do
|
37
|
-
expect(subject.namespace).to eql("I18n")
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context "when namespace is not set" do
|
42
|
-
subject { I18n::JS::Segment.new(file, translations) }
|
43
|
-
|
44
|
-
it "should default namespace to `I18n`" do
|
45
|
-
expect(subject.namespace).to eql("I18n")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context "when pretty_print is nil" do
|
50
|
-
it "should set pretty_print to false" do
|
51
|
-
expect(subject.pretty_print).to be false
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context "when pretty_print is truthy" do
|
56
|
-
let(:pretty_print){ 1 }
|
57
|
-
|
58
|
-
it "should set pretty_print to true" do
|
59
|
-
expect(subject.pretty_print).to be true
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe "#saving!" do
|
65
|
-
before { allow(I18n::JS).to receive(:export_i18n_js_dir_path).and_return(temp_path) }
|
66
|
-
|
67
|
-
context "when json_only is true with locale" do
|
68
|
-
let(:file){ "tmp/i18n-js/%{locale}.js" }
|
69
|
-
let(:json_only){ true }
|
70
|
-
|
71
|
-
it 'should output JSON files per locale' do
|
72
|
-
subject.save!
|
73
|
-
file_should_exist "en.js"
|
74
|
-
file_should_exist "fr.js"
|
75
|
-
|
76
|
-
expect(File.read(File.join(temp_path, "en.js"))).to eql(
|
77
|
-
%Q({"en":{"test":"Test"}})
|
78
|
-
)
|
79
|
-
|
80
|
-
expect(File.read(File.join(temp_path, "fr.js"))).to eql(
|
81
|
-
%Q({"fr":{"test":"Test2"}})
|
82
|
-
)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
context "when json_only is true without locale" do
|
87
|
-
let(:file){ "tmp/i18n-js/segment.js" }
|
88
|
-
let(:json_only){ true }
|
89
|
-
|
90
|
-
it 'should output one JSON file for all locales' do
|
91
|
-
subject.save!
|
92
|
-
file_should_exist "segment.js"
|
93
|
-
|
94
|
-
expect(File.read(File.join(temp_path, "segment.js"))).to eql(
|
95
|
-
%Q({"en":{"test":"Test"},"fr":{"test":"Test2"}})
|
96
|
-
)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
context "when json_only and pretty print are true" do
|
101
|
-
let(:file){ "tmp/i18n-js/segment.js" }
|
102
|
-
let(:json_only){ true }
|
103
|
-
let(:pretty_print){ true }
|
104
|
-
|
105
|
-
it 'should output one JSON file for all locales' do
|
106
|
-
subject.save!
|
107
|
-
file_should_exist "segment.js"
|
108
|
-
|
109
|
-
expect(File.read(File.join(temp_path, "segment.js"))).to eql <<-EOS
|
110
|
-
{
|
111
|
-
"en": {
|
112
|
-
"test": "Test"
|
113
|
-
},
|
114
|
-
"fr": {
|
115
|
-
"test": "Test2"
|
116
|
-
}
|
117
|
-
}
|
118
|
-
EOS
|
119
|
-
.chomp
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
describe "#save!" do
|
125
|
-
before { allow(I18n::JS).to receive(:export_i18n_js_dir_path).and_return(temp_path) }
|
126
|
-
before { subject.save! }
|
127
|
-
|
128
|
-
context "when file does not include %{locale}" do
|
129
|
-
it "should write the file" do
|
130
|
-
file_should_exist "segment.js"
|
131
|
-
|
132
|
-
expect(File.open(File.join(temp_path, "segment.js")){|f| f.read}).to eql <<-EOF
|
133
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
134
|
-
MyNamespace.translations["en"] = I18n.extend((MyNamespace.translations["en"] || {}), {"test":"Test"});
|
135
|
-
MyNamespace.translations["fr"] = I18n.extend((MyNamespace.translations["fr"] || {}), {"test":"Test2"});
|
136
|
-
EOF
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
context "when file includes %{locale}" do
|
141
|
-
let(:file){ "tmp/i18n-js/%{locale}.js" }
|
142
|
-
|
143
|
-
it "should write files" do
|
144
|
-
file_should_exist "en.js"
|
145
|
-
file_should_exist "fr.js"
|
146
|
-
|
147
|
-
expect(File.open(File.join(temp_path, "en.js")){|f| f.read}).to eql <<-EOF
|
148
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
149
|
-
MyNamespace.translations["en"] = I18n.extend((MyNamespace.translations["en"] || {}), {"test":"Test"});
|
150
|
-
EOF
|
151
|
-
|
152
|
-
expect(File.open(File.join(temp_path, "fr.js")){|f| f.read}).to eql <<-EOF
|
153
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
154
|
-
MyNamespace.translations["fr"] = I18n.extend((MyNamespace.translations["fr"] || {}), {"test":"Test2"});
|
155
|
-
EOF
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
context "when js_extend is true" do
|
160
|
-
let(:js_extend){ true }
|
161
|
-
|
162
|
-
let(:translations){ { en: { "b" => "Test", "a" => "Test" } } }
|
163
|
-
|
164
|
-
it 'should output the keys as sorted' do
|
165
|
-
file_should_exist "segment.js"
|
166
|
-
|
167
|
-
expect(File.open(File.join(temp_path, "segment.js")){|f| f.read}).to eql <<-EOF
|
168
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
169
|
-
MyNamespace.translations["en"] = I18n.extend((MyNamespace.translations["en"] || {}), {"a":"Test","b":"Test"});
|
170
|
-
EOF
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
context "when js_extend is false" do
|
175
|
-
let(:js_extend){ false }
|
176
|
-
|
177
|
-
let(:translations){ { en: { "b" => "Test", "a" => "Test" } } }
|
178
|
-
|
179
|
-
it 'should output the keys as sorted' do
|
180
|
-
file_should_exist "segment.js"
|
181
|
-
|
182
|
-
expect(File.open(File.join(temp_path, "segment.js")){|f| f.read}).to eql <<-EOF
|
183
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
184
|
-
MyNamespace.translations["en"] = {"a":"Test","b":"Test"};
|
185
|
-
EOF
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
context "when sort_translation_keys is true" do
|
190
|
-
let(:sort_translation_keys){ true }
|
191
|
-
|
192
|
-
let(:translations){ { en: { "b" => "Test", "a" => "Test" } } }
|
193
|
-
|
194
|
-
it 'should output the keys as sorted' do
|
195
|
-
file_should_exist "segment.js"
|
196
|
-
|
197
|
-
expect(File.open(File.join(temp_path, "segment.js")){|f| f.read}).to eql <<-EOF
|
198
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
199
|
-
MyNamespace.translations["en"] = I18n.extend((MyNamespace.translations["en"] || {}), {"a":"Test","b":"Test"});
|
200
|
-
EOF
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
context "when sort_translation_keys is false" do
|
205
|
-
let(:sort_translation_keys){ false }
|
206
|
-
|
207
|
-
let(:translations){ { en: { "b" => "Test", "a" => "Test" } } }
|
208
|
-
|
209
|
-
it 'should output the keys as sorted' do
|
210
|
-
file_should_exist "segment.js"
|
211
|
-
|
212
|
-
expect(File.open(File.join(temp_path, "segment.js")){|f| f.read}).to eql <<-EOF
|
213
|
-
MyNamespace.translations || (MyNamespace.translations = {});
|
214
|
-
MyNamespace.translations["en"] = I18n.extend((MyNamespace.translations["en"] || {}), {"b":"Test","a":"Test"});
|
215
|
-
EOF
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe I18n::JS::Utils do
|
4
|
-
|
5
|
-
describe ".strip_keys_with_nil_values" do
|
6
|
-
subject { described_class.strip_keys_with_nil_values(input_hash) }
|
7
|
-
|
8
|
-
context 'when input_hash does NOT contain nil value' do
|
9
|
-
let(:input_hash) { {a: 1, b: { c: 2 }} }
|
10
|
-
let(:expected_hash) { input_hash }
|
11
|
-
|
12
|
-
it 'returns the original input' do
|
13
|
-
is_expected.to eq expected_hash
|
14
|
-
end
|
15
|
-
end
|
16
|
-
context 'when input_hash does contain nil value' do
|
17
|
-
let(:input_hash) { {a: 1, b: { c: 2, d: nil }, e: { f: nil }} }
|
18
|
-
let(:expected_hash) { {a: 1, b: { c: 2 }, e: {}} }
|
19
|
-
|
20
|
-
it 'returns the original input with nil values removed' do
|
21
|
-
is_expected.to eq expected_hash
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "hash merging" do
|
27
|
-
it "performs a deep merge" do
|
28
|
-
target = {:a => {:b => 1}}
|
29
|
-
result = described_class.deep_merge(target, {:a => {:c => 2}})
|
30
|
-
|
31
|
-
expect(result[:a]).to eql({:b => 1, :c => 2})
|
32
|
-
end
|
33
|
-
|
34
|
-
it "performs a banged deep merge" do
|
35
|
-
target = {:a => {:b => 1}}
|
36
|
-
described_class.deep_merge!(target, {:a => {:c => 2}})
|
37
|
-
|
38
|
-
expect(target[:a]).to eql({:b => 1, :c => 2})
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe ".deep_reject" do
|
43
|
-
it "performs a deep keys rejection" do
|
44
|
-
hash = {:a => {:b => 1}}
|
45
|
-
|
46
|
-
result = described_class.deep_reject(hash) { |k, v| k == :b }
|
47
|
-
|
48
|
-
expect(result).to eql({:a => {}})
|
49
|
-
end
|
50
|
-
|
51
|
-
it "performs a deep keys rejection prunning the whole tree if necessary" do
|
52
|
-
hash = {:a => {:b => {:c => {:d => 1, :e => 2}}}}
|
53
|
-
|
54
|
-
result = described_class.deep_reject(hash) { |k, v| k == :b }
|
55
|
-
|
56
|
-
expect(result).to eql({:a => {}})
|
57
|
-
end
|
58
|
-
|
59
|
-
|
60
|
-
it "performs a deep keys rejection without changing the original hash" do
|
61
|
-
hash = {:a => {:b => 1, :c => 2}}
|
62
|
-
|
63
|
-
result = described_class.deep_reject(hash) { |k, v| k == :b }
|
64
|
-
|
65
|
-
expect(result).to eql({:a => {:c => 2}})
|
66
|
-
expect(hash).to eql({:a => {:b => 1, :c => 2}})
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe ".deep_key_sort" do
|
71
|
-
let(:unsorted_hash) { {:z => {:b => 1, :a => 2}, :y => 3} }
|
72
|
-
subject(:sorting) { described_class.deep_key_sort(unsorted_hash) }
|
73
|
-
|
74
|
-
it "performs a deep keys sort without changing the original hash" do
|
75
|
-
should eql({:y => 3, :z => {:a => 2, :b => 1}})
|
76
|
-
expect(unsorted_hash).to eql({:z => {:b => 1, :a => 2}, :y => 3})
|
77
|
-
end
|
78
|
-
|
79
|
-
# Idea from gem `rails_admin`
|
80
|
-
context "when hash contain non-Symbol as key" do
|
81
|
-
let(:unsorted_hash) { {:z => {1 => 1, true => 2}, :y => 3} }
|
82
|
-
|
83
|
-
it "performs a deep keys sort without error" do
|
84
|
-
expect{ sorting }.to_not raise_error
|
85
|
-
end
|
86
|
-
it "converts keys to symbols" do
|
87
|
-
should eql({:y => 3, :z => {1 => 1, true => 2}})
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe ".scopes_match?" do
|
93
|
-
it "performs a comparison of literal scopes" do
|
94
|
-
expect(described_class.scopes_match?([:a, :b], [:a, :b, :c])).to_not eql true
|
95
|
-
expect(described_class.scopes_match?([:a, :b, :c], [:a, :b, :c])).to eql true
|
96
|
-
expect(described_class.scopes_match?([:a, :b, :c], [:a, :b, :d])).to_not eql true
|
97
|
-
end
|
98
|
-
|
99
|
-
it "performs a comparison of wildcard scopes" do
|
100
|
-
expect(described_class.scopes_match?([:a, '*'], [:a, :b, :c])).to_not eql true
|
101
|
-
expect(described_class.scopes_match?([:a, '*', :c], [:a, :b, :c])).to eql true
|
102
|
-
expect(described_class.scopes_match?([:a, :b, :c], [:a, '*', :c])).to eql true
|
103
|
-
expect(described_class.scopes_match?([:a, :b, :c], [:a, '*', '*'])).to eql true
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
data/spec/ruby/i18n/js_spec.rb
DELETED
@@ -1,748 +0,0 @@
|
|
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
|
-
allow(I18n::JS::Segment).to receive(:new).with("tmp/i18n-js/all.js", translations, {js_extend: true, sort_translation_keys: true, json_only: false}).and_call_original
|
34
|
-
allow_any_instance_of(I18n::JS::Segment).to 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
|
-
allow(I18n::JS::Segment).to receive(:new).with("tmp/i18n-js/no_scope.js", translations, {js_extend: true, sort_translation_keys: true, json_only: false}).and_call_original
|
41
|
-
allow_any_instance_of(I18n::JS::Segment).to 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
|
-
expect(result.map(&:file)).to 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 as json only" do
|
113
|
-
set_config "json_only.yml"
|
114
|
-
I18n::JS.export
|
115
|
-
en_output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "json_only.en.js"))
|
116
|
-
fr_output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "json_only.fr.js"))
|
117
|
-
expect(en_output).to eq (<<EOS
|
118
|
-
{"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":"$"}}}}}
|
119
|
-
EOS
|
120
|
-
).chop
|
121
|
-
expect(fr_output).to eq (<<EOS
|
122
|
-
{"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":"€"}}}}}
|
123
|
-
EOS
|
124
|
-
).chop
|
125
|
-
expect(JSON.parse(en_output).keys.first).to eq 'en'
|
126
|
-
end
|
127
|
-
|
128
|
-
it "exports with :except condition" do
|
129
|
-
set_config "except_condition.yml"
|
130
|
-
I18n::JS.export
|
131
|
-
|
132
|
-
file_should_exist "trimmed.js"
|
133
|
-
end
|
134
|
-
|
135
|
-
it "calls .export_i18n_js" do
|
136
|
-
allow(described_class).to receive(:export_i18n_js)
|
137
|
-
I18n::JS.export
|
138
|
-
expect(described_class).to have_received(:export_i18n_js).once
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
context "filters" do
|
143
|
-
it "filters translations using scope *.date.formats" do
|
144
|
-
result = I18n::JS.filter(translations, "*.date.formats")
|
145
|
-
expect(result[:en][:date].keys).to eql([:formats])
|
146
|
-
expect(result[:fr][:date].keys).to eql([:formats])
|
147
|
-
end
|
148
|
-
|
149
|
-
it "filters translations using scope [*.date.formats, *.number.currency.format]" do
|
150
|
-
result = I18n::JS.scoped_translations(["*.date.formats", "*.number.currency.format"])
|
151
|
-
expect(result[:en].keys.collect(&:to_s).sort).to eql(%w[ date number ])
|
152
|
-
expect(result[:fr].keys.collect(&:to_s).sort).to eql(%w[ date number ])
|
153
|
-
end
|
154
|
-
|
155
|
-
it "filters translations using multi-star scope" do
|
156
|
-
result = I18n::JS.scoped_translations("*.*.formats")
|
157
|
-
|
158
|
-
expect(result[:en].keys.collect(&:to_s).sort).to eql(%w[ date time ])
|
159
|
-
expect(result[:fr].keys.collect(&:to_s).sort).to eql(%w[ date time ])
|
160
|
-
|
161
|
-
expect(result[:en][:date].keys).to eql([:formats])
|
162
|
-
expect(result[:en][:time].keys).to eql([:formats])
|
163
|
-
|
164
|
-
expect(result[:fr][:date].keys).to eql([:formats])
|
165
|
-
expect(result[:fr][:time].keys).to eql([:formats])
|
166
|
-
end
|
167
|
-
|
168
|
-
it "filters translations using alternated stars" do
|
169
|
-
result = I18n::JS.scoped_translations("*.admin.*.title")
|
170
|
-
|
171
|
-
expect(result[:en][:admin].keys.collect(&:to_s).sort).to eql(%w[ edit show ])
|
172
|
-
expect(result[:fr][:admin].keys.collect(&:to_s).sort).to eql(%w[ edit show ])
|
173
|
-
|
174
|
-
expect(result[:en][:admin][:show][:title]).to eql("Show")
|
175
|
-
expect(result[:fr][:admin][:show][:title]).to eql("Visualiser")
|
176
|
-
|
177
|
-
expect(result[:en][:admin][:edit][:title]).to eql("Edit")
|
178
|
-
expect(result[:fr][:admin][:edit][:title]).to eql("Editer")
|
179
|
-
end
|
180
|
-
|
181
|
-
describe ".filtered_translations" do
|
182
|
-
subject do
|
183
|
-
I18n::JS.filtered_translations
|
184
|
-
end
|
185
|
-
|
186
|
-
let!(:old_sort_translation_keys) { I18n::JS.sort_translation_keys? }
|
187
|
-
before { I18n::JS.sort_translation_keys = sort_translation_keys_value }
|
188
|
-
after { I18n::JS.sort_translation_keys = old_sort_translation_keys }
|
189
|
-
before { expect(I18n::JS.sort_translation_keys?).to eq(sort_translation_keys_value) }
|
190
|
-
|
191
|
-
let(:sorted_hash) do
|
192
|
-
{sorted: :hash}
|
193
|
-
end
|
194
|
-
before do
|
195
|
-
allow(I18n::JS::Utils).
|
196
|
-
to receive(:deep_key_sort).
|
197
|
-
and_return(sorted_hash)
|
198
|
-
end
|
199
|
-
|
200
|
-
shared_examples_for ".filtered_translations" do
|
201
|
-
subject do
|
202
|
-
I18n::JS.filtered_translations
|
203
|
-
end
|
204
|
-
|
205
|
-
# This example is to prevent the regression from
|
206
|
-
# PR https://github.com/fnando/i18n-js/pull/318
|
207
|
-
it {should be_a(Hash)}
|
208
|
-
# Might need to test the keys... or not
|
209
|
-
end
|
210
|
-
|
211
|
-
context "when translation keys SHOULD be sorted" do
|
212
|
-
let(:sort_translation_keys_value) { true }
|
213
|
-
|
214
|
-
it_behaves_like ".filtered_translations"
|
215
|
-
it {should eq(sorted_hash)}
|
216
|
-
end
|
217
|
-
context "when translation keys should NOT be sorted" do
|
218
|
-
let(:sort_translation_keys_value) { false }
|
219
|
-
|
220
|
-
it_behaves_like ".filtered_translations"
|
221
|
-
it {should_not eq(sorted_hash)}
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
context "exceptions" do
|
227
|
-
it "does not include scopes listed in the exceptions list" do
|
228
|
-
result = I18n::JS.scoped_translations("*", ['de.*', '*.admin', '*.*.currency'])
|
229
|
-
|
230
|
-
expect(result[:de]).to be_empty
|
231
|
-
|
232
|
-
expect(result[:en][:admin]).to be_nil
|
233
|
-
expect(result[:fr][:admin]).to be_nil
|
234
|
-
expect(result[:ja][:admin]).to be_nil
|
235
|
-
|
236
|
-
expect(result[:en][:number][:currency]).to be_nil
|
237
|
-
expect(result[:fr][:number][:currency]).to be_nil
|
238
|
-
end
|
239
|
-
|
240
|
-
it "does not include scopes listed in the exceptions list and respects the 'only' option" do
|
241
|
-
result = I18n::JS.scoped_translations("fr.*", ['*.admin', '*.*.currency'])
|
242
|
-
|
243
|
-
expect(result[:en]).to be_nil
|
244
|
-
expect(result[:de]).to be_nil
|
245
|
-
expect(result[:ja]).to be_nil
|
246
|
-
|
247
|
-
expect(result[:fr][:admin]).to be_nil
|
248
|
-
expect(result[:fr][:number][:currency]).to be_nil
|
249
|
-
|
250
|
-
expect(result[:fr][:time][:am]).to be_a(String)
|
251
|
-
end
|
252
|
-
|
253
|
-
it "does exclude absolute scopes listed in the exceptions list" do
|
254
|
-
result = I18n::JS.scoped_translations("*", ['de', 'en.admin', 'fr.number.currency'])
|
255
|
-
|
256
|
-
expect(result[:de]).to be_nil
|
257
|
-
|
258
|
-
expect(result[:en]).to be_a(Hash)
|
259
|
-
expect(result[:en][:admin]).to be_nil
|
260
|
-
|
261
|
-
expect(result[:fr][:number]).to be_a(Hash)
|
262
|
-
expect(result[:fr][:number][:currency]).to be_nil
|
263
|
-
end
|
264
|
-
|
265
|
-
it "does not exclude non-absolute scopes listed in the exceptions list" do
|
266
|
-
result = I18n::JS.scoped_translations("*", ['admin', 'currency'])
|
267
|
-
|
268
|
-
expect(result[:en][:admin]).to be_a(Hash)
|
269
|
-
expect(result[:fr][:admin]).to be_a(Hash)
|
270
|
-
expect(result[:ja][:admin]).to be_a(Hash)
|
271
|
-
|
272
|
-
expect(result[:en][:number][:currency]).to be_a(Hash)
|
273
|
-
expect(result[:fr][:number][:currency]).to be_a(Hash)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
context "fallbacks" do
|
278
|
-
subject(:translations) do
|
279
|
-
I18n::JS.translation_segments.first.translations
|
280
|
-
end
|
281
|
-
|
282
|
-
it "exports without fallback when disabled" do
|
283
|
-
set_config "js_file_per_locale_without_fallbacks.yml"
|
284
|
-
expect(subject[:fr][:fallback_test]).to eql(nil)
|
285
|
-
expect(subject[:fr][:null_test]).to eql(nil)
|
286
|
-
expect(subject[:de][:null_test]).to eql(nil)
|
287
|
-
end
|
288
|
-
|
289
|
-
it "exports with default_locale as fallback when enabled" do
|
290
|
-
set_config "js_file_per_locale_with_fallbacks_enabled.yml"
|
291
|
-
expect(subject[:fr][:fallback_test]).to eql("Success")
|
292
|
-
expect(subject[:fr][:null_test]).to eql("fallback for null")
|
293
|
-
expect(subject[:de][:null_test]).to eql("fallback for null")
|
294
|
-
end
|
295
|
-
|
296
|
-
it "exports with default_locale as fallback when enabled with :default_locale" do
|
297
|
-
set_config "js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml"
|
298
|
-
expect(subject[:fr][:fallback_test]).to eql("Success")
|
299
|
-
expect(subject[:fr][:null_test]).to eql("fallback for null")
|
300
|
-
expect(subject[:de][:null_test]).to eql("fallback for null")
|
301
|
-
end
|
302
|
-
|
303
|
-
it "exports with given locale as fallback" do
|
304
|
-
set_config "js_file_per_locale_with_fallbacks_as_locale.yml"
|
305
|
-
expect(subject[:fr][:fallback_test]).to eql("Erfolg")
|
306
|
-
expect(subject[:fr][:null_test]).to eql(nil)
|
307
|
-
expect(subject[:de][:null_test]).to eql(nil)
|
308
|
-
end
|
309
|
-
|
310
|
-
context "when given locale is in `I18n.available_locales` but its translation is missing" do
|
311
|
-
subject { translations[:fr][:fallback_test] }
|
312
|
-
|
313
|
-
let(:new_locale) { :pirate }
|
314
|
-
let!(:old_available_locales) { I18n.config.available_locales }
|
315
|
-
let!(:new_available_locales) { I18n.config.available_locales + [new_locale] }
|
316
|
-
before do
|
317
|
-
I18n.config.available_locales = new_available_locales
|
318
|
-
set_config "js_file_per_locale_with_fallbacks_as_locale_without_fallback_translations.yml"
|
319
|
-
end
|
320
|
-
after do
|
321
|
-
I18n.config.available_locales = old_available_locales
|
322
|
-
end
|
323
|
-
|
324
|
-
it {should eql(nil)}
|
325
|
-
end
|
326
|
-
|
327
|
-
context "with I18n::Fallbacks enabled" do
|
328
|
-
let(:backend_with_fallbacks) { backend_class_with_fallbacks.new }
|
329
|
-
let!(:old_backebad) { I18n.backend }
|
330
|
-
|
331
|
-
before do
|
332
|
-
I18n::JS.backend = backend_with_fallbacks
|
333
|
-
I18n.fallbacks[:fr] = [:de, :en]
|
334
|
-
end
|
335
|
-
after { I18n::JS.backend = old_backebad }
|
336
|
-
|
337
|
-
it "exports with defined locale as fallback when enabled" do
|
338
|
-
set_config "js_file_per_locale_with_fallbacks_enabled.yml"
|
339
|
-
expect(subject[:fr][:fallback_test]).to eql("Erfolg")
|
340
|
-
end
|
341
|
-
|
342
|
-
it "exports with defined locale as fallback when enabled with :default_locale" do
|
343
|
-
set_config "js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml"
|
344
|
-
expect(subject[:fr][:fallback_test]).to eql("Success")
|
345
|
-
end
|
346
|
-
|
347
|
-
it "exports with Fallbacks as Hash" do
|
348
|
-
set_config "js_file_per_locale_with_fallbacks_as_hash.yml"
|
349
|
-
expect(subject[:fr][:fallback_test]).to eql("Erfolg")
|
350
|
-
end
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
context "namespace, prefix, suffix, and pretty_print options" do
|
355
|
-
|
356
|
-
before do
|
357
|
-
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
358
|
-
set_config "js_file_with_namespace_prefix_and_pretty_print.yml"
|
359
|
-
end
|
360
|
-
|
361
|
-
it "exports with defined locale as fallback when enabled" do
|
362
|
-
I18n::JS.export
|
363
|
-
file_should_exist "en.js"
|
364
|
-
output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "en.js"))
|
365
|
-
expect(output).to match(/^#{
|
366
|
-
<<EOS
|
367
|
-
import random from 'random-library';
|
368
|
-
Foo.translations || (Foo.translations = {});
|
369
|
-
Foo.translations["en"] = {
|
370
|
-
"number": {
|
371
|
-
"format": {
|
372
|
-
EOS
|
373
|
-
}.+#{
|
374
|
-
<<EOS
|
375
|
-
"edit": {
|
376
|
-
"title": "Edit"
|
377
|
-
}
|
378
|
-
},
|
379
|
-
"foo": "Foo",
|
380
|
-
"fallback_test": "Success"
|
381
|
-
};
|
382
|
-
//test
|
383
|
-
EOS
|
384
|
-
}$/)
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
context "I18n.available_locales" do
|
389
|
-
|
390
|
-
context "when I18n.available_locales is not set" do
|
391
|
-
it "should allow all locales" do
|
392
|
-
result = I18n::JS.scoped_translations("*.admin.*.title")
|
393
|
-
|
394
|
-
expect(result[:en][:admin][:show][:title]).to eql("Show")
|
395
|
-
expect(result[:fr][:admin][:show][:title]).to eql("Visualiser")
|
396
|
-
expect(result[:ja][:admin][:show][:title]).to eql("Ignore me")
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
context "when I18n.available_locales is set" do
|
401
|
-
before { allow(::I18n).to receive(:available_locales){ [:en, :fr] } }
|
402
|
-
|
403
|
-
it "should ignore non-valid locales" do
|
404
|
-
result = I18n::JS.scoped_translations("*.admin.*.title")
|
405
|
-
|
406
|
-
expect(result[:en][:admin][:show][:title]).to eql("Show")
|
407
|
-
expect(result[:fr][:admin][:show][:title]).to eql("Visualiser")
|
408
|
-
expect(result.keys.include?(:ja)).to eql(false)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
context "general" do
|
414
|
-
it "sets export directory" do
|
415
|
-
expect(I18n::JS::DEFAULT_EXPORT_DIR_PATH).to eql("public/javascripts")
|
416
|
-
end
|
417
|
-
|
418
|
-
it "sets empty hash as configuration when no file is found" do
|
419
|
-
expect(I18n::JS.config_file_exists?).to eql(false)
|
420
|
-
expect(I18n::JS.config).to eql({})
|
421
|
-
end
|
422
|
-
|
423
|
-
it "executes erb in config file" do
|
424
|
-
set_config "erb.yml"
|
425
|
-
|
426
|
-
config_entry = I18n::JS.config[:translations].first
|
427
|
-
expect(config_entry["only"]).to eq("*.date.formats")
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
describe "i18n.js exporting" do
|
432
|
-
after { begin described_class.send(:remove_instance_variable, :@export_i18n_js_dir_path); rescue; end }
|
433
|
-
|
434
|
-
describe ".export_i18n_js with global variable" do
|
435
|
-
before do
|
436
|
-
allow(FileUtils).to receive(:mkdir_p).and_call_original
|
437
|
-
allow(FileUtils).to receive(:cp).and_call_original
|
438
|
-
|
439
|
-
allow(described_class).to receive(:export_i18n_js_dir_path).and_return(export_i18n_js_dir_path)
|
440
|
-
I18n::JS.export_i18n_js
|
441
|
-
end
|
442
|
-
|
443
|
-
context 'when .export_i18n_js_dir_path returns something' do
|
444
|
-
let(:export_i18n_js_dir_path) { temp_path }
|
445
|
-
|
446
|
-
it "does create the folder before copying" do
|
447
|
-
expect(FileUtils).to have_received(:mkdir_p).with(export_i18n_js_dir_path).once
|
448
|
-
end
|
449
|
-
it "does copy the file with FileUtils.cp" do
|
450
|
-
expect(FileUtils).to have_received(:cp).once
|
451
|
-
end
|
452
|
-
it "exports the file" do
|
453
|
-
expect(File).to be_file(File.join(I18n::JS.export_i18n_js_dir_path, "i18n.js"))
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
context 'when .export_i18n_js_dir_path is set to nil' do
|
458
|
-
let(:export_i18n_js_dir_path) { nil }
|
459
|
-
|
460
|
-
it "does NOT create the folder before copying" do
|
461
|
-
expect(FileUtils).to_not have_received(:mkdir_p)
|
462
|
-
end
|
463
|
-
it "does NOT copy the file with FileUtils.cp" do
|
464
|
-
expect(FileUtils).to_not have_received(:cp)
|
465
|
-
end
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
describe ".export_i18n_js with config" do
|
470
|
-
|
471
|
-
let(:export_action) do
|
472
|
-
allow(FileUtils).to receive(:mkdir_p).and_call_original
|
473
|
-
allow(FileUtils).to receive(:cp).and_call_original
|
474
|
-
I18n::JS.export_i18n_js
|
475
|
-
end
|
476
|
-
|
477
|
-
context 'when :export_i18n_js set in config' do
|
478
|
-
before { set_config "js_export_dir_custom.yml"; export_action }
|
479
|
-
let(:export_i18n_js_dir_path) { temp_path }
|
480
|
-
let(:config_export_path) { "tmp/i18n-js/foo" }
|
481
|
-
|
482
|
-
it "does create the folder before copying" do
|
483
|
-
expect(FileUtils).to have_received(:mkdir_p).with(config_export_path).once
|
484
|
-
end
|
485
|
-
it "does copy the file with FileUtils.cp" do
|
486
|
-
expect(FileUtils).to have_received(:cp).once
|
487
|
-
end
|
488
|
-
it "exports the file" do
|
489
|
-
expect(File).to be_file(File.join(config_export_path, "i18n.js"))
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
|
-
context 'when .export_i18n_js_dir_path is set to false' do
|
494
|
-
before { set_config "js_export_dir_none.yml"; export_action }
|
495
|
-
|
496
|
-
it "does NOT create the folder before copying" do
|
497
|
-
expect(FileUtils).to_not have_received(:mkdir_p)
|
498
|
-
end
|
499
|
-
|
500
|
-
it "does NOT copy the file with FileUtils.cp" do
|
501
|
-
expect(FileUtils).to_not have_received(:cp)
|
502
|
-
end
|
503
|
-
end
|
504
|
-
end
|
505
|
-
|
506
|
-
describe '.export_i18n_js_dir_path' do
|
507
|
-
let(:default_path) { I18n::JS::DEFAULT_EXPORT_DIR_PATH }
|
508
|
-
let(:new_path) { File.join("tmp", default_path) }
|
509
|
-
after { described_class.send(:remove_instance_variable, :@export_i18n_js_dir_path) }
|
510
|
-
|
511
|
-
subject { described_class.export_i18n_js_dir_path }
|
512
|
-
|
513
|
-
context "when it is not set" do
|
514
|
-
it { should eq default_path }
|
515
|
-
end
|
516
|
-
context "when it is set to another path already" do
|
517
|
-
before { described_class.export_i18n_js_dir_path = new_path }
|
518
|
-
|
519
|
-
it { should eq new_path }
|
520
|
-
end
|
521
|
-
context "when it is set to nil already" do
|
522
|
-
before { described_class.export_i18n_js_dir_path = nil }
|
523
|
-
|
524
|
-
it { should eq :none }
|
525
|
-
end
|
526
|
-
end
|
527
|
-
end
|
528
|
-
|
529
|
-
describe "translation key sorting" do
|
530
|
-
|
531
|
-
describe ".sort_translation_keys?" do
|
532
|
-
after { described_class.send(:remove_instance_variable, :@sort_translation_keys) }
|
533
|
-
subject { described_class.sort_translation_keys? }
|
534
|
-
|
535
|
-
|
536
|
-
context "set with config" do
|
537
|
-
|
538
|
-
context 'when :sort_translation_keys is not set in config' do
|
539
|
-
before :each do
|
540
|
-
set_config "default.yml"
|
541
|
-
end
|
542
|
-
|
543
|
-
it { should eq true }
|
544
|
-
end
|
545
|
-
|
546
|
-
context 'when :sort_translation_keys set to true in config' do
|
547
|
-
before :each do
|
548
|
-
set_config "js_sort_translation_keys_true.yml"
|
549
|
-
end
|
550
|
-
|
551
|
-
it { should eq true }
|
552
|
-
end
|
553
|
-
|
554
|
-
context 'when :sort_translation_keys set to false in config' do
|
555
|
-
before :each do
|
556
|
-
set_config "js_sort_translation_keys_false.yml"
|
557
|
-
end
|
558
|
-
|
559
|
-
it { should eq false }
|
560
|
-
end
|
561
|
-
end
|
562
|
-
|
563
|
-
context 'set by .sort_translation_keys' do
|
564
|
-
|
565
|
-
context "when it is not set" do
|
566
|
-
it { should eq true }
|
567
|
-
end
|
568
|
-
|
569
|
-
context "when it is set to true" do
|
570
|
-
before { described_class.sort_translation_keys = true }
|
571
|
-
|
572
|
-
it { should eq true }
|
573
|
-
end
|
574
|
-
|
575
|
-
context "when it is set to false" do
|
576
|
-
before { described_class.sort_translation_keys = false }
|
577
|
-
|
578
|
-
it { should eq false }
|
579
|
-
end
|
580
|
-
end
|
581
|
-
end
|
582
|
-
|
583
|
-
context "exporting" do
|
584
|
-
subject do
|
585
|
-
I18n::JS.export
|
586
|
-
file_should_exist "en.js"
|
587
|
-
File.read(File.join(I18n::JS.export_i18n_js_dir_path, "en.js"))
|
588
|
-
end
|
589
|
-
|
590
|
-
before do
|
591
|
-
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
592
|
-
end
|
593
|
-
|
594
|
-
context 'sort_translation_keys is true' do
|
595
|
-
before :each do
|
596
|
-
set_config "js_sort_translation_keys_true.yml"
|
597
|
-
end
|
598
|
-
|
599
|
-
it "exports with the keys sorted" do
|
600
|
-
expect(subject).to eq <<EOS
|
601
|
-
I18n.translations || (I18n.translations = {});
|
602
|
-
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","merge_plurals":{"one":"Apple","other":"Apples"},"merge_plurals_with_no_overrides":{"one":"Apple","other":"Apples","zero":"No Apple"},"merge_plurals_with_partial_overrides":{"one":"Cat","other":"Cats"},"null_test":"fallback for null","number":{"currency":{"format":{"delimiter":",","format":"%u%n","precision":2,"separator":".","unit":"$"}},"format":{"delimiter":",","precision":3,"separator":"."},"human":{"decimal_units":{"units":{"million":"Million"}}}},"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"}});
|
603
|
-
EOS
|
604
|
-
end
|
605
|
-
end
|
606
|
-
end
|
607
|
-
end
|
608
|
-
|
609
|
-
describe "js_extend option" do
|
610
|
-
before do
|
611
|
-
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
612
|
-
end
|
613
|
-
|
614
|
-
# https://github.com/fnando/i18n-js/issues/553
|
615
|
-
describe "millions" do
|
616
|
-
before do
|
617
|
-
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
618
|
-
end
|
619
|
-
|
620
|
-
subject do
|
621
|
-
File.read(File.join(I18n::JS.export_i18n_js_dir_path, "millions.js"))
|
622
|
-
end
|
623
|
-
|
624
|
-
it "should correctly export millions" do
|
625
|
-
set_config "millions.yml"
|
626
|
-
I18n::JS.export
|
627
|
-
file_should_exist "millions.js"
|
628
|
-
|
629
|
-
expect(subject).to eq <<EOS
|
630
|
-
I18n.translations || (I18n.translations = {});
|
631
|
-
I18n.translations[\"de\"] = I18n.extend((I18n.translations[\"de\"] || {}), {\"number\":{\"human\":{\"decimal_units\":{\"units\":{\"million\":\"Million\"}}}}});
|
632
|
-
I18n.translations[\"en\"] = I18n.extend((I18n.translations[\"en\"] || {}), {\"number\":{\"human\":{\"decimal_units\":{\"units\":{\"million\":\"Million\"}}}}});
|
633
|
-
I18n.translations[\"en-US\"] = I18n.extend((I18n.translations[\"en-US\"] || {}), {\"number\":{\"human\":{\"decimal_units\":{\"units\":{\"million\":\"Million\"}}}}});
|
634
|
-
I18n.translations[\"es\"] = I18n.extend((I18n.translations[\"es\"] || {}), {\"number\":{\"human\":{\"decimal_units\":{\"units\":{\"million\":{\"one\":\"millón\",\"other\":\"millones\"}}}}}});
|
635
|
-
I18n.translations[\"fr\"] = I18n.extend((I18n.translations[\"fr\"] || {}), {\"number\":{\"human\":{\"decimal_units\":{\"units\":{\"million\":\"Million\"}}}}});
|
636
|
-
I18n.translations[\"ja\"] = I18n.extend((I18n.translations[\"ja\"] || {}), {\"number\":{\"human\":{\"decimal_units\":{\"units\":{\"million\":\"Million\"}}}}});
|
637
|
-
I18n.translations[\"ru\"] = I18n.extend((I18n.translations[\"ru\"] || {}), {\"number\":{\"human\":{\"decimal_units\":{\"units\":{\"million\":\"Million\"}}}}});
|
638
|
-
EOS
|
639
|
-
end
|
640
|
-
end
|
641
|
-
|
642
|
-
it "exports with js_extend option at parent level" do
|
643
|
-
set_config "js_extend_parent.yml"
|
644
|
-
I18n::JS.export
|
645
|
-
|
646
|
-
file_should_exist "js_extend_parent.js"
|
647
|
-
|
648
|
-
output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "js_extend_parent.js"))
|
649
|
-
expect(output).to eq(<<EOS
|
650
|
-
I18n.translations || (I18n.translations = {});
|
651
|
-
I18n.translations[\"en\"] = {\"date\":{\"formats\":{\"default\":\"%Y-%m-%d\",\"long\":\"%B %d, %Y\",\"short\":\"%b %d\"}}};
|
652
|
-
I18n.translations[\"fr\"] = {\"date\":{\"formats\":{\"default\":\"%d/%m/%Y\",\"long\":\"%e %B %Y\",\"long_ordinal\":\"%e %B %Y\",\"only_day\":\"%e\",\"short\":\"%e %b\"}}};
|
653
|
-
EOS
|
654
|
-
)
|
655
|
-
end
|
656
|
-
|
657
|
-
it "exports with js_extend option at segment level" do
|
658
|
-
set_config "js_extend_segment.yml"
|
659
|
-
I18n::JS.export
|
660
|
-
|
661
|
-
file_should_exist "js_extend_segment.js"
|
662
|
-
|
663
|
-
output = File.read(File.join(I18n::JS.export_i18n_js_dir_path, "js_extend_segment.js"))
|
664
|
-
expect(output).to eq(<<EOS
|
665
|
-
I18n.translations || (I18n.translations = {});
|
666
|
-
I18n.translations[\"en\"] = {\"date\":{\"formats\":{\"default\":\"%Y-%m-%d\",\"long\":\"%B %d, %Y\",\"short\":\"%b %d\"}}};
|
667
|
-
I18n.translations[\"fr\"] = {\"date\":{\"formats\":{\"default\":\"%d/%m/%Y\",\"long\":\"%e %B %Y\",\"long_ordinal\":\"%e %B %Y\",\"only_day\":\"%e\",\"short\":\"%e %b\"}}};
|
668
|
-
EOS
|
669
|
-
)
|
670
|
-
end
|
671
|
-
end
|
672
|
-
|
673
|
-
describe "merging plural keys" do
|
674
|
-
before do
|
675
|
-
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
676
|
-
end
|
677
|
-
|
678
|
-
subject do
|
679
|
-
File.read(File.join(I18n::JS.export_i18n_js_dir_path, "merge_plurals.js"))
|
680
|
-
end
|
681
|
-
|
682
|
-
it "should correctly merge the plural keys" do
|
683
|
-
set_config "merge_plurals.yml"
|
684
|
-
I18n::JS.export
|
685
|
-
file_should_exist "merge_plurals.js"
|
686
|
-
|
687
|
-
expect(subject).to eq <<EOS
|
688
|
-
I18n.translations || (I18n.translations = {});
|
689
|
-
I18n.translations[\"en\"] = I18n.extend((I18n.translations[\"en\"] || {}), {\"merge_plurals\":{\"one\":\"Apple\",\"other\":\"Apples\"}});\nI18n.translations[\"fr\"] = I18n.extend((I18n.translations[\"fr\"] || {}), {\"merge_plurals\":{\"one\":\"Pomme\",\"other\":\"Pommes\",\"zero\":\"Pomme\"}});
|
690
|
-
EOS
|
691
|
-
end
|
692
|
-
end
|
693
|
-
|
694
|
-
describe "merging plural keys, with fallbacks" do
|
695
|
-
before do
|
696
|
-
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
697
|
-
end
|
698
|
-
|
699
|
-
subject do
|
700
|
-
File.read(File.join(I18n::JS.export_i18n_js_dir_path, "merge_plurals_with_no_overrides.js"))
|
701
|
-
end
|
702
|
-
|
703
|
-
it "should correctly merge the plural keys" do
|
704
|
-
set_config "merge_plurals_with_no_overrides.yml"
|
705
|
-
I18n::JS.export
|
706
|
-
file_should_exist "merge_plurals_with_no_overrides.js"
|
707
|
-
|
708
|
-
expect(subject).to eq <<EOS
|
709
|
-
I18n.translations || (I18n.translations = {});
|
710
|
-
I18n.translations[\"de\"] = I18n.extend((I18n.translations[\"de\"] || {}), {\"merge_plurals_with_no_overrides\":{\"one\":\"Apple\",\"other\":\"Apples\",\"zero\":\"No Apple\"}});
|
711
|
-
I18n.translations[\"en\"] = I18n.extend((I18n.translations[\"en\"] || {}), {\"merge_plurals_with_no_overrides\":{\"one\":\"Apple\",\"other\":\"Apples\",\"zero\":\"No Apple\"}});
|
712
|
-
I18n.translations[\"en-US\"] = I18n.extend((I18n.translations[\"en-US\"] || {}), {\"merge_plurals_with_no_overrides\":{\"one\":\"Apple\",\"other\":\"Apples\",\"zero\":\"No Apple\"}});
|
713
|
-
I18n.translations[\"es\"] = I18n.extend((I18n.translations[\"es\"] || {}), {\"merge_plurals_with_no_overrides\":{\"one\":\"Apple\",\"other\":\"Apples\",\"zero\":\"No Apple\"}});
|
714
|
-
I18n.translations[\"fr\"] = I18n.extend((I18n.translations[\"fr\"] || {}), {\"merge_plurals_with_no_overrides\":{\"one\":\"Apple\",\"other\":\"Apples\",\"zero\":\"No Apple\"}});
|
715
|
-
I18n.translations[\"ja\"] = I18n.extend((I18n.translations[\"ja\"] || {}), {\"merge_plurals_with_no_overrides\":{\"one\":\"Apple\",\"other\":\"Apples\",\"zero\":\"No Apple\"}});
|
716
|
-
I18n.translations[\"ru\"] = I18n.extend((I18n.translations[\"ru\"] || {}), {\"merge_plurals_with_no_overrides\":{\"few\":\"кошек\",\"many\":\"кошка\",\"one\":\"кот\",\"other\":\"кошек\"}});
|
717
|
-
EOS
|
718
|
-
end
|
719
|
-
end
|
720
|
-
|
721
|
-
# see discussion @ https://github.com/fnando/i18n-js/pull/551#issuecomment-543205767
|
722
|
-
describe "merging plural keys, with fallbacks, with partial overrides" do
|
723
|
-
before do
|
724
|
-
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
725
|
-
end
|
726
|
-
|
727
|
-
subject do
|
728
|
-
File.read(File.join(I18n::JS.export_i18n_js_dir_path, "merge_plurals_with_partial_overrides.js"))
|
729
|
-
end
|
730
|
-
|
731
|
-
it "should correctly merge the plural keys" do
|
732
|
-
set_config "merge_plurals_with_partial_overrides.yml"
|
733
|
-
I18n::JS.export
|
734
|
-
file_should_exist "merge_plurals_with_partial_overrides.js"
|
735
|
-
|
736
|
-
expect(subject).to eq <<EOS
|
737
|
-
I18n.translations || (I18n.translations = {});
|
738
|
-
I18n.translations[\"de\"] = I18n.extend((I18n.translations[\"de\"] || {}), {\"merge_plurals_with_partial_overrides\":{\"one\":\"Cat\",\"other\":\"Cats\"}});
|
739
|
-
I18n.translations[\"en\"] = I18n.extend((I18n.translations[\"en\"] || {}), {\"merge_plurals_with_partial_overrides\":{\"one\":\"Cat\",\"other\":\"Cats\"}});
|
740
|
-
I18n.translations[\"en-US\"] = I18n.extend((I18n.translations[\"en-US\"] || {}), {\"merge_plurals_with_partial_overrides\":{\"few\":null,\"many\":null,\"one\":\"Cat\",\"other\":\"Cats\"}});
|
741
|
-
I18n.translations[\"es\"] = I18n.extend((I18n.translations[\"es\"] || {}), {\"merge_plurals_with_partial_overrides\":{\"one\":\"Cat\",\"other\":\"Cats\"}});
|
742
|
-
I18n.translations[\"fr\"] = I18n.extend((I18n.translations[\"fr\"] || {}), {\"merge_plurals_with_partial_overrides\":{\"one\":\"Cat\",\"other\":\"Cats\"}});
|
743
|
-
I18n.translations[\"ja\"] = I18n.extend((I18n.translations[\"ja\"] || {}), {\"merge_plurals_with_partial_overrides\":{\"one\":\"Cat\",\"other\":\"Cats\"}});
|
744
|
-
I18n.translations[\"ru\"] = I18n.extend((I18n.translations[\"ru\"] || {}), {\"merge_plurals_with_partial_overrides\":{\"one\":\"Cat\",\"other\":\"Cats\"}});
|
745
|
-
EOS
|
746
|
-
end
|
747
|
-
end
|
748
|
-
end
|