i18n-js 4.0.1 → 4.1.0
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 +22 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +14 -2
- data/MIGRATING_FROM_V3_TO_V4.md +1 -1
- data/README.md +219 -9
- data/bin/pack +79 -0
- data/i18n-js.gemspec +4 -1
- data/lib/i18n-js/cli/check_command.rb +7 -147
- data/lib/i18n-js/cli/command.rb +10 -0
- data/lib/i18n-js/cli/lint_scripts_command.rb +157 -0
- data/lib/i18n-js/cli/lint_translations_command.rb +155 -0
- data/lib/i18n-js/cli/plugins_command.rb +67 -0
- data/lib/i18n-js/cli.rb +12 -1
- data/lib/i18n-js/embed_fallback_translations_plugin.rb +78 -0
- data/lib/i18n-js/lint.js +150645 -0
- data/lib/i18n-js/lint.ts +196 -0
- data/lib/i18n-js/listen.rb +3 -2
- data/lib/i18n-js/plugin.rb +38 -0
- data/lib/i18n-js/schema.rb +53 -14
- data/lib/i18n-js/version.rb +1 -1
- data/lib/i18n-js.rb +20 -3
- data/package.json +10 -0
- metadata +32 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60f1ad0e9477d8e6570573f64ac2a55136653da8e4791c6fc32e69c9547e197b
|
4
|
+
data.tar.gz: d46a3d8b8a25020635d9c108b2cb2caf3a256e419112a21a0d986f94e1e19c37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1d63d094866a836a69a9b3fc8f2989608a1b794daf5d5c82062e17b3bcb07ab7fb0ec8f2c05aa27a2585236cff33d974c313ca81be96de129340ecce8f78490
|
7
|
+
data.tar.gz: 7ec126b41663f765a421950c1ddcb215d460681355088fc77731673c44f3baf590cc58c6e5841bc9dda6f58732249488cdad3b020287b57d7d1de70cf8e95716
|
@@ -9,12 +9,15 @@ on:
|
|
9
9
|
|
10
10
|
jobs:
|
11
11
|
build:
|
12
|
-
name:
|
12
|
+
name:
|
13
|
+
Tests with Ruby ${{ matrix.ruby }}, Node ${{ matrix.node }} and ${{
|
14
|
+
matrix.gemfile }}
|
13
15
|
runs-on: "ubuntu-latest"
|
14
16
|
strategy:
|
15
17
|
fail-fast: false
|
16
18
|
matrix:
|
17
19
|
ruby: ["2.7", "3.0", "3.1"]
|
20
|
+
node: ["16", "18"]
|
18
21
|
gemfile:
|
19
22
|
- Gemfile
|
20
23
|
if: |
|
@@ -32,6 +35,23 @@ jobs:
|
|
32
35
|
${{ runner.os }}-${{ matrix.ruby }}-gems-${{
|
33
36
|
hashFiles(matrix.gemfile) }}
|
34
37
|
|
38
|
+
- uses: actions/cache@v3
|
39
|
+
id: npm-cache
|
40
|
+
with:
|
41
|
+
path: vendor/bundle
|
42
|
+
key: >
|
43
|
+
${{ runner.os }}-${{ matrix.node }}-npm-${{
|
44
|
+
hashFiles('package.json') }}
|
45
|
+
|
46
|
+
- name: Set up Node
|
47
|
+
uses: actions/setup-node@v2-beta
|
48
|
+
with:
|
49
|
+
node-version: ${{ matrix.node }}
|
50
|
+
|
51
|
+
- name: Install npm dependencies
|
52
|
+
run: |
|
53
|
+
yarn install
|
54
|
+
|
35
55
|
- name: Set up Ruby
|
36
56
|
uses: ruby/setup-ruby@v1
|
37
57
|
with:
|
@@ -49,4 +69,5 @@ jobs:
|
|
49
69
|
env:
|
50
70
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
51
71
|
run: |
|
72
|
+
yarn compile
|
52
73
|
bundle exec rake
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -11,13 +11,25 @@ Prefix your message with one of the following:
|
|
11
11
|
- [Security] in case of vulnerabilities.
|
12
12
|
-->
|
13
13
|
|
14
|
-
## v4.0
|
14
|
+
## v4.1.0 - Dec 09, 2022
|
15
|
+
|
16
|
+
- [Added] Parse configuration files as erb.
|
17
|
+
- [Changed] `I18n.listen(run_on_start:)` was added to control if files should be
|
18
|
+
exported during `I18n.listen`'s boot. The default value is `true`.
|
19
|
+
- [Added] Now it's possible to transform translations before exporting them
|
20
|
+
using a stable plugin api.
|
21
|
+
- [Deprecated] The `i18n check` has been deprecated. Use
|
22
|
+
`i18n lint:translations` instead.
|
23
|
+
- [Added] Use `i18n lint:scripts` to lint JavaScript/TypeScript.
|
24
|
+
- [Fixed] Expand paths passed to `I18nJS.listen(locales_dir:)`.
|
25
|
+
|
26
|
+
## v4.0.1 - Aug 25, 2022
|
15
27
|
|
16
28
|
- [Fixed] Shell out export to avoid handling I18n reloading heuristics.
|
17
29
|
- [Changed] `I18nJS.listen` now accepts a directories list to watch.
|
18
30
|
- [Changed] `I18nJS.listen` now accepts
|
19
31
|
[listen](https://rubygems.org/gems/listen) options via `:options`.
|
20
32
|
|
21
|
-
## Jul 29, 2022
|
33
|
+
## v4.0.0 - Jul 29, 2022
|
22
34
|
|
23
35
|
- Official release of i18n-js v4.0.0.
|
data/MIGRATING_FROM_V3_TO_V4.md
CHANGED
@@ -135,7 +135,7 @@ not even using it on my projects) or eRb files.
|
|
135
135
|
|
136
136
|
### Upgrading the configuration file
|
137
137
|
|
138
|
-
The configuration file loaded from `config/
|
138
|
+
The configuration file loaded from `config/i18n.yml` has changed. Given the v3
|
139
139
|
configuration below
|
140
140
|
|
141
141
|
```yaml
|
data/README.md
CHANGED
@@ -76,6 +76,22 @@ The output path can use the following placeholders:
|
|
76
76
|
- `:locale`: the language that's being exported.
|
77
77
|
- `:digest`: the MD5 hex digest of the exported file.
|
78
78
|
|
79
|
+
The config file is processed as erb, so you can have dynamic content on it if
|
80
|
+
you want. The following example shows how to use groups from a variable.
|
81
|
+
|
82
|
+
```yml
|
83
|
+
---
|
84
|
+
<% group = "{en,pt}" %>
|
85
|
+
|
86
|
+
translations:
|
87
|
+
- file: app/frontend/translations.json
|
88
|
+
patterns:
|
89
|
+
- "<%= group %>.*"
|
90
|
+
- "!<%= group %>.activerecord"
|
91
|
+
- "!<%= group %>.errors"
|
92
|
+
- "!<%= group %>.number.nth"
|
93
|
+
```
|
94
|
+
|
79
95
|
The Ruby API:
|
80
96
|
|
81
97
|
```ruby
|
@@ -96,7 +112,9 @@ Commands:
|
|
96
112
|
- init: Initialize a project
|
97
113
|
- export: Export translations as JSON files
|
98
114
|
- version: Show package version
|
99
|
-
-
|
115
|
+
- plugins: List plugins that will be activated
|
116
|
+
- lint:translations: Check for missing translations
|
117
|
+
- lint:scripts: Lint files using TypeScript
|
100
118
|
|
101
119
|
Run `i18n COMMAND --help` for more information on specific commands.
|
102
120
|
```
|
@@ -105,14 +123,126 @@ By default, `i18n` will use `config/i18n.yml` and `config/environment.rb` as the
|
|
105
123
|
configuration files. If you don't have these files, then you'll need to specify
|
106
124
|
both `--config` and `--require`.
|
107
125
|
|
126
|
+
### Plugins
|
127
|
+
|
128
|
+
#### Built-in plugins:
|
129
|
+
|
130
|
+
##### `embed_fallback_translations`:
|
131
|
+
|
132
|
+
Embed fallback translations inferred from the default locale. This can be useful
|
133
|
+
in cases where you have multiple large translation files and don't want to load
|
134
|
+
the default locale together with the target locale.
|
135
|
+
|
136
|
+
To use it, add the following to your configuration file:
|
137
|
+
|
138
|
+
```yaml
|
139
|
+
embed_fallback_translations:
|
140
|
+
enabled: true
|
141
|
+
```
|
142
|
+
|
143
|
+
#### Plugin API
|
144
|
+
|
145
|
+
You can transform the exported translations by adding plugins. A plugin must
|
146
|
+
inherit from `I18nJS::Plugin` and can have 3 class methods. The following
|
147
|
+
example shows how the built-in `embed_fallback_translations` plugin is
|
148
|
+
implemented.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
# frozen_string_literal: true
|
152
|
+
|
153
|
+
module I18nJS
|
154
|
+
require "i18n-js/plugin"
|
155
|
+
|
156
|
+
class EmbedFallbackTranslationsPlugin < I18nJS::Plugin
|
157
|
+
CONFIG_KEY = :embed_fallback_translations
|
158
|
+
|
159
|
+
# This method must set up the basic plugin configuration, like adding the
|
160
|
+
# config's root key in case your plugin accepts configuration (defined via
|
161
|
+
# the config file).
|
162
|
+
#
|
163
|
+
# If you don't add this key, the linter will prevent non-default keys from
|
164
|
+
# being added to the configuration file.
|
165
|
+
def self.setup
|
166
|
+
I18nJS::Schema.root_keys << CONFIG_KEY
|
167
|
+
end
|
168
|
+
|
169
|
+
# In case your plugin accepts configuration, this is where you must validate
|
170
|
+
# the configuration, making sure only valid keys and type is provided.
|
171
|
+
# If the configuration contains invalid data, then you must raise an
|
172
|
+
# exception using something like
|
173
|
+
# `raise I18nJS::Schema::InvalidError, error_message`.
|
174
|
+
def self.validate_schema(config:)
|
175
|
+
return unless config.key?(CONFIG_KEY)
|
176
|
+
|
177
|
+
plugin_config = config[CONFIG_KEY]
|
178
|
+
valid_keys = %i[enabled]
|
179
|
+
schema = I18nJS::Schema.new(config)
|
180
|
+
|
181
|
+
schema.expect_required_keys(valid_keys, plugin_config)
|
182
|
+
schema.reject_extraneous_keys(valid_keys, plugin_config)
|
183
|
+
schema.expect_enabled_config(CONFIG_KEY, plugin_config[:enabled])
|
184
|
+
end
|
185
|
+
|
186
|
+
# This method is responsible for transforming the translations. The
|
187
|
+
# translations you'll receive may be already be filtered by other plugins
|
188
|
+
# and by the default filtering itself. If you need to access the original
|
189
|
+
# translations, use `I18nJS.translations`.
|
190
|
+
#
|
191
|
+
# Make sure you always check whether your plugin is active before
|
192
|
+
# transforming translations; otherwise, opting out transformation won't be
|
193
|
+
# possible.
|
194
|
+
def self.transform(translations:, config:)
|
195
|
+
return translations unless config.dig(CONFIG_KEY, :enabled)
|
196
|
+
|
197
|
+
translations_glob = Glob.new(translations)
|
198
|
+
translations_glob << "*"
|
199
|
+
|
200
|
+
mapping = translations.keys.each_with_object({}) do |locale, buffer|
|
201
|
+
buffer[locale] = Glob.new(translations[locale]).tap do |glob|
|
202
|
+
glob << "*"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
default_locale = I18n.default_locale
|
207
|
+
default_locale_glob = mapping.delete(default_locale)
|
208
|
+
default_locale_paths = default_locale_glob.paths
|
209
|
+
|
210
|
+
mapping.each do |locale, glob|
|
211
|
+
missing_keys = default_locale_paths - glob.paths
|
212
|
+
|
213
|
+
missing_keys.each do |key|
|
214
|
+
components = key.split(".").map(&:to_sym)
|
215
|
+
fallback_translation = translations.dig(default_locale, *components)
|
216
|
+
|
217
|
+
next unless fallback_translation
|
218
|
+
|
219
|
+
translations_glob.set([locale, key].join("."), fallback_translation)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
translations_glob.to_h
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
I18nJS.register_plugin(EmbedFallbackTranslationsPlugin)
|
228
|
+
end
|
229
|
+
```
|
230
|
+
|
231
|
+
To distribute this plugin, you need to create a gem package that matches the
|
232
|
+
pattern `i18n-js/*_plugin.rb`. You can test whether your plugin will be found by
|
233
|
+
installing your gem, opening a iRB session and running
|
234
|
+
`Gem.find_files("i18n-js/*_plugin.rb")`. If your plugin is not listed, then you
|
235
|
+
need to double check your gem load path and see why the file is not being
|
236
|
+
loaded.
|
237
|
+
|
108
238
|
### Listing missing translations
|
109
239
|
|
110
|
-
To list missing and extraneous translations, you can use
|
111
|
-
command will load your translations similarly to
|
112
|
-
will output the list of keys that don't have a
|
113
|
-
default locale. Here's an example:
|
240
|
+
To list missing and extraneous translations, you can use
|
241
|
+
`i18n lint:translations`. This command will load your translations similarly to
|
242
|
+
how `i18n export` does, but will output the list of keys that don't have a
|
243
|
+
matching translation against the default locale. Here's an example:
|
114
244
|
|
115
|
-

|
116
246
|
|
117
247
|
This command will exist with status 1 whenever there are missing translations.
|
118
248
|
This way you can use it as a CI linting.
|
@@ -133,7 +263,7 @@ translations:
|
|
133
263
|
patterns:
|
134
264
|
- "*"
|
135
265
|
|
136
|
-
|
266
|
+
lint_translations:
|
137
267
|
ignore:
|
138
268
|
- en.mailer.login.subject
|
139
269
|
- en.mailer.login.body
|
@@ -145,6 +275,82 @@ check:
|
|
145
275
|
> accepts the full translation scope, rather than accepting a pattern like
|
146
276
|
> `pt.ignored.scope.*`.
|
147
277
|
|
278
|
+
### Linting your JavaScript/TypeScript files
|
279
|
+
|
280
|
+
To lint your script files and check for missing translations (which can signal
|
281
|
+
that you're either using wrong scopes or forgot to add the translation), use
|
282
|
+
`i18n lint:scripts`. This command will parse your JavaScript/TypeScript files
|
283
|
+
and extract all scopes being used. This command requires a Node.js runtime. You
|
284
|
+
can either specify one via `--node-path`, or let the plugin infer a binary from
|
285
|
+
your `$PATH`.
|
286
|
+
|
287
|
+
The comparison will be made against the export JSON files, which means it'll
|
288
|
+
consider transformations performed by plugins (e.g. the output files may be
|
289
|
+
affected by `embed_fallback_translations` plugin).
|
290
|
+
|
291
|
+
The translations that will be extract must be called as one of the following
|
292
|
+
ways:
|
293
|
+
|
294
|
+
- `i18n.t(scope, options)`
|
295
|
+
- `i18n.translate(scope, options)`
|
296
|
+
- `t(scope, options)`
|
297
|
+
|
298
|
+
Notice that only literal strings can be used, as in `i18n.t("message")`. If
|
299
|
+
you're using dynamic scoping through variables (e.g.
|
300
|
+
`const scope = "message"; i18n.t(scope)`), they will be skipped.
|
301
|
+
|
302
|
+
```console
|
303
|
+
$ i18n lint:scripts --config test/config/lint.yml --require test/config/require.rb
|
304
|
+
=> Config file: "test/config/lint.yml"
|
305
|
+
=> Require file: "test/config/require.rb"
|
306
|
+
=> Node: "/Users/fnando/.asdf/shims/node"
|
307
|
+
=> Available locales: [:en, :es, :pt]
|
308
|
+
=> Patterns: ["!(node_modules)/**/*.js", "!(node_modules)/**/*.ts", "!(node_modules)/**/*.jsx", "!(node_modules)/**/*.tsx"]
|
309
|
+
=> 9 translations, 11 missing, 4 ignored
|
310
|
+
- test/scripts/lint/file.js:1:1: en.js.missing
|
311
|
+
- test/scripts/lint/file.js:1:1: es.js.missing
|
312
|
+
- test/scripts/lint/file.js:1:1: pt.js.missing
|
313
|
+
- test/scripts/lint/file.js:2:8: en.base.js.missing
|
314
|
+
- test/scripts/lint/file.js:2:8: es.base.js.missing
|
315
|
+
- test/scripts/lint/file.js:2:8: pt.base.js.missing
|
316
|
+
- test/scripts/lint/file.js:4:8: en.js.missing
|
317
|
+
- test/scripts/lint/file.js:4:8: es.js.missing
|
318
|
+
- test/scripts/lint/file.js:4:8: pt.js.missing
|
319
|
+
- test/scripts/lint/file.js:6:1: en.another_ignore_scope
|
320
|
+
- test/scripts/lint/file.js:6:1: es.another_ignore_scope
|
321
|
+
```
|
322
|
+
|
323
|
+
This command will list all locales and their missing translations. Avoid listing
|
324
|
+
a particular translation, you can set `lint.ignore` on your config file.
|
325
|
+
|
326
|
+
```yaml
|
327
|
+
---
|
328
|
+
translations:
|
329
|
+
- file: app/frontend/translations.json
|
330
|
+
patterns:
|
331
|
+
- "*"
|
332
|
+
|
333
|
+
lint_scripts:
|
334
|
+
ignore:
|
335
|
+
- ignore_scope # will ignore this scope on all languages
|
336
|
+
- pt.another_ignore_scope # will ignore this scope only on `pt`
|
337
|
+
```
|
338
|
+
|
339
|
+
You can also set the patterns that will be looked up. By default, it scans all
|
340
|
+
JavaScript and TypeScript files that don't live on `node_modules`.
|
341
|
+
|
342
|
+
```yaml
|
343
|
+
---
|
344
|
+
translations:
|
345
|
+
- file: app/frontend/translations.json
|
346
|
+
patterns:
|
347
|
+
- "*"
|
348
|
+
|
349
|
+
lint:
|
350
|
+
patterns:
|
351
|
+
- "app/assets/**/*.ts"
|
352
|
+
```
|
353
|
+
|
148
354
|
## Automatically export translations
|
149
355
|
|
150
356
|
### Using [watchman](https://facebook.github.io/watchman/)
|
@@ -230,7 +436,10 @@ The code above will watch for changes based on `config/i18n.yml` and
|
|
230
436
|
|
231
437
|
- `config_file` - i18n-js configuration file
|
232
438
|
- `locales_dir` - one or multiple directories to watch for locales changes
|
233
|
-
- `options` - passed directly to
|
439
|
+
- `options` - passed directly to
|
440
|
+
[listen](https://github.com/guard/listen/#options)
|
441
|
+
- `run_on_start` - export files on start. Defaults to `true`. When disabled,
|
442
|
+
files will be exported only when there are file changes.
|
234
443
|
|
235
444
|
Example:
|
236
445
|
|
@@ -238,7 +447,8 @@ Example:
|
|
238
447
|
I18nJS.listen(
|
239
448
|
config_file: "config/i18n.yml",
|
240
449
|
locales_dir: ["config/locales", "app/views"],
|
241
|
-
options: {only: %r{.yml$}
|
450
|
+
options: {only: %r{.yml$}},
|
451
|
+
run_on_start: false
|
242
452
|
)
|
243
453
|
```
|
244
454
|
|
data/bin/pack
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "optparse"
|
5
|
+
require_relative "../lib/i18n-js/version"
|
6
|
+
|
7
|
+
def write_file(path, contents)
|
8
|
+
File.open(File.expand_path(path), "w") do |io|
|
9
|
+
io << contents
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
changelog_path = "./CHANGELOG.md"
|
14
|
+
version_path = "./lib/i18n-js/version.rb"
|
15
|
+
|
16
|
+
version = nil
|
17
|
+
segments = I18nJS::VERSION.split(".")
|
18
|
+
major, minor, patch = *segments.take(3).map(&:to_i)
|
19
|
+
pre = segments[4].to_s
|
20
|
+
pre_version = pre.gsub(/[^\d]/m, "").to_i
|
21
|
+
date = Time.now.strftime("%b %d, %Y")
|
22
|
+
release = false
|
23
|
+
alpha = false
|
24
|
+
|
25
|
+
OptionParser.new do |opts|
|
26
|
+
opts.on("--major") do
|
27
|
+
version = "#{major + 1}.0.0"
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on("--minor") do
|
31
|
+
version = "#{major}.#{minor + 1}.0"
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("--patch") do
|
35
|
+
version = "#{major}.#{minor}.#{patch + 1}"
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("--alpha") do
|
39
|
+
alpha = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("--release") do
|
43
|
+
release = true
|
44
|
+
end
|
45
|
+
end.parse!
|
46
|
+
|
47
|
+
version = "#{version}.alpha#{pre_version + 1}" if alpha
|
48
|
+
|
49
|
+
unless version
|
50
|
+
puts "ERROR: You need to use either one of: --major, --minor, --patch"
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
|
54
|
+
puts "=> Current version: #{I18nJS::VERSION}"
|
55
|
+
puts "=> Next version: #{version}"
|
56
|
+
|
57
|
+
system "yarn", "install"
|
58
|
+
system "yarn", "compile"
|
59
|
+
|
60
|
+
write_file changelog_path,
|
61
|
+
File.read(changelog_path)
|
62
|
+
.gsub("Unreleased", "v#{version} - #{date}")
|
63
|
+
|
64
|
+
puts "=> Updated #{changelog_path}"
|
65
|
+
|
66
|
+
write_file version_path,
|
67
|
+
File.read(version_path)
|
68
|
+
.gsub(/VERSION = ".*?"/, %[VERSION = "#{version}"])
|
69
|
+
|
70
|
+
puts "=> Updated #{version_path}"
|
71
|
+
|
72
|
+
if release
|
73
|
+
system "git", "add", changelog_path, version_path
|
74
|
+
system "git", "commit", "-m", "Bump up version (v#{version})"
|
75
|
+
system "git", "tag", "v#{version}"
|
76
|
+
end
|
77
|
+
|
78
|
+
system "rake", "build"
|
79
|
+
system "git", "checkout", changelog_path, version_path unless release
|
data/i18n-js.gemspec
CHANGED
@@ -31,16 +31,19 @@ Gem::Specification.new do |spec|
|
|
31
31
|
.reject {|f| f.match(%r{^(test|spec|features|images)/}) }
|
32
32
|
end
|
33
33
|
|
34
|
+
spec.files << "lib/i18n-js/lint.js"
|
35
|
+
|
34
36
|
spec.bindir = "exe"
|
35
37
|
spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
|
36
38
|
spec.require_paths = ["lib"]
|
37
39
|
|
38
|
-
spec.add_dependency "glob"
|
40
|
+
spec.add_dependency "glob", ">= 0.4.0"
|
39
41
|
spec.add_dependency "i18n"
|
40
42
|
|
41
43
|
spec.add_development_dependency "activesupport"
|
42
44
|
spec.add_development_dependency "minitest"
|
43
45
|
spec.add_development_dependency "minitest-utils"
|
46
|
+
spec.add_development_dependency "mocha"
|
44
47
|
spec.add_development_dependency "pry-meta"
|
45
48
|
spec.add_development_dependency "rake"
|
46
49
|
spec.add_development_dependency "rubocop"
|
@@ -1,156 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "benchmark"
|
4
|
-
|
5
3
|
module I18nJS
|
6
4
|
class CLI
|
7
|
-
class CheckCommand <
|
5
|
+
class CheckCommand < LintTranslationsCommand
|
8
6
|
command_name "check"
|
9
|
-
description "Check for missing translations"
|
10
|
-
|
11
|
-
parse do |opts|
|
12
|
-
opts.banner = "Usage: i18n #{name} [options]"
|
13
|
-
|
14
|
-
opts.on(
|
15
|
-
"-cCONFIG_FILE",
|
16
|
-
"--config=CONFIG_FILE",
|
17
|
-
"The configuration file that will be used"
|
18
|
-
) do |config_file|
|
19
|
-
options[:config_file] = config_file
|
20
|
-
end
|
21
|
-
|
22
|
-
opts.on(
|
23
|
-
"-rREQUIRE_FILE",
|
24
|
-
"--require=REQUIRE_FILE",
|
25
|
-
"A Ruby file that must be loaded"
|
26
|
-
) do |require_file|
|
27
|
-
options[:require_file] = require_file
|
28
|
-
end
|
29
|
-
|
30
|
-
opts.on(
|
31
|
-
"--[no-]color",
|
32
|
-
"Force colored output"
|
33
|
-
) do |colored|
|
34
|
-
options[:colored] = colored
|
35
|
-
end
|
36
|
-
|
37
|
-
opts.on("-h", "--help", "Prints this help") do
|
38
|
-
ui.exit_with opts.to_s
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
command do
|
43
|
-
set_defaults!
|
44
|
-
ui.colored = options[:colored]
|
45
|
-
|
46
|
-
unless options[:config_file]
|
47
|
-
ui.fail_with("=> ERROR: you need to specify the config file")
|
48
|
-
end
|
49
|
-
|
50
|
-
ui.stdout_print("=> Config file:", options[:config_file].inspect)
|
51
|
-
config_file = File.expand_path(options[:config_file])
|
52
|
-
|
53
|
-
if options[:require_file]
|
54
|
-
ui.stdout_print("=> Require file:", options[:require_file].inspect)
|
55
|
-
require_file = File.expand_path(options[:require_file])
|
56
|
-
end
|
57
|
-
|
58
|
-
unless File.file?(config_file)
|
59
|
-
ui.fail_with(
|
60
|
-
"=> ERROR: config file doesn't exist at",
|
61
|
-
config_file.inspect
|
62
|
-
)
|
63
|
-
end
|
64
|
-
|
65
|
-
if require_file && !File.file?(require_file)
|
66
|
-
ui.fail_with(
|
67
|
-
"=> ERROR: require file doesn't exist at",
|
68
|
-
require_file.inspect
|
69
|
-
)
|
70
|
-
end
|
71
|
-
|
72
|
-
config = Glob::SymbolizeKeys.call(YAML.load_file(config_file))
|
73
|
-
Schema.validate!(config)
|
74
|
-
|
75
|
-
load_require_file!(require_file) if require_file
|
76
|
-
|
77
|
-
default_locale = I18n.default_locale
|
78
|
-
available_locales = I18n.available_locales
|
79
|
-
ignored_keys = config.dig(:check, :ignore) || []
|
80
|
-
|
81
|
-
mapping = available_locales.each_with_object({}) do |locale, buffer|
|
82
|
-
buffer[locale] =
|
83
|
-
Glob::Map.call(Glob.filter(I18nJS.translations, ["#{locale}.*"]))
|
84
|
-
.map {|key| key.gsub(/^.*?\./, "") }
|
85
|
-
end
|
86
|
-
|
87
|
-
default_locale_keys = mapping.delete(default_locale)
|
88
|
-
|
89
|
-
if ignored_keys.any?
|
90
|
-
ui.stdout_print "=> Check #{options[:config_file].inspect} for " \
|
91
|
-
"ignored keys."
|
92
|
-
end
|
93
|
-
|
94
|
-
ui.stdout_print "=> #{default_locale}: #{default_locale_keys.size} " \
|
95
|
-
"translations"
|
96
|
-
|
97
|
-
total_missing_count = 0
|
98
|
-
|
99
|
-
mapping.each do |locale, partial_keys|
|
100
|
-
ignored_count = 0
|
101
|
-
|
102
|
-
# Compute list of filtered keys (i.e. keys not ignored)
|
103
|
-
filtered_keys = partial_keys.reject do |key|
|
104
|
-
key = "#{locale}.#{key}"
|
105
|
-
|
106
|
-
ignored = ignored_keys.include?(key)
|
107
|
-
ignored_count += 1 if ignored
|
108
|
-
ignored
|
109
|
-
end
|
110
|
-
|
111
|
-
extraneous = (partial_keys - default_locale_keys).reject do |key|
|
112
|
-
key = "#{locale}.#{key}"
|
113
|
-
ignored = ignored_keys.include?(key)
|
114
|
-
ignored_count += 1 if ignored
|
115
|
-
ignored
|
116
|
-
end
|
117
|
-
|
118
|
-
missing = (default_locale_keys - (filtered_keys - extraneous))
|
119
|
-
.reject {|key| ignored_keys.include?("#{locale}.#{key}") }
|
120
|
-
|
121
|
-
ignored_count += extraneous.size
|
122
|
-
total_missing_count += missing.size
|
123
|
-
|
124
|
-
ui.stdout_print "=> #{locale}: #{missing.size} missing, " \
|
125
|
-
"#{extraneous.size} extraneous, " \
|
126
|
-
"#{ignored_count} ignored"
|
127
|
-
|
128
|
-
all_keys = (default_locale_keys + extraneous + missing).uniq.sort
|
129
|
-
|
130
|
-
all_keys.each do |key|
|
131
|
-
next if ignored_keys.include?("#{locale}.#{key}")
|
132
|
-
|
133
|
-
label = if extraneous.include?(key)
|
134
|
-
ui.yellow("extraneous")
|
135
|
-
elsif missing.include?(key)
|
136
|
-
ui.red("missing")
|
137
|
-
else
|
138
|
-
next
|
139
|
-
end
|
140
|
-
|
141
|
-
ui.stdout_print(" - #{locale}.#{key} (#{label})")
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
exit(1) if total_missing_count.nonzero?
|
146
|
-
end
|
147
|
-
|
148
|
-
private def set_defaults!
|
149
|
-
config_file = "./config/i18n.yml"
|
150
|
-
require_file = "./config/environment.rb"
|
7
|
+
description "Check for missing translations based on the default " \
|
8
|
+
"locale (DEPRECATED: Use `i18n lint:translations` instead)"
|
151
9
|
|
152
|
-
|
153
|
-
|
10
|
+
def command
|
11
|
+
ui.stderr_print "=> WARNING: `i18n check` has been deprecated in " \
|
12
|
+
"favor of `i18n lint:translations`"
|
13
|
+
super
|
154
14
|
end
|
155
15
|
end
|
156
16
|
end
|
data/lib/i18n-js/cli/command.rb
CHANGED
@@ -39,6 +39,16 @@ module I18nJS
|
|
39
39
|
@options ||= {}
|
40
40
|
end
|
41
41
|
|
42
|
+
private def load_config_file(config_file)
|
43
|
+
config = Glob::SymbolizeKeys.call(YAML.load_file(config_file))
|
44
|
+
|
45
|
+
if config.key?(:check)
|
46
|
+
config[:lint_translations] ||= config.delete(:check)
|
47
|
+
end
|
48
|
+
|
49
|
+
config
|
50
|
+
end
|
51
|
+
|
42
52
|
private def load_require_file!(require_file)
|
43
53
|
require_without_warnings(require_file)
|
44
54
|
rescue Exception => error # rubocop:disable Lint/RescueException
|