i18n-js 3.0.0.rc9 → 3.0.0.rc10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|