i18n-js 3.0.0.rc9 → 3.0.0.rc10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -4
- data/CHANGELOG.md +30 -2
- data/README.md +26 -8
- data/app/assets/javascripts/i18n.js +2 -1
- data/i18n-js.gemspec +1 -1
- data/lib/i18n/js.rb +30 -30
- data/lib/i18n/js/middleware.rb +2 -1
- data/lib/i18n/js/segment.rb +23 -6
- data/lib/i18n/js/utils.rb +20 -3
- data/lib/i18n/js/version.rb +1 -1
- data/lib/rails/generators/i18n/js/config/templates/i18n-js.yml +1 -2
- data/spec/fixtures/custom_path.yml +2 -1
- data/spec/fixtures/default.yml +2 -1
- data/spec/fixtures/erb.yml +2 -1
- data/spec/fixtures/except_condition.yml +2 -0
- data/spec/fixtures/js_export_dir_custom.yml +1 -0
- data/spec/fixtures/js_export_dir_none.yml +1 -1
- data/spec/fixtures/js_file_per_locale.yml +5 -1
- data/spec/fixtures/js_file_with_namespace_and_pretty_print.yml +2 -0
- data/spec/fixtures/js_sort_translation_keys_false.yml +6 -0
- data/spec/fixtures/js_sort_translation_keys_true.yml +6 -0
- data/spec/fixtures/multiple_conditions.yml +2 -0
- data/spec/fixtures/multiple_conditions_per_locale.yml +4 -2
- data/spec/fixtures/multiple_files.yml +2 -1
- data/spec/fixtures/no_scope.yml +2 -1
- data/spec/fixtures/simple_scope.yml +2 -1
- data/spec/i18n_js_spec.rb +198 -35
- data/spec/segment_spec.rb +43 -5
- data/spec/utils_spec.rb +37 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b54325d787e4471fabea3b33981f0b4ada0e660
|
4
|
+
data.tar.gz: 94c1e7e0ef79bfb984f9725453c40aef5290d3e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63dedf3a5d8d7f144773e922ba113d5586c1eb608f5745608a363dd3b5604acd38863a4978bd1bce091bfc6b033ad85bf59915985b790d96beafa7681d739833
|
7
|
+
data.tar.gz: 30ee692d8f29ad98fc71014e119d41ae82490cddbd1a21a808cff54c97c25bf0c3abda5102555684c131a191e302eb93cd7fe98559dbc8aee2c8df9912d5fd79
|
data/.travis.yml
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
# Send builds to container-based infrastructure
|
2
|
+
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
3
|
+
sudo: false
|
1
4
|
language: ruby
|
5
|
+
cache:
|
6
|
+
- bundler
|
2
7
|
rvm:
|
3
8
|
- 2.0
|
4
9
|
- 2.1
|
5
10
|
- 2.2
|
6
11
|
- ruby-head
|
7
|
-
|
8
|
-
before_install: # Need to install npm to test js
|
9
|
-
- sudo apt-get update
|
10
|
-
- sudo apt-get install npm
|
12
|
+
before_install: # Need to install something extra to test JS
|
11
13
|
- npm install jasmine-node@1.14.2
|
12
14
|
gemfile:
|
13
15
|
- gemfiles/i18n_0_6.gemfile
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,32 @@
|
|
1
1
|
|
2
2
|
## Unreleased
|
3
3
|
|
4
|
+
### breaking changes
|
5
|
+
|
6
|
+
### enhancements
|
7
|
+
|
8
|
+
### bug fixes
|
9
|
+
|
10
|
+
|
11
|
+
## 3.0.0.rc10
|
12
|
+
|
13
|
+
### breaking changes
|
14
|
+
|
15
|
+
- [Ruby] In `config/i18n-js.yml`, if you are using `%{locale}` in your filename and are referencing specific translations keys, please add `*.` to the beginning of those keys. ([#320](https://github.com/fnando/i18n-js/pull/320))
|
16
|
+
- [Ruby] The `:except` option to exclude certain phrases now (only) accepts the same patterns the `:only` option accepts
|
17
|
+
|
18
|
+
### enhancements
|
19
|
+
|
20
|
+
- [Ruby] Make handling of per-locale and not-per-locale exporting to be more consistent ([#320](https://github.com/fnando/i18n-js/pull/320))
|
21
|
+
- [Ruby] Add option `sort_translation_keys` to sort translation keys alphabetically ([#318](https://github.com/fnando/i18n-js/pull/318))
|
22
|
+
|
23
|
+
### bug fixes
|
24
|
+
|
25
|
+
- [Ruby] Fix fallback logic to work with not-per-locale files ([#320](https://github.com/fnando/i18n-js/pull/320))
|
26
|
+
|
27
|
+
|
28
|
+
## 3.0.0.rc9
|
29
|
+
|
4
30
|
### enhancements
|
5
31
|
|
6
32
|
- [JS] Force currency number sign to be at first place using `sign_first` option, default to `true`
|
@@ -10,13 +36,15 @@
|
|
10
36
|
- [Ruby] Add `:except` option to exclude certain phrases or groups of phrases from the
|
11
37
|
outputted translations ([#312](https://github.com/fnando/i18n-js/pull/312))
|
12
38
|
- [JS] You can now set `I18n.missingBehavior='guess'` to have the scope string output as text instead of of the
|
13
|
-
"[missing `scope`]" message when no translation is available.
|
14
|
-
|
39
|
+
"[missing `scope`]" message when no translation is available.
|
40
|
+
Combined that with `I18n.missingTranslationPrefix='SOMETHING'` and you can
|
15
41
|
still identify those missing strings.
|
42
|
+
([#304](https://github.com/fnando/i18n-js/pull/304))
|
16
43
|
|
17
44
|
### bug fixes
|
18
45
|
|
19
46
|
- [JS] Fix missing translation message when scope is passed in options
|
47
|
+
- [Ruby] Fix save cache directory verification when path is a symbolic link ([#329](https://github.com/fnando/i18n-js/pull/329))
|
20
48
|
|
21
49
|
|
22
50
|
## 3.0.0.rc8
|
data/README.md
CHANGED
@@ -38,6 +38,8 @@ then you must add the following line to your `app/assets/javascripts/application
|
|
38
38
|
// This is optional (in case you have `I18n is not defined` error)
|
39
39
|
// If you want to put this line, you must put it BEFORE `i18n/translations`
|
40
40
|
//= require i18n
|
41
|
+
// Some people even need to add the extension to make it work, see https://github.com/fnando/i18n-js/issues/283
|
42
|
+
//= require i18n.js
|
41
43
|
//
|
42
44
|
// This is a must
|
43
45
|
//= require i18n/translations
|
@@ -97,7 +99,7 @@ translations:
|
|
97
99
|
- file: "public/javascripts/i18n/%{locale}.js"
|
98
100
|
only: '*'
|
99
101
|
- file: "public/javascripts/frontend/i18n/%{locale}.js"
|
100
|
-
only: ['frontend', 'users']
|
102
|
+
only: ['*.frontend', '*.users.*']
|
101
103
|
```
|
102
104
|
|
103
105
|
You can also include ERB in your config file.
|
@@ -116,7 +118,7 @@ keys listed in `except` configuration param:
|
|
116
118
|
|
117
119
|
```yaml
|
118
120
|
translations:
|
119
|
-
- except: ['active_admin', 'ransack']
|
121
|
+
- except: ['*.active_admin', '*.ransack', '*.activerecord.errors']
|
120
122
|
```
|
121
123
|
|
122
124
|
|
@@ -134,16 +136,25 @@ translations:
|
|
134
136
|
- Any `String`: considered as a relative path for a folder to `Rails.root` and export `i18n.js` to that folder for `rake i18n:js:export`
|
135
137
|
- Any non-`String` (`nil`, `false`, `:none`, etc): Disable `i18n.js` exporting
|
136
138
|
|
137
|
-
-
|
139
|
+
- `I18n::JS.sort_translation_keys`
|
140
|
+
Expected Type: `Boolean`
|
141
|
+
Default: `true`
|
142
|
+
Behaviour:
|
143
|
+
- Sets whether or not to deep sort all translation keys in order to generate identical output for the same translations
|
144
|
+
- Set to true to ensure identical asset fingerprints for the asset pipeline
|
145
|
+
|
146
|
+
- You may also set `export_i18n_js` and `sort_translation_keys` in your config file, e.g.:
|
138
147
|
|
139
148
|
```yaml
|
140
149
|
export_i18n_js_: false
|
141
150
|
# OR
|
142
151
|
export_i18n_js: "my/path"
|
143
152
|
|
153
|
+
sort_translation_keys: false
|
154
|
+
|
144
155
|
translations:
|
145
156
|
- ...
|
146
|
-
|
157
|
+
```
|
147
158
|
|
148
159
|
To find more examples on how to use the configuration file please refer to the tests.
|
149
160
|
|
@@ -358,15 +369,22 @@ becomes "what is your favorite Christmas present"
|
|
358
369
|
|
359
370
|
In order to still detect untranslated strings, you can
|
360
371
|
i18n.missingTranslationPrefix to something like:
|
361
|
-
|
362
|
-
|
372
|
+
```javascript
|
373
|
+
I18n.missingTranslationPrefix = 'EE: ';
|
374
|
+
```
|
363
375
|
|
364
376
|
And result will be:
|
365
|
-
|
366
|
-
|
377
|
+
```javascript
|
378
|
+
"EE: what is your favorite Christmas present"
|
379
|
+
```
|
367
380
|
|
368
381
|
This will help you doing automated tests against your localisation assets.
|
369
382
|
|
383
|
+
Some people prefer returning `null` for missing translation:
|
384
|
+
```javascript
|
385
|
+
I18n.missingTranslation = function () { return undefined; };
|
386
|
+
```
|
387
|
+
|
370
388
|
Pluralization is possible as well and by default provides English rules:
|
371
389
|
|
372
390
|
I18n.t("inbox.counting", {count: 10}); // You have 10 messages
|
@@ -18,7 +18,8 @@
|
|
18
18
|
module.exports = factory(this);
|
19
19
|
} else if (typeof define === 'function' && define.amd) {
|
20
20
|
// AMD
|
21
|
-
|
21
|
+
var global=this;
|
22
|
+
define('i18n', function(){ return factory(global);});
|
22
23
|
} else {
|
23
24
|
// Browser globals
|
24
25
|
this.I18n = factory(this);
|
data/i18n-js.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
s.add_dependency "i18n", "~> 0.6"
|
22
|
-
s.add_development_dependency "appraisal", "~>
|
22
|
+
s.add_development_dependency "appraisal", "~> 2.0"
|
23
23
|
s.add_development_dependency "activesupport", ">= 3"
|
24
24
|
s.add_development_dependency "rspec", "~> 3.0"
|
25
25
|
s.add_development_dependency "rake"
|
data/lib/i18n/js.rb
CHANGED
@@ -34,17 +34,6 @@ module I18n
|
|
34
34
|
translation_segments.each(&:save!)
|
35
35
|
end
|
36
36
|
|
37
|
-
def self.segments_per_locale(pattern, scope, exceptions, options)
|
38
|
-
I18n.available_locales.each_with_object([]) do |locale, segments|
|
39
|
-
scope = [scope] unless scope.respond_to?(:each)
|
40
|
-
result = scoped_translations(scope.collect{|s| "#{locale}.#{s}"}, exceptions)
|
41
|
-
merge_with_fallbacks!(result, locale, scope, exceptions) if use_fallbacks?
|
42
|
-
|
43
|
-
next if result.empty?
|
44
|
-
segments << Segment.new(::I18n.interpolate(pattern, {:locale => locale}), result, options)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
37
|
def self.segment_for_scope(scope, exceptions)
|
49
38
|
if scope == "*"
|
50
39
|
exclude(translations, exceptions)
|
@@ -61,23 +50,34 @@ module I18n
|
|
61
50
|
|
62
51
|
segment_options = options.slice(:namespace, :pretty_print)
|
63
52
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
53
|
+
result = segment_for_scope(only, exceptions)
|
54
|
+
|
55
|
+
merge_with_fallbacks!(result) if fallbacks
|
56
|
+
|
57
|
+
segments << Segment.new(file, result, segment_options) unless result.empty?
|
70
58
|
|
71
59
|
segments
|
72
60
|
end
|
73
61
|
end
|
74
62
|
|
63
|
+
# deep_merge! given result with result for fallback locale
|
64
|
+
def self.merge_with_fallbacks!(result)
|
65
|
+
I18n.available_locales.each do |locale|
|
66
|
+
fallback_locales = FallbackLocales.new(fallbacks, locale)
|
67
|
+
fallback_locales.each do |fallback_locale|
|
68
|
+
result[locale] = Utils.deep_merge(result[fallback_locale], result[locale] || {})
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
75
73
|
def self.filtered_translations
|
76
|
-
{}.tap do |result|
|
74
|
+
translations = {}.tap do |result|
|
77
75
|
translation_segments.each do |segment|
|
78
76
|
Utils.deep_merge!(result, segment.translations)
|
79
77
|
end
|
80
78
|
end
|
79
|
+
return Utils.deep_key_sort(translations) if I18n::JS.sort_translation_keys?
|
80
|
+
translations
|
81
81
|
end
|
82
82
|
|
83
83
|
def self.translation_segments
|
@@ -109,9 +109,9 @@ module I18n
|
|
109
109
|
|
110
110
|
[scopes].flatten.each do |scope|
|
111
111
|
translations_without_exceptions = exclude(translations, exceptions)
|
112
|
-
filtered_translations = filter(translations_without_exceptions, scope)
|
112
|
+
filtered_translations = filter(translations_without_exceptions, scope) || {}
|
113
113
|
|
114
|
-
Utils.deep_merge!
|
114
|
+
Utils.deep_merge!(result, filtered_translations)
|
115
115
|
end
|
116
116
|
|
117
117
|
result
|
@@ -122,8 +122,9 @@ module I18n
|
|
122
122
|
return translations if exceptions.empty?
|
123
123
|
|
124
124
|
exceptions.inject(translations) do |memo, exception|
|
125
|
-
|
126
|
-
|
125
|
+
exception_scopes = exception.to_s.split(".")
|
126
|
+
Utils.deep_reject(memo) do |key, value, scopes|
|
127
|
+
Utils.scopes_match?(scopes, exception_scopes)
|
127
128
|
end
|
128
129
|
end
|
129
130
|
end
|
@@ -166,15 +167,14 @@ module I18n
|
|
166
167
|
end
|
167
168
|
end
|
168
169
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
170
|
+
def self.sort_translation_keys?
|
171
|
+
@sort_translation_keys ||= (config[:sort_translation_keys]) if config.has_key?(:sort_translation_keys)
|
172
|
+
@sort_translation_keys = true if @sort_translation_keys.nil?
|
173
|
+
@sort_translation_keys
|
174
|
+
end
|
173
175
|
|
174
|
-
|
175
|
-
|
176
|
-
result[locale] = Utils.deep_merge(fallback_result[fallback_locale], result[locale])
|
177
|
-
end
|
176
|
+
def self.sort_translation_keys=(value)
|
177
|
+
@sort_translation_keys = !!value
|
178
178
|
end
|
179
179
|
|
180
180
|
### Export i18n.js
|
data/lib/i18n/js/middleware.rb
CHANGED
data/lib/i18n/js/segment.rb
CHANGED
@@ -5,6 +5,8 @@ module I18n
|
|
5
5
|
class Segment
|
6
6
|
attr_accessor :file, :translations, :namespace, :pretty_print
|
7
7
|
|
8
|
+
LOCALE_INTERPOLATOR = /%\{locale\}/
|
9
|
+
|
8
10
|
def initialize(file, translations, options = {})
|
9
11
|
@file = file
|
10
12
|
@translations = translations
|
@@ -14,18 +16,28 @@ module I18n
|
|
14
16
|
|
15
17
|
# Saves JSON file containing translations
|
16
18
|
def save!
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
f << %(#{self.namespace}.translations || (#{self.namespace}.translations = {});\n)
|
21
|
-
self.translations.each do |locale, translations|
|
22
|
-
f << %(#{self.namespace}.translations["#{locale}"] = #{print_json(translations)};\n);
|
19
|
+
if self.file =~ LOCALE_INTERPOLATOR
|
20
|
+
I18n.available_locales.each do |locale|
|
21
|
+
write_file(file_for_locale(locale), self.translations.slice(locale))
|
23
22
|
end
|
23
|
+
else
|
24
|
+
write_file
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
28
|
protected
|
28
29
|
|
30
|
+
def write_file(_file = self.file, _translations = self.translations)
|
31
|
+
FileUtils.mkdir_p File.dirname(_file)
|
32
|
+
File.open(_file, "w+") do |f|
|
33
|
+
f << %(#{self.namespace}.translations || (#{self.namespace}.translations = {});\n)
|
34
|
+
_translations.each do |locale, translations_for_locale|
|
35
|
+
output_translations = I18n::JS.sort_translation_keys? ? Utils.deep_key_sort(translations_for_locale) : translations_for_locale
|
36
|
+
f << %(#{self.namespace}.translations["#{locale}"] = #{print_json(output_translations)};\n);
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
29
41
|
# Outputs pretty or ugly JSON depending on :pretty_print option
|
30
42
|
def print_json(translations)
|
31
43
|
if pretty_print
|
@@ -34,6 +46,11 @@ module I18n
|
|
34
46
|
translations.to_json
|
35
47
|
end
|
36
48
|
end
|
49
|
+
|
50
|
+
# interpolates filename
|
51
|
+
def file_for_locale(locale)
|
52
|
+
self.file.gsub(LOCALE_INTERPOLATOR, locale.to_s)
|
53
|
+
end
|
37
54
|
end
|
38
55
|
end
|
39
56
|
end
|
data/lib/i18n/js/utils.rb
CHANGED
@@ -22,13 +22,30 @@ module I18n
|
|
22
22
|
target_hash.merge!(hash, &MERGER)
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.deep_reject(hash, &block)
|
25
|
+
def self.deep_reject(hash, scopes = [], &block)
|
26
26
|
hash.each_with_object({}) do |(k, v), memo|
|
27
|
-
unless block.call(k, v)
|
28
|
-
memo[k] = v.kind_of?(Hash) ? deep_reject(v, &block) : v
|
27
|
+
unless block.call(k, v, scopes + [k.to_s])
|
28
|
+
memo[k] = v.kind_of?(Hash) ? deep_reject(v, scopes + [k.to_s], &block) : v
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
def self.scopes_match?(scopes1, scopes2)
|
34
|
+
if scopes1.length == scopes2.length
|
35
|
+
[scopes1, scopes2].transpose.all? do |scope1, scope2|
|
36
|
+
scope1.to_s == '*' || scope2.to_s == '*' || scope1.to_s == scope2.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.deep_key_sort(hash)
|
42
|
+
# Avoid things like `true` or `1` from YAML which causes error
|
43
|
+
hash.keys.sort {|a, b| a.to_s <=> b.to_s}.
|
44
|
+
each_with_object({}) do |key, seed|
|
45
|
+
value = hash[key]
|
46
|
+
seed[key] = value.is_a?(Hash) ? deep_key_sort(value) : value
|
47
|
+
end
|
48
|
+
end
|
32
49
|
end
|
33
50
|
end
|
34
51
|
end
|
data/lib/i18n/js/version.rb
CHANGED
@@ -9,8 +9,7 @@
|
|
9
9
|
# so.
|
10
10
|
#
|
11
11
|
# For more informations about the export options with this file, please
|
12
|
-
# refer to the
|
13
|
-
# https://github.com/fnando/i18n-js#export-configuration
|
12
|
+
# refer to the README
|
14
13
|
#
|
15
14
|
#
|
16
15
|
# If you're going to use the Rails 3.1 asset pipeline, change
|
data/spec/fixtures/default.yml
CHANGED
data/spec/fixtures/erb.yml
CHANGED
data/spec/fixtures/no_scope.yml
CHANGED
data/spec/i18n_js_spec.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe I18n::JS do
|
4
|
+
|
4
5
|
describe '.config_file_path' do
|
5
6
|
let(:default_path) { I18n::JS::DEFAULT_CONFIG_PATH }
|
6
7
|
let(:new_path) { File.join("tmp", default_path) }
|
@@ -62,6 +63,19 @@ describe I18n::JS do
|
|
62
63
|
|
63
64
|
file_should_exist "en.js"
|
64
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"] = {"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"] = {"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
|
+
)
|
65
79
|
end
|
66
80
|
|
67
81
|
it "exports with multiple conditions" do
|
@@ -77,13 +91,22 @@ describe I18n::JS do
|
|
77
91
|
set_config "multiple_conditions_per_locale.yml"
|
78
92
|
|
79
93
|
result = I18n::JS.translation_segments
|
80
|
-
result.map(&:file).should eql(["tmp/i18n-js/bits.
|
94
|
+
result.map(&:file).should eql(["tmp/i18n-js/bits.%{locale}.js"])
|
81
95
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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"] = {"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"] = {"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
|
+
)
|
87
110
|
end
|
88
111
|
|
89
112
|
it "exports with :except condition" do
|
@@ -138,69 +161,126 @@ describe I18n::JS do
|
|
138
161
|
result[:en][:admin][:edit][:title].should eql("Edit")
|
139
162
|
result[:fr][:admin][:edit][:title].should eql("Editer")
|
140
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
|
141
208
|
end
|
142
209
|
|
143
210
|
context "exceptions" do
|
144
|
-
it "does not include
|
145
|
-
result = I18n::JS.scoped_translations("*", ['admin'])
|
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
|
146
215
|
|
147
216
|
result[:en][:admin].should be_nil
|
148
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
|
149
222
|
end
|
150
223
|
|
151
|
-
it "does not include
|
152
|
-
result = I18n::JS.scoped_translations("
|
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
|
153
230
|
|
154
|
-
result[:
|
155
|
-
result[:
|
231
|
+
result[:fr][:admin].should be_nil
|
232
|
+
result[:fr][:number][:currency].should be_nil
|
156
233
|
|
157
|
-
result[:fr][:
|
158
|
-
result[:fr][:admin][:show].should be_empty
|
159
|
-
result[:fr][:admin][:edit].should be_empty
|
234
|
+
result[:fr][:time][:am].should be_a(String)
|
160
235
|
end
|
161
236
|
|
162
|
-
it "does
|
163
|
-
result = I18n::JS.scoped_translations("
|
237
|
+
it "does exclude absolute scopes listed in the exceptions list" do
|
238
|
+
result = I18n::JS.scoped_translations("*", ['de', 'en.admin', 'fr.number.currency'])
|
164
239
|
|
165
|
-
result[:en].should be_nil
|
166
240
|
result[:de].should be_nil
|
167
|
-
result[:ja].should be_nil
|
168
241
|
|
169
|
-
result[:
|
170
|
-
result[:
|
171
|
-
|
172
|
-
result[:fr][:
|
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'])
|
173
251
|
|
174
|
-
result[:
|
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)
|
175
258
|
end
|
176
259
|
end
|
177
260
|
|
178
261
|
context "fallbacks" do
|
179
262
|
subject do
|
180
|
-
I18n::JS.translation_segments.
|
181
|
-
hash[segment.file] = segment.translations
|
182
|
-
hash
|
183
|
-
end
|
263
|
+
I18n::JS.translation_segments.first.translations
|
184
264
|
end
|
185
265
|
|
186
266
|
it "exports without fallback when disabled" do
|
187
267
|
set_config "js_file_per_locale_without_fallbacks.yml"
|
188
|
-
subject[
|
268
|
+
subject[:fr][:fallback_test].should eql(nil)
|
189
269
|
end
|
190
270
|
|
191
271
|
it "exports with default_locale as fallback when enabled" do
|
192
272
|
set_config "js_file_per_locale_with_fallbacks_enabled.yml"
|
193
|
-
subject[
|
273
|
+
subject[:fr][:fallback_test].should eql("Success")
|
194
274
|
end
|
195
275
|
|
196
276
|
it "exports with default_locale as fallback when enabled with :default_locale" do
|
197
277
|
set_config "js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml"
|
198
|
-
subject[
|
278
|
+
subject[:fr][:fallback_test].should eql("Success")
|
199
279
|
end
|
200
280
|
|
201
281
|
it "exports with given locale as fallback" do
|
202
282
|
set_config "js_file_per_locale_with_fallbacks_as_locale.yml"
|
203
|
-
subject[
|
283
|
+
subject[:fr][:fallback_test].should eql("Erfolg")
|
204
284
|
end
|
205
285
|
|
206
286
|
context "with I18n::Fallbacks enabled" do
|
@@ -215,17 +295,17 @@ describe I18n::JS do
|
|
215
295
|
|
216
296
|
it "exports with defined locale as fallback when enabled" do
|
217
297
|
set_config "js_file_per_locale_with_fallbacks_enabled.yml"
|
218
|
-
subject[
|
298
|
+
subject[:fr][:fallback_test].should eql("Erfolg")
|
219
299
|
end
|
220
300
|
|
221
301
|
it "exports with defined locale as fallback when enabled with :default_locale" do
|
222
302
|
set_config "js_file_per_locale_with_fallbacks_as_default_locale_symbol.yml"
|
223
|
-
subject[
|
303
|
+
subject[:fr][:fallback_test].should eql("Success")
|
224
304
|
end
|
225
305
|
|
226
306
|
it "exports with Fallbacks as Hash" do
|
227
307
|
set_config "js_file_per_locale_with_fallbacks_as_hash.yml"
|
228
|
-
subject[
|
308
|
+
subject[:fr][:fallback_test].should eql("Erfolg")
|
229
309
|
end
|
230
310
|
end
|
231
311
|
end
|
@@ -263,6 +343,7 @@ EOS
|
|
263
343
|
end
|
264
344
|
|
265
345
|
context "I18n.available_locales" do
|
346
|
+
|
266
347
|
context "when I18n.available_locales is not set" do
|
267
348
|
it "should allow all locales" do
|
268
349
|
result = I18n::JS.scoped_translations("*.admin.*.title")
|
@@ -401,4 +482,86 @@ EOS
|
|
401
482
|
end
|
402
483
|
end
|
403
484
|
end
|
485
|
+
|
486
|
+
describe "translation key sorting" do
|
487
|
+
|
488
|
+
describe ".sort_translation_keys?" do
|
489
|
+
after { described_class.send(:remove_instance_variable, :@sort_translation_keys) }
|
490
|
+
subject { described_class.sort_translation_keys? }
|
491
|
+
|
492
|
+
|
493
|
+
context "set with config" do
|
494
|
+
|
495
|
+
context 'when :sort_translation_keys is not set in config' do
|
496
|
+
before :each do
|
497
|
+
set_config "default.yml"
|
498
|
+
end
|
499
|
+
|
500
|
+
it { should eq true }
|
501
|
+
end
|
502
|
+
|
503
|
+
context 'when :sort_translation_keys set to true in config' do
|
504
|
+
before :each do
|
505
|
+
set_config "js_sort_translation_keys_true.yml"
|
506
|
+
end
|
507
|
+
|
508
|
+
it { should eq true }
|
509
|
+
end
|
510
|
+
|
511
|
+
context 'when :sort_translation_keys set to false in config' do
|
512
|
+
before :each do
|
513
|
+
set_config "js_sort_translation_keys_false.yml"
|
514
|
+
end
|
515
|
+
|
516
|
+
it { should eq false }
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
context 'set by .sort_translation_keys' do
|
521
|
+
|
522
|
+
context "when it is not set" do
|
523
|
+
it { should eq true }
|
524
|
+
end
|
525
|
+
|
526
|
+
context "when it is set to true" do
|
527
|
+
before { described_class.sort_translation_keys = true }
|
528
|
+
|
529
|
+
it { should eq true }
|
530
|
+
end
|
531
|
+
|
532
|
+
context "when it is set to false" do
|
533
|
+
before { described_class.sort_translation_keys = false }
|
534
|
+
|
535
|
+
it { should eq false }
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
context "exporting" do
|
541
|
+
subject do
|
542
|
+
I18n::JS.export
|
543
|
+
file_should_exist "en.js"
|
544
|
+
File.read(File.join(I18n::JS.export_i18n_js_dir_path, "en.js"))
|
545
|
+
end
|
546
|
+
|
547
|
+
before do
|
548
|
+
stub_const('I18n::JS::DEFAULT_EXPORT_DIR_PATH', temp_path)
|
549
|
+
end
|
550
|
+
|
551
|
+
context 'sort_translation_keys is true' do
|
552
|
+
before :each do
|
553
|
+
set_config "js_sort_translation_keys_true.yml"
|
554
|
+
end
|
555
|
+
|
556
|
+
it "exports with the keys sorted" do
|
557
|
+
expect(subject).to eq(<<EOS
|
558
|
+
I18n.translations || (I18n.translations = {});
|
559
|
+
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","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"}};
|
560
|
+
EOS
|
561
|
+
)
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
end
|
404
567
|
end
|
data/spec/segment_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require "spec_helper"
|
|
3
3
|
describe I18n::JS::Segment do
|
4
4
|
|
5
5
|
let(:file) { "tmp/i18n-js/segment.js" }
|
6
|
-
let(:translations){ {
|
6
|
+
let(:translations){ { en: { "test" => "Test" }, fr: { "test" => "Test2" } } }
|
7
7
|
let(:namespace) { "MyNamespace" }
|
8
8
|
let(:pretty_print){ nil }
|
9
9
|
let(:options) { {namespace: namespace, pretty_print: pretty_print} }
|
@@ -58,14 +58,52 @@ describe I18n::JS::Segment do
|
|
58
58
|
before { allow(I18n::JS).to receive(:export_i18n_js_dir_path).and_return(temp_path) }
|
59
59
|
before { subject.save! }
|
60
60
|
|
61
|
-
|
62
|
-
|
61
|
+
context "when file does not include %{locale}" do
|
62
|
+
it "should write the file" do
|
63
|
+
file_should_exist "segment.js"
|
63
64
|
|
64
|
-
|
65
|
+
File.open(File.join(temp_path, "segment.js")){|f| f.read}.should eql <<-EOF
|
65
66
|
MyNamespace.translations || (MyNamespace.translations = {});
|
66
67
|
MyNamespace.translations["en"] = {"test":"Test"};
|
67
68
|
MyNamespace.translations["fr"] = {"test":"Test2"};
|
68
|
-
EOF
|
69
|
+
EOF
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when file includes %{locale}" do
|
74
|
+
let(:file){ "tmp/i18n-js/%{locale}.js" }
|
75
|
+
|
76
|
+
it "should write files" do
|
77
|
+
file_should_exist "en.js"
|
78
|
+
file_should_exist "fr.js"
|
79
|
+
|
80
|
+
File.open(File.join(temp_path, "en.js")){|f| f.read}.should eql <<-EOF
|
81
|
+
MyNamespace.translations || (MyNamespace.translations = {});
|
82
|
+
MyNamespace.translations["en"] = {"test":"Test"};
|
83
|
+
EOF
|
84
|
+
|
85
|
+
File.open(File.join(temp_path, "fr.js")){|f| f.read}.should eql <<-EOF
|
86
|
+
MyNamespace.translations || (MyNamespace.translations = {});
|
87
|
+
MyNamespace.translations["fr"] = {"test":"Test2"};
|
88
|
+
EOF
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when sort_translation_keys? is true" do
|
93
|
+
before :each do
|
94
|
+
I18n::JS.sort_translation_keys = true
|
95
|
+
end
|
96
|
+
|
97
|
+
let(:translations){ { en: { "b" => "Test", "a" => "Test" } } }
|
98
|
+
|
99
|
+
it 'should output the keys as sorted' do
|
100
|
+
file_should_exist "segment.js"
|
101
|
+
|
102
|
+
File.open(File.join(temp_path, "segment.js")){|f| f.read}.should eql <<-EOF
|
103
|
+
MyNamespace.translations || (MyNamespace.translations = {});
|
104
|
+
MyNamespace.translations["en"] = {"a":"Test","b":"Test"};
|
105
|
+
EOF
|
106
|
+
end
|
69
107
|
end
|
70
108
|
end
|
71
109
|
end
|
data/spec/utils_spec.rb
CHANGED
@@ -66,4 +66,41 @@ describe I18n::JS::Utils do
|
|
66
66
|
hash.should eql({:a => {:b => 1, :c => 2}})
|
67
67
|
end
|
68
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
|
+
unsorted_hash.should 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
|
+
described_class.scopes_match?([:a, :b], [:a, :b, :c]).should_not eql true
|
95
|
+
described_class.scopes_match?([:a, :b, :c], [:a, :b, :c]).should eql true
|
96
|
+
described_class.scopes_match?([:a, :b, :c], [:a, :b, :d]).should_not eql true
|
97
|
+
end
|
98
|
+
|
99
|
+
it "performs a comparison of wildcard scopes" do
|
100
|
+
described_class.scopes_match?([:a, '*'], [:a, :b, :c]).should_not eql true
|
101
|
+
described_class.scopes_match?([:a, '*', :c], [:a, :b, :c]).should eql true
|
102
|
+
described_class.scopes_match?([:a, :b, :c], [:a, '*', :c]).should eql true
|
103
|
+
described_class.scopes_match?([:a, :b, :c], [:a, '*', '*']).should eql true
|
104
|
+
end
|
105
|
+
end
|
69
106
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i18n-js
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.rc10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nando Vieira
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: i18n
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '2.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '2.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activesupport
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -141,6 +141,8 @@ files:
|
|
141
141
|
- spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml
|
142
142
|
- spec/fixtures/js_file_per_locale_without_fallbacks.yml
|
143
143
|
- spec/fixtures/js_file_with_namespace_and_pretty_print.yml
|
144
|
+
- spec/fixtures/js_sort_translation_keys_false.yml
|
145
|
+
- spec/fixtures/js_sort_translation_keys_true.yml
|
144
146
|
- spec/fixtures/locales.yml
|
145
147
|
- spec/fixtures/multiple_conditions.yml
|
146
148
|
- spec/fixtures/multiple_conditions_per_locale.yml
|
@@ -213,6 +215,8 @@ test_files:
|
|
213
215
|
- spec/fixtures/js_file_per_locale_with_fallbacks_enabled.yml
|
214
216
|
- spec/fixtures/js_file_per_locale_without_fallbacks.yml
|
215
217
|
- spec/fixtures/js_file_with_namespace_and_pretty_print.yml
|
218
|
+
- spec/fixtures/js_sort_translation_keys_false.yml
|
219
|
+
- spec/fixtures/js_sort_translation_keys_true.yml
|
216
220
|
- spec/fixtures/locales.yml
|
217
221
|
- spec/fixtures/multiple_conditions.yml
|
218
222
|
- spec/fixtures/multiple_conditions_per_locale.yml
|