i18n-js 4.2.0 → 4.2.1
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/.github/workflows/ruby-tests.yml +1 -1
- data/CHANGELOG.md +8 -0
- data/MIGRATING_FROM_V3_TO_V4.md +4 -4
- data/README.md +25 -18
- data/lib/i18n-js/embed_fallback_translations_plugin.rb +46 -35
- data/lib/i18n-js/export_files_plugin.rb +32 -26
- data/lib/i18n-js/plugin.rb +50 -13
- data/lib/i18n-js/schema.rb +122 -49
- data/lib/i18n-js/version.rb +1 -1
- data/lib/i18n-js.rb +11 -8
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0545bfe4fee26d8a5aed4aec6262e8a3db733ac951dbb0111f05e8fb2488e516
|
4
|
+
data.tar.gz: 50dd6e5e4e019f01e7b72fcfacb71faf2faa73ed7157082c647432b4ef96f263
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5366522c08e868ce0b555a4d0c0d10029bbee9270cb9602600f1ca4f2e89473f02baa577644dd5ae9a38046c3c03abf6c66029d79bac7d4ca41f50ed6ea4b86
|
7
|
+
data.tar.gz: c3a0670b398c51c9bd1e01b163c46b2e69fe8f4ee06a099d8d46dc78ee57da1e78bff51e35db98ddb15b91fe2f422c8a8584fadb0b4066dc191a01eab65adac4
|
data/CHANGELOG.md
CHANGED
@@ -11,6 +11,14 @@ Prefix your message with one of the following:
|
|
11
11
|
- [Security] in case of vulnerabilities.
|
12
12
|
-->
|
13
13
|
|
14
|
+
## v4.2.1 - Dec 25, 2022
|
15
|
+
|
16
|
+
- [Changed] Change plugin api to be based on instance methods. This avoids
|
17
|
+
having to pass in the config for each and every method. It also allows us
|
18
|
+
adding helper methods to the base class.
|
19
|
+
- [Fixed] Fix performance issues with embed fallback translations' initial
|
20
|
+
implementation.
|
21
|
+
|
14
22
|
## v4.2.0 - Dec 10, 2022
|
15
23
|
|
16
24
|
- [Added] Add `I18nJS::Plugin.after_export(files:, config:)` method, that's
|
data/MIGRATING_FROM_V3_TO_V4.md
CHANGED
@@ -47,8 +47,8 @@ JSON
|
|
47
47
|
```
|
48
48
|
|
49
49
|
You can also use guard. Make sure you have both
|
50
|
-
[guard](https://rubygems.org/
|
51
|
-
[guard-compat](https://rubygems.org/
|
50
|
+
[guard](https://rubygems.org/gems/guard) and
|
51
|
+
[guard-compat](https://rubygems.org/gems/guard-compat) installed and use
|
52
52
|
Guardfile file with the following contents:
|
53
53
|
|
54
54
|
```ruby
|
@@ -174,8 +174,8 @@ translations:
|
|
174
174
|
|
175
175
|
Other configuration options:
|
176
176
|
|
177
|
-
- `export_i18n_js`:
|
178
|
-
- `fallbacks`:
|
177
|
+
- `export_i18n_js`: replaced by [export_files plugin](https://github.com/fnando/i18n-js#export_files)
|
178
|
+
- `fallbacks`: replaced by [embed_fallback_translations plugin](https://github.com/fnando/i18n-js#embed_fallback_translations)
|
179
179
|
- `js_available_locales`: removed (on v4 you can use groups, like in
|
180
180
|
`{pt-BR,en}.*`)
|
181
181
|
- `namespace`: removed without an equivalent
|
data/README.md
CHANGED
@@ -209,26 +209,21 @@ i18n.store({
|
|
209
209
|
#### Plugin API
|
210
210
|
|
211
211
|
You can transform the exported translations by adding plugins. A plugin must
|
212
|
-
inherit from `I18nJS::Plugin` and can have 4 class methods
|
213
|
-
|
214
|
-
[lib/i18n-js/embed_fallback_translations_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/embed_fallback_translations_plugin.rb)
|
215
|
-
|
216
|
-
Here's the base `I18nJS::Plugin` class with the documented api:
|
212
|
+
inherit from `I18nJS::Plugin` and can have 4 class methods (they're all optional
|
213
|
+
and will default to a noop implementation). For real examples, see [lib/i18n-js/embed_fallback_translations_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/embed_fallback_translations_plugin.rb) and [lib/i18n-js/export_files_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/export_files_plugin.rb)
|
217
214
|
|
218
215
|
```ruby
|
219
216
|
# frozen_string_literal: true
|
220
217
|
|
221
218
|
module I18nJS
|
222
|
-
class Plugin
|
219
|
+
class SamplePlugin < I18nJS::Plugin
|
223
220
|
# This method is responsible for transforming the translations. The
|
224
221
|
# translations you'll receive may be already be filtered by other plugins
|
225
222
|
# and by the default filtering itself. If you need to access the original
|
226
223
|
# translations, use `I18nJS.translations`.
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
# possible.
|
231
|
-
def self.transform(translations:, config:)
|
224
|
+
def transform(translations:)
|
225
|
+
# transform `translations` here…
|
226
|
+
|
232
227
|
translations
|
233
228
|
end
|
234
229
|
|
@@ -237,7 +232,11 @@ module I18nJS
|
|
237
232
|
# If the configuration contains invalid data, then you must raise an
|
238
233
|
# exception using something like
|
239
234
|
# `raise I18nJS::Schema::InvalidError, error_message`.
|
240
|
-
|
235
|
+
#
|
236
|
+
# Notice the validation will only happen when the plugin configuration is
|
237
|
+
# set (i.e. the configuration contains your config key).
|
238
|
+
def validate_schema
|
239
|
+
# validate plugin schema here…
|
241
240
|
end
|
242
241
|
|
243
242
|
# This method must set up the basic plugin configuration, like adding the
|
@@ -246,7 +245,9 @@ module I18nJS
|
|
246
245
|
#
|
247
246
|
# If you don't add this key, the linter will prevent non-default keys from
|
248
247
|
# being added to the configuration file.
|
249
|
-
def
|
248
|
+
def setup
|
249
|
+
# If you plugin has configuration, uncomment the line below
|
250
|
+
# I18nJS::Schema.root_keys << config_key
|
250
251
|
end
|
251
252
|
|
252
253
|
# This method is called whenever `I18nJS.call(**kwargs)` finishes exporting
|
@@ -254,15 +255,21 @@ module I18nJS
|
|
254
255
|
#
|
255
256
|
# You can use it to further process exported files, or generate new files
|
256
257
|
# based on the translations that have been exported.
|
257
|
-
|
258
|
-
|
259
|
-
# processing files; otherwise, opting out won't be possible.
|
260
|
-
def self.after_export(files:, config:)
|
258
|
+
def after_export(files:)
|
259
|
+
# process exported files here…
|
261
260
|
end
|
262
261
|
end
|
263
262
|
end
|
264
263
|
```
|
265
264
|
|
265
|
+
The class `I18nJS::Plugin` implements some helper methods that you can use:
|
266
|
+
|
267
|
+
- `I18nJS::Plugin#config_key`: the configuration key that was inferred out of
|
268
|
+
your plugin's class name.
|
269
|
+
- `I18nJS::Plugin#config`: the plugin configuration.
|
270
|
+
- `I18nJS::Plugin#enabled?`: whether the plugin is enabled or not based on the
|
271
|
+
plugin's configuration.
|
272
|
+
|
266
273
|
To distribute this plugin, you need to create a gem package that matches the
|
267
274
|
pattern `i18n-js/*_plugin.rb`. You can test whether your plugin will be found by
|
268
275
|
installing your gem, opening a iRB session and running
|
@@ -512,7 +519,7 @@ that loads all the exported translation.
|
|
512
519
|
|
513
520
|
[There's a document](https://github.com/fnando/i18n-js/tree/main/MIGRATING_FROM_V3_TO_V4.md)
|
514
521
|
outlining some of the things you need to do to migrate from v3 to v4. It may not
|
515
|
-
be as complete as we'd like it to be, so let
|
522
|
+
be as complete as we'd like it to be, so let us know if you face any issues
|
516
523
|
during the migration is not outline is that document.
|
517
524
|
|
518
525
|
#### How can I export translations without having a database around?
|
@@ -4,54 +4,65 @@ module I18nJS
|
|
4
4
|
require "i18n-js/plugin"
|
5
5
|
|
6
6
|
class EmbedFallbackTranslationsPlugin < I18nJS::Plugin
|
7
|
-
|
7
|
+
module Utils
|
8
|
+
# Based on deep_merge by Stefan Rusterholz, see
|
9
|
+
# <https://www.ruby-forum.com/topic/142809>.
|
10
|
+
# This method is used to handle I18n fallbacks. Given two equivalent path
|
11
|
+
# nodes in two locale trees:
|
12
|
+
# 1. If the node in the current locale appears to be an I18n pluralization
|
13
|
+
# (:one, :other, etc.), use the node, but merge in any missing/non-nil
|
14
|
+
# keys from the fallback (default) locale.
|
15
|
+
# 2. Else if both nodes are Hashes, combine (merge) the key-value pairs of
|
16
|
+
# the two nodes into one, prioritizing the current locale.
|
17
|
+
# 3. Else if either node is nil, use the other node.
|
18
|
+
|
19
|
+
PLURAL_KEYS = %i[zero one two few many other].freeze
|
20
|
+
PLURAL_MERGER = proc {|_key, v1, v2| v1 || v2 }
|
21
|
+
MERGER = proc do |_key, v1, v2|
|
22
|
+
if v1.is_a?(Hash) && v2.is_a?(Hash)
|
23
|
+
if (v2.keys - PLURAL_KEYS).empty?
|
24
|
+
v2.merge(v1, &PLURAL_MERGER).slice(*v2.keys)
|
25
|
+
else
|
26
|
+
v1.merge(v2, &MERGER)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
v2 || v1
|
30
|
+
end
|
31
|
+
end
|
8
32
|
|
9
|
-
|
10
|
-
|
33
|
+
def self.deep_merge(target_hash, hash)
|
34
|
+
target_hash.merge(hash, &MERGER)
|
35
|
+
end
|
11
36
|
end
|
12
37
|
|
13
|
-
def
|
14
|
-
|
38
|
+
def setup
|
39
|
+
I18nJS::Schema.root_keys << config_key
|
40
|
+
end
|
15
41
|
|
16
|
-
|
42
|
+
def validate_schema
|
17
43
|
valid_keys = %i[enabled]
|
18
|
-
schema = I18nJS::Schema.new(config)
|
19
44
|
|
20
|
-
schema.expect_required_keys(valid_keys,
|
21
|
-
schema.reject_extraneous_keys(valid_keys,
|
22
|
-
schema.expect_enabled_config(CONFIG_KEY, plugin_config[:enabled])
|
45
|
+
schema.expect_required_keys(keys: valid_keys, path: [config_key])
|
46
|
+
schema.reject_extraneous_keys(keys: valid_keys, path: [config_key])
|
23
47
|
end
|
24
48
|
|
25
|
-
def
|
26
|
-
return translations unless
|
49
|
+
def transform(translations:)
|
50
|
+
return translations unless enabled?
|
27
51
|
|
28
|
-
|
29
|
-
|
52
|
+
fallback_locale = I18n.default_locale.to_sym
|
53
|
+
locales_to_fallback = translations.keys - [fallback_locale]
|
30
54
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
end
|
55
|
+
translations_with_fallback = {}
|
56
|
+
translations_with_fallback[fallback_locale] =
|
57
|
+
translations[fallback_locale]
|
36
58
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
mapping.each do |locale, glob|
|
42
|
-
missing_keys = default_locale_paths - glob.paths
|
43
|
-
|
44
|
-
missing_keys.each do |key|
|
45
|
-
components = key.split(".").map(&:to_sym)
|
46
|
-
fallback_translation = translations.dig(default_locale, *components)
|
47
|
-
|
48
|
-
next unless fallback_translation
|
49
|
-
|
50
|
-
translations_glob.set([locale, key].join("."), fallback_translation)
|
51
|
-
end
|
59
|
+
locales_to_fallback.each do |locale|
|
60
|
+
translations_with_fallback[locale] = Utils.deep_merge(
|
61
|
+
translations[fallback_locale], translations[locale]
|
62
|
+
)
|
52
63
|
end
|
53
64
|
|
54
|
-
|
65
|
+
translations_with_fallback
|
55
66
|
end
|
56
67
|
end
|
57
68
|
|
@@ -4,37 +4,43 @@ module I18nJS
|
|
4
4
|
require "i18n-js/plugin"
|
5
5
|
|
6
6
|
class ExportFilesPlugin < I18nJS::Plugin
|
7
|
-
|
8
|
-
|
9
|
-
def self.setup
|
10
|
-
I18nJS::Schema.root_keys << CONFIG_KEY
|
7
|
+
def setup
|
8
|
+
I18nJS::Schema.root_keys << config_key
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
14
|
-
return unless config.key?(CONFIG_KEY)
|
15
|
-
|
16
|
-
plugin_config = config[CONFIG_KEY]
|
11
|
+
def validate_schema
|
17
12
|
valid_keys = %i[enabled files]
|
18
|
-
schema = I18nJS::Schema.new(config)
|
19
|
-
|
20
|
-
schema.expect_required_keys(valid_keys, plugin_config)
|
21
|
-
schema.reject_extraneous_keys(valid_keys, plugin_config)
|
22
|
-
schema.expect_enabled_config(CONFIG_KEY, plugin_config[:enabled])
|
23
|
-
schema.expect_array_with_items(:files, plugin_config[:files])
|
24
|
-
|
25
|
-
plugin_config[:files].each do |exports|
|
26
|
-
schema.expect_required_keys(%i[template output], exports)
|
27
|
-
schema.reject_extraneous_keys(%i[template output], exports)
|
28
|
-
schema.expect_type(:template, exports[:template], String, exports)
|
29
|
-
schema.expect_type(:output, exports[:output], String, exports)
|
30
|
-
end
|
31
|
-
end
|
32
13
|
|
33
|
-
|
34
|
-
|
14
|
+
schema.expect_required_keys(keys: valid_keys, path: [config_key])
|
15
|
+
schema.reject_extraneous_keys(keys: valid_keys, path: [config_key])
|
16
|
+
schema.expect_array_with_items(path: [config_key, :files])
|
17
|
+
|
18
|
+
config[:files].each_with_index do |_exports, index|
|
19
|
+
export_keys = %i[template output]
|
35
20
|
|
36
|
-
|
21
|
+
schema.expect_required_keys(
|
22
|
+
keys: export_keys,
|
23
|
+
path: [config_key, :files, index]
|
24
|
+
)
|
25
|
+
|
26
|
+
schema.reject_extraneous_keys(
|
27
|
+
keys: export_keys,
|
28
|
+
path: [config_key, :files, index]
|
29
|
+
)
|
30
|
+
|
31
|
+
schema.expect_type(
|
32
|
+
path: [config_key, :files, index, :template],
|
33
|
+
types: String
|
34
|
+
)
|
35
|
+
|
36
|
+
schema.expect_type(
|
37
|
+
path: [config_key, :files, index, :output],
|
38
|
+
types: String
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
37
42
|
|
43
|
+
def after_export(files:)
|
38
44
|
require "erb"
|
39
45
|
require "digest/md5"
|
40
46
|
require "json"
|
@@ -45,7 +51,7 @@ module I18nJS
|
|
45
51
|
extension = File.extname(name)
|
46
52
|
base_name = File.basename(file, extension)
|
47
53
|
|
48
|
-
|
54
|
+
config[:files].each do |export|
|
49
55
|
translations = JSON.load_file(file)
|
50
56
|
template = Template.new(
|
51
57
|
file: file,
|
data/lib/i18n-js/plugin.rb
CHANGED
@@ -3,13 +3,16 @@
|
|
3
3
|
require_relative "schema"
|
4
4
|
|
5
5
|
module I18nJS
|
6
|
+
def self.available_plugins
|
7
|
+
@available_plugins ||= Set.new
|
8
|
+
end
|
9
|
+
|
6
10
|
def self.plugins
|
7
11
|
@plugins ||= []
|
8
12
|
end
|
9
13
|
|
10
14
|
def self.register_plugin(plugin)
|
11
|
-
|
12
|
-
plugin.setup
|
15
|
+
available_plugins << plugin
|
13
16
|
end
|
14
17
|
|
15
18
|
def self.plugin_files
|
@@ -22,16 +25,53 @@ module I18nJS
|
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
28
|
+
def self.initialize_plugins!(config:)
|
29
|
+
@plugins = available_plugins.map do |plugin|
|
30
|
+
plugin.new(config: config).tap(&:setup)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
25
34
|
class Plugin
|
35
|
+
# The configuration that's being used to export translations.
|
36
|
+
attr_reader :main_config
|
37
|
+
|
38
|
+
# The `I18nJS::Schema` instance that can be used to validate your plugin's
|
39
|
+
# configuration.
|
40
|
+
attr_reader :schema
|
41
|
+
|
42
|
+
def initialize(config:)
|
43
|
+
@main_config = config
|
44
|
+
@schema = I18nJS::Schema.new(@main_config)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Infer the config key name out of the class.
|
48
|
+
# If you plugin is called `MySamplePlugin`, the key will be `my_sample`.
|
49
|
+
def config_key
|
50
|
+
self.class.name.split("::").last
|
51
|
+
.gsub(/Plugin$/, "")
|
52
|
+
.gsub(/^([A-Z]+)([A-Z])/) { "#{$1.downcase}#{$2}" }
|
53
|
+
.gsub(/^([A-Z]+)/) { $1.downcase }
|
54
|
+
.gsub(/([A-Z]+)/m) { "_#{$1.downcase}" }
|
55
|
+
.downcase
|
56
|
+
.to_sym
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return the plugin configuration
|
60
|
+
def config
|
61
|
+
main_config[config_key] || {}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Check whether plugin is enabled or not.
|
65
|
+
# A plugin is enabled when the plugin configuration has `enabled: true`.
|
66
|
+
def enabled?
|
67
|
+
config[:enabled]
|
68
|
+
end
|
69
|
+
|
26
70
|
# This method is responsible for transforming the translations. The
|
27
71
|
# translations you'll receive may be already be filtered by other plugins
|
28
72
|
# and by the default filtering itself. If you need to access the original
|
29
73
|
# translations, use `I18nJS.translations`.
|
30
|
-
|
31
|
-
# Make sure you always check whether your plugin is active before
|
32
|
-
# transforming translations; otherwise, opting out transformation won't be
|
33
|
-
# possible.
|
34
|
-
def self.transform(translations:, config:) # rubocop:disable Lint/UnusedMethodArgument
|
74
|
+
def transform(translations:)
|
35
75
|
translations
|
36
76
|
end
|
37
77
|
|
@@ -40,7 +80,7 @@ module I18nJS
|
|
40
80
|
# If the configuration contains invalid data, then you must raise an
|
41
81
|
# exception using something like
|
42
82
|
# `raise I18nJS::Schema::InvalidError, error_message`.
|
43
|
-
def
|
83
|
+
def validate_schema
|
44
84
|
end
|
45
85
|
|
46
86
|
# This method must set up the basic plugin configuration, like adding the
|
@@ -49,7 +89,7 @@ module I18nJS
|
|
49
89
|
#
|
50
90
|
# If you don't add this key, the linter will prevent non-default keys from
|
51
91
|
# being added to the configuration file.
|
52
|
-
def
|
92
|
+
def setup
|
53
93
|
end
|
54
94
|
|
55
95
|
# This method is called whenever `I18nJS.call(**kwargs)` finishes exporting
|
@@ -57,10 +97,7 @@ module I18nJS
|
|
57
97
|
#
|
58
98
|
# You can use it to further process exported files, or generate new files
|
59
99
|
# based on the translations that have been exported.
|
60
|
-
|
61
|
-
# Make sure you always check whether your plugin is active before
|
62
|
-
# processing files; otherwise, opting out won't be possible.
|
63
|
-
def self.after_export(files:, config:)
|
100
|
+
def after_export(files:)
|
64
101
|
end
|
65
102
|
end
|
66
103
|
end
|
data/lib/i18n-js/schema.rb
CHANGED
@@ -25,7 +25,7 @@ module I18nJS
|
|
25
25
|
def self.validate!(target)
|
26
26
|
schema = new(target)
|
27
27
|
schema.validate!
|
28
|
-
|
28
|
+
schema
|
29
29
|
end
|
30
30
|
|
31
31
|
attr_reader :target
|
@@ -35,13 +35,44 @@ module I18nJS
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def validate!
|
38
|
-
|
38
|
+
validate_root
|
39
|
+
|
40
|
+
expect_required_keys(
|
41
|
+
keys: self.class.required_root_keys,
|
42
|
+
path: nil
|
43
|
+
)
|
44
|
+
|
45
|
+
reject_extraneous_keys(
|
46
|
+
keys: self.class.root_keys,
|
47
|
+
path: nil
|
48
|
+
)
|
39
49
|
|
40
|
-
expect_required_keys(self.class.required_root_keys, target)
|
41
|
-
reject_extraneous_keys(self.class.root_keys, target)
|
42
50
|
validate_translations
|
43
51
|
validate_lint_translations
|
44
52
|
validate_lint_scripts
|
53
|
+
validate_plugins
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_plugins
|
57
|
+
I18nJS.plugins.each do |plugin|
|
58
|
+
next unless target.key?(plugin.config_key)
|
59
|
+
|
60
|
+
expect_type(
|
61
|
+
path: [plugin.config_key, :enabled],
|
62
|
+
types: [TrueClass, FalseClass]
|
63
|
+
)
|
64
|
+
|
65
|
+
plugin.validate_schema
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_root
|
70
|
+
return if target.is_a?(Hash)
|
71
|
+
|
72
|
+
message = "Expected config to be \"Hash\"; " \
|
73
|
+
"got #{target.class} instead"
|
74
|
+
|
75
|
+
reject message, target
|
45
76
|
end
|
46
77
|
|
47
78
|
def validate_lint_translations
|
@@ -49,11 +80,14 @@ module I18nJS
|
|
49
80
|
|
50
81
|
return unless target.key?(key)
|
51
82
|
|
52
|
-
|
83
|
+
expect_type(path: [key], types: Hash)
|
84
|
+
|
85
|
+
expect_required_keys(
|
86
|
+
keys: REQUIRED_LINT_TRANSLATIONS_KEYS,
|
87
|
+
path: [key]
|
88
|
+
)
|
53
89
|
|
54
|
-
expect_type(key,
|
55
|
-
expect_required_keys(REQUIRED_LINT_TRANSLATIONS_KEYS, config)
|
56
|
-
expect_type(:ignore, config[:ignore], Array, config)
|
90
|
+
expect_type(path: [key, :ignore], types: Array)
|
57
91
|
end
|
58
92
|
|
59
93
|
def validate_lint_scripts
|
@@ -61,31 +95,36 @@ module I18nJS
|
|
61
95
|
|
62
96
|
return unless target.key?(key)
|
63
97
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
expect_type(:
|
98
|
+
expect_type(path: [key], types: Hash)
|
99
|
+
expect_required_keys(
|
100
|
+
keys: REQUIRED_LINT_SCRIPTS_KEYS,
|
101
|
+
path: [key]
|
102
|
+
)
|
103
|
+
expect_type(path: [key, :ignore], types: Array)
|
104
|
+
expect_type(path: [key, :patterns], types: Array)
|
70
105
|
end
|
71
106
|
|
72
107
|
def validate_translations
|
73
|
-
|
74
|
-
|
75
|
-
expect_type(:translations, translations, Array, target)
|
76
|
-
expect_array_with_items(:translations, translations)
|
108
|
+
expect_array_with_items(path: [:translations])
|
77
109
|
|
78
|
-
translations.
|
79
|
-
validate_translation(translation)
|
110
|
+
target[:translations].each_with_index do |translation, index|
|
111
|
+
validate_translation(translation, index)
|
80
112
|
end
|
81
113
|
end
|
82
114
|
|
83
|
-
def validate_translation(
|
84
|
-
expect_required_keys(
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
115
|
+
def validate_translation(_translation, index)
|
116
|
+
expect_required_keys(
|
117
|
+
path: [:translations, index],
|
118
|
+
keys: REQUIRED_TRANSLATION_KEYS
|
119
|
+
)
|
120
|
+
|
121
|
+
reject_extraneous_keys(
|
122
|
+
keys: TRANSLATION_KEYS,
|
123
|
+
path: [:translations, index]
|
124
|
+
)
|
125
|
+
|
126
|
+
expect_type(path: [:translations, index, :file], types: String)
|
127
|
+
expect_array_with_items(path: [:translations, index, :patterns])
|
89
128
|
end
|
90
129
|
|
91
130
|
def reject(error_message, node = nil)
|
@@ -93,51 +132,85 @@ module I18nJS
|
|
93
132
|
raise InvalidError, "#{error_message}#{node_json}"
|
94
133
|
end
|
95
134
|
|
96
|
-
def
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
reject "Expected #{config_key}.enabled to be a boolean; " \
|
102
|
-
"got #{actual_type} instead"
|
103
|
-
end
|
135
|
+
def expect_type(path:, types:)
|
136
|
+
path = prepare_path(path: path)
|
137
|
+
value = value_for(path: path)
|
138
|
+
types = Array(types)
|
104
139
|
|
105
|
-
|
106
|
-
return if value.is_a?(expected_type)
|
140
|
+
return if types.any? {|type| value.is_a?(type) }
|
107
141
|
|
108
142
|
actual_type = value.class
|
109
143
|
|
144
|
+
type_desc = if types.size == 1
|
145
|
+
types[0].to_s.inspect
|
146
|
+
else
|
147
|
+
"one of #{types.inspect}"
|
148
|
+
end
|
149
|
+
|
110
150
|
message = [
|
111
|
-
"Expected #{
|
151
|
+
"Expected #{path.join('.').inspect} to be #{type_desc};",
|
112
152
|
"got #{actual_type} instead"
|
113
153
|
].join(" ")
|
114
154
|
|
115
|
-
reject message,
|
155
|
+
reject message, target
|
116
156
|
end
|
117
157
|
|
118
|
-
def expect_array_with_items(
|
158
|
+
def expect_array_with_items(path:)
|
159
|
+
expect_type(path: path, types: Array)
|
160
|
+
|
161
|
+
path = prepare_path(path: path)
|
162
|
+
value = value_for(path: path)
|
163
|
+
|
119
164
|
return unless value.empty?
|
120
165
|
|
121
|
-
reject "Expected #{
|
166
|
+
reject "Expected #{path.join('.').inspect} to have at least one item",
|
167
|
+
target
|
122
168
|
end
|
123
169
|
|
124
|
-
def expect_required_keys(
|
125
|
-
|
170
|
+
def expect_required_keys(keys:, path:)
|
171
|
+
path = prepare_path(path: path)
|
172
|
+
value = value_for(path: path)
|
173
|
+
actual_keys = value.keys.map(&:to_sym)
|
126
174
|
|
127
|
-
|
128
|
-
next if
|
175
|
+
keys.each do |key|
|
176
|
+
next if actual_keys.include?(key)
|
129
177
|
|
130
|
-
|
178
|
+
path_desc = if path.empty?
|
179
|
+
key.to_s.inspect
|
180
|
+
else
|
181
|
+
(path + [key]).join(".").inspect
|
182
|
+
end
|
183
|
+
|
184
|
+
reject "Expected #{path_desc} to be defined", target
|
131
185
|
end
|
132
186
|
end
|
133
187
|
|
134
|
-
def reject_extraneous_keys(
|
135
|
-
|
136
|
-
|
188
|
+
def reject_extraneous_keys(keys:, path:)
|
189
|
+
path = prepare_path(path: path)
|
190
|
+
value = value_for(path: path)
|
191
|
+
|
192
|
+
actual_keys = value.keys.map(&:to_sym)
|
193
|
+
extraneous = actual_keys.to_a - keys.to_a
|
137
194
|
|
138
195
|
return if extraneous.empty?
|
139
196
|
|
140
|
-
|
197
|
+
path_desc = if path.empty?
|
198
|
+
"config"
|
199
|
+
else
|
200
|
+
path.join(".").inspect
|
201
|
+
end
|
202
|
+
|
203
|
+
reject "#{path_desc} has unexpected keys: #{extraneous.inspect}",
|
204
|
+
target
|
205
|
+
end
|
206
|
+
|
207
|
+
def prepare_path(path:)
|
208
|
+
path = path.to_s.split(".").map(&:to_sym) unless path.is_a?(Array)
|
209
|
+
path
|
210
|
+
end
|
211
|
+
|
212
|
+
def value_for(path:)
|
213
|
+
path.empty? ? target : target.dig(*path)
|
141
214
|
end
|
142
215
|
end
|
143
216
|
end
|
data/lib/i18n-js/version.rb
CHANGED
data/lib/i18n-js.rb
CHANGED
@@ -23,29 +23,32 @@ module I18nJS
|
|
23
23
|
"you must set either `config_file` or `config`"
|
24
24
|
end
|
25
25
|
|
26
|
-
load_plugins!
|
27
|
-
|
28
26
|
config = Glob::SymbolizeKeys.call(config || load_config_file(config_file))
|
29
27
|
|
28
|
+
load_plugins!
|
29
|
+
initialize_plugins!(config: config)
|
30
30
|
Schema.validate!(config)
|
31
|
+
|
31
32
|
exported_files = []
|
32
33
|
|
33
|
-
config[:translations].each
|
34
|
-
exported_files += export_group(group, config)
|
35
|
-
end
|
34
|
+
config[:translations].each {|group| exported_files += export_group(group) }
|
36
35
|
|
37
36
|
plugins.each do |plugin|
|
38
|
-
plugin.after_export(files: exported_files.dup
|
37
|
+
plugin.after_export(files: exported_files.dup) if plugin.enabled?
|
39
38
|
end
|
40
39
|
|
41
40
|
exported_files
|
42
41
|
end
|
43
42
|
|
44
|
-
def self.export_group(group
|
43
|
+
def self.export_group(group)
|
45
44
|
filtered_translations = Glob.filter(translations, group[:patterns])
|
46
45
|
filtered_translations =
|
47
46
|
plugins.reduce(filtered_translations) do |buffer, plugin|
|
48
|
-
plugin.
|
47
|
+
if plugin.enabled?
|
48
|
+
plugin.transform(translations: buffer)
|
49
|
+
else
|
50
|
+
buffer
|
51
|
+
end
|
49
52
|
end
|
50
53
|
|
51
54
|
output_file_path = File.expand_path(group[:file])
|
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: 4.2.
|
4
|
+
version: 4.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nando Vieira
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-12-
|
11
|
+
date: 2022-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: glob
|
@@ -223,10 +223,10 @@ metadata:
|
|
223
223
|
rubygems_mfa_required: 'true'
|
224
224
|
homepage_uri: https://github.com/fnando/i18n-js
|
225
225
|
bug_tracker_uri: https://github.com/fnando/i18n-js/issues
|
226
|
-
source_code_uri: https://github.com/fnando/i18n-js/tree/v4.2.
|
227
|
-
changelog_uri: https://github.com/fnando/i18n-js/tree/v4.2.
|
228
|
-
documentation_uri: https://github.com/fnando/i18n-js/tree/v4.2.
|
229
|
-
license_uri: https://github.com/fnando/i18n-js/tree/v4.2.
|
226
|
+
source_code_uri: https://github.com/fnando/i18n-js/tree/v4.2.1
|
227
|
+
changelog_uri: https://github.com/fnando/i18n-js/tree/v4.2.1/CHANGELOG.md
|
228
|
+
documentation_uri: https://github.com/fnando/i18n-js/tree/v4.2.1/README.md
|
229
|
+
license_uri: https://github.com/fnando/i18n-js/tree/v4.2.1/LICENSE.md
|
230
230
|
post_install_message:
|
231
231
|
rdoc_options: []
|
232
232
|
require_paths:
|
@@ -242,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
242
|
- !ruby/object:Gem::Version
|
243
243
|
version: '0'
|
244
244
|
requirements: []
|
245
|
-
rubygems_version: 3.3.
|
245
|
+
rubygems_version: 3.3.7
|
246
246
|
signing_key:
|
247
247
|
specification_version: 4
|
248
248
|
summary: Export i18n translations and use them on JavaScript.
|