i18n-tasks 0.9.33 → 1.0.12
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/README.md +28 -11
- data/config/locales/en.yml +5 -3
- data/config/locales/ru.yml +1 -0
- data/i18n-tasks.gemspec +12 -6
- data/lib/i18n/tasks/base_task.rb +2 -1
- data/lib/i18n/tasks/cli.rb +27 -17
- data/lib/i18n/tasks/command/commander.rb +1 -0
- data/lib/i18n/tasks/command/commands/data.rb +8 -6
- data/lib/i18n/tasks/command/commands/eq_base.rb +2 -2
- data/lib/i18n/tasks/command/commands/health.rb +4 -3
- data/lib/i18n/tasks/command/commands/interpolations.rb +1 -1
- data/lib/i18n/tasks/command/commands/meta.rb +1 -1
- data/lib/i18n/tasks/command/commands/missing.rb +22 -9
- data/lib/i18n/tasks/command/commands/tree.rb +8 -6
- data/lib/i18n/tasks/command/commands/usages.rb +5 -4
- data/lib/i18n/tasks/command/dsl.rb +4 -4
- data/lib/i18n/tasks/command/option_parsers/enum.rb +2 -0
- data/lib/i18n/tasks/command/option_parsers/locale.rb +2 -1
- data/lib/i18n/tasks/command/options/common.rb +5 -0
- data/lib/i18n/tasks/command/options/data.rb +4 -1
- data/lib/i18n/tasks/command/options/locales.rb +5 -5
- data/lib/i18n/tasks/concurrent/cached_value.rb +2 -2
- data/lib/i18n/tasks/configuration.rb +17 -10
- data/lib/i18n/tasks/console_context.rb +1 -1
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +9 -2
- data/lib/i18n/tasks/data/file_formats.rb +3 -1
- data/lib/i18n/tasks/data/file_system_base.rb +7 -6
- data/lib/i18n/tasks/data/router/conservative_router.rb +2 -1
- data/lib/i18n/tasks/data/router/pattern_router.rb +3 -1
- data/lib/i18n/tasks/data/tree/node.rb +6 -3
- data/lib/i18n/tasks/data/tree/nodes.rb +6 -7
- data/lib/i18n/tasks/data/tree/siblings.rb +10 -4
- data/lib/i18n/tasks/data/tree/traversal.rb +34 -11
- data/lib/i18n/tasks/html_keys.rb +4 -6
- data/lib/i18n/tasks/ignore_keys.rb +4 -3
- data/lib/i18n/tasks/interpolations.rb +10 -4
- data/lib/i18n/tasks/key_pattern_matching.rb +3 -2
- data/lib/i18n/tasks/locale_pathname.rb +1 -1
- data/lib/i18n/tasks/missing_keys.rb +4 -0
- data/lib/i18n/tasks/plural_keys.rb +5 -6
- data/lib/i18n/tasks/references.rb +4 -2
- data/lib/i18n/tasks/reports/base.rb +4 -3
- data/lib/i18n/tasks/reports/terminal.rb +8 -6
- data/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb +118 -0
- data/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb +91 -0
- data/lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb +69 -0
- data/lib/i18n/tasks/scanners/erb_ast_processor.rb +74 -0
- data/lib/i18n/tasks/scanners/erb_ast_scanner.rb +48 -0
- data/lib/i18n/tasks/scanners/file_scanner.rb +4 -3
- data/lib/i18n/tasks/scanners/files/caching_file_finder.rb +0 -3
- data/lib/i18n/tasks/scanners/files/file_finder.rb +3 -2
- data/lib/i18n/tasks/scanners/local_ruby_parser.rb +85 -0
- data/lib/i18n/tasks/scanners/occurrence_from_position.rb +3 -3
- data/lib/i18n/tasks/scanners/pattern_mapper.rb +1 -1
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +8 -5
- data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +4 -2
- data/lib/i18n/tasks/scanners/relative_keys.rb +19 -4
- data/lib/i18n/tasks/scanners/results/occurrence.rb +17 -1
- data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +9 -34
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +91 -154
- data/lib/i18n/tasks/scanners/ruby_key_literals.rb +4 -4
- data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +2 -0
- data/lib/i18n/tasks/split_key.rb +3 -1
- data/lib/i18n/tasks/string_interpolation.rb +1 -0
- data/lib/i18n/tasks/translation.rb +3 -3
- data/lib/i18n/tasks/translators/base_translator.rb +5 -3
- data/lib/i18n/tasks/translators/deepl_translator.rb +10 -2
- data/lib/i18n/tasks/translators/google_translator.rb +2 -0
- data/lib/i18n/tasks/translators/yandex_translator.rb +2 -0
- data/lib/i18n/tasks/used_keys.rb +21 -14
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/i18n/tasks.rb +17 -7
- data/templates/config/i18n-tasks.yml +21 -1
- metadata +44 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e77c4c19ca040adf1879259e8ad1d963e181eb1682e97ffa99f5b09d1b59e803
|
4
|
+
data.tar.gz: cc5ed0a16159c8f14a9006ab3908cfdc93b5dbc338603d54846d0ae4f84abb80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 978d87d9e73dc5f4e183a7fdebabeec9864c3b1ba530f722bc3a80428b1c422eeda55f2bbd2b7ec8c1a67649ee128db4ae9ab0fc24db4b8cf386f7740b0f34d4
|
7
|
+
data.tar.gz: 46a0b0cbb262c17aaf1195293edd1fc04d665dd85d5f8d1b399f1ae565db2eb99e3e281fb1a34a5cb3629aadee2c2b279642d8beca623814fcd9b75c0c502b5e
|
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
# i18n-tasks [![Build Status][badge-
|
1
|
+
# i18n-tasks [![Build Status][badge-ci]][ci] [![Coverage Status][badge-coverage]][coverage] [](https://gitter.im/glebm/i18n-tasks?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
2
|
+
|
3
|
+
[](https://stand-with-ukraine.pp.ua/)
|
2
4
|
|
3
5
|
i18n-tasks helps you find and manage missing and unused translations.
|
4
6
|
|
@@ -22,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
|
|
22
24
|
Add i18n-tasks to the Gemfile:
|
23
25
|
|
24
26
|
```ruby
|
25
|
-
gem 'i18n-tasks', '~> 0.
|
27
|
+
gem 'i18n-tasks', '~> 1.0.12'
|
26
28
|
```
|
27
29
|
|
28
30
|
Copy the default [configuration file](#configuration):
|
@@ -212,7 +214,7 @@ $ i18n-tasks missing -f yaml fr | i18n-tasks tree-set-value 'TRME %{value}' | i1
|
|
212
214
|
$ i18n-tasks unused -f yaml | i18n-tasks data-remove
|
213
215
|
```
|
214
216
|
|
215
|
-
Remove all keys
|
217
|
+
Remove all keys from `fr` that do not exist in `en`. Do not change `en`:
|
216
218
|
```console
|
217
219
|
$ i18n-tasks missing -t diff -f yaml en | i18n-tasks tree-mv en fr | i18n-tasks data-remove
|
218
220
|
```
|
@@ -221,7 +223,7 @@ See the full list of tasks with `i18n-tasks --help`.
|
|
221
223
|
|
222
224
|
### Features and limitations
|
223
225
|
|
224
|
-
`i18n-tasks` uses an AST scanner for `.rb` files, and a regexp-based scanner for other files, such as `.haml`.
|
226
|
+
`i18n-tasks` uses an AST scanner for `.rb` and `.html.erb` files, and a regexp-based scanner for other files, such as `.haml`.
|
225
227
|
|
226
228
|
#### Relative keys
|
227
229
|
|
@@ -353,7 +355,7 @@ If you have implemented a custom adapter please share it on [the wiki][wiki].
|
|
353
355
|
|
354
356
|
### Usage search
|
355
357
|
|
356
|
-
i18n-tasks uses an AST scanner for `.rb` files, and a regexp scanner for all other files.
|
358
|
+
i18n-tasks uses an AST scanner for `.rb` and `.html.erb` files, and a regexp scanner for all other files.
|
357
359
|
New scanners can be added easily: please refer to [this example](https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example).
|
358
360
|
|
359
361
|
See the `search` section in the [config file][config] for all available configuration options.
|
@@ -385,7 +387,7 @@ See the [config file][config] to find out more.
|
|
385
387
|
Where this key is depends on your Google API console:
|
386
388
|
|
387
389
|
* Old console: API Access -> Simple API Access -> Key for server apps.
|
388
|
-
* New console:
|
390
|
+
* New console: Nav Menu -> APIs & Services -> Credentials -> Create Credentials -> API Keys -> Restrict Key -> Cloud Translation API
|
389
391
|
|
390
392
|
In both cases, you may need to create the key if it doesn't exist.
|
391
393
|
|
@@ -405,7 +407,9 @@ translation:
|
|
405
407
|
```yaml
|
406
408
|
# config/i18n-tasks.yml
|
407
409
|
translation:
|
408
|
-
deepl_api_key: <
|
410
|
+
deepl_api_key: <DeepL Pro API key>
|
411
|
+
deepl_host: <optional>
|
412
|
+
deepl_version: <optional>
|
409
413
|
```
|
410
414
|
|
411
415
|
<a name="yandex-translation-config"></a>
|
@@ -432,15 +436,28 @@ See [i18n-tasks wiki: CSV import and export tasks](https://github.com/glebm/i18n
|
|
432
436
|
Tasks that come with the gem are defined in [lib/i18n/tasks/command/commands](lib/i18n/tasks/command/commands).
|
433
437
|
Custom tasks can be added easily, see the examples [on the wiki](https://github.com/glebm/i18n-tasks/wiki#custom-tasks).
|
434
438
|
|
439
|
+
# Development
|
440
|
+
|
441
|
+
- Install dependencies using `bundle install`
|
442
|
+
- Run tests using `bundle exec rspec`
|
443
|
+
- Install [Overcommit](overcommit) by running `overcommit --install`
|
444
|
+
|
445
|
+
## Skip Overcommit-hooks
|
446
|
+
|
447
|
+
- `SKIP=RuboCop git commit`
|
448
|
+
- `OVERCOMMIT_DISABLE=1 git commit`
|
449
|
+
|
450
|
+
|
435
451
|
[MIT license]: /LICENSE.txt
|
436
|
-
[
|
437
|
-
[badge-
|
452
|
+
[ci]: https://github.com/glebm/i18n-tasks/actions/workflows/tests.yml
|
453
|
+
[badge-ci]: https://github.com/glebm/i18n-tasks/actions/workflows/tests.yml/badge.svg
|
438
454
|
[coverage]: https://codeclimate.com/github/glebm/i18n-tasks
|
439
455
|
[badge-coverage]: https://api.codeclimate.com/v1/badges/5d173e90ada8df07cedc/test_coverage
|
440
|
-
[config]: https://github.com/glebm/i18n-tasks/blob/
|
456
|
+
[config]: https://github.com/glebm/i18n-tasks/blob/main/templates/config/i18n-tasks.yml
|
441
457
|
[wiki]: https://github.com/glebm/i18n-tasks/wiki "i18n-tasks wiki"
|
442
458
|
[i18n-gem]: https://github.com/svenfuchs/i18n "svenfuchs/i18n on Github"
|
443
459
|
[screenshot-i18n-tasks]: https://i.imgur.com/XZBd8l7.png "i18n-tasks screenshot"
|
444
460
|
[screenshot-find]: https://i.imgur.com/VxBrSfY.png "i18n-tasks find output screenshot"
|
445
|
-
[adapter-example]: https://github.com/glebm/i18n-tasks/blob/
|
461
|
+
[adapter-example]: https://github.com/glebm/i18n-tasks/blob/main/lib/i18n/tasks/data/file_system_base.rb
|
446
462
|
[custom-scanner-docs]: https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example
|
463
|
+
[overcommit]: https://github.com/sds/overcommit#installation
|
data/config/locales/en.yml
CHANGED
@@ -11,6 +11,7 @@ en:
|
|
11
11
|
desc:
|
12
12
|
all_locales: Do not expect key patterns to start with a locale, instead apply them to all
|
13
13
|
locales implicitly.
|
14
|
+
config: Override config location
|
14
15
|
confirm: Confirm automatically
|
15
16
|
data_format: 'Data format: %{valid_text}.'
|
16
17
|
keep_order: Keep the order of the keys
|
@@ -32,7 +33,7 @@ en:
|
|
32
33
|
Value. Interpolates: %{value}, %{human_key}, %{key}, %{default}, %{value_or_human_key},
|
33
34
|
%{value_or_default_or_human_key}
|
34
35
|
desc:
|
35
|
-
add_missing: add missing keys to locale data
|
36
|
+
add_missing: add missing keys to locale data, optionally match a pattern
|
36
37
|
check_consistent_interpolations: verify that all translations use correct interpolation variables
|
37
38
|
check_normalized: verify that all translation data is normalized
|
38
39
|
config: display i18n-tasks configuration
|
@@ -45,12 +46,13 @@ en:
|
|
45
46
|
gem_path: show path to the gem
|
46
47
|
health: is everything OK?
|
47
48
|
irb: start REPL session within i18n-tasks context
|
48
|
-
missing: show missing translations
|
49
|
+
missing: show missing translations, optionally match a pattern
|
49
50
|
mv: rename/merge the keys in locale data that match the given pattern
|
50
51
|
normalize: 'normalize translation data: sort and move to the right files'
|
51
52
|
remove_unused: remove unused keys
|
52
53
|
rm: remove the keys in locale data that match the given pattern
|
53
|
-
translate_missing: translate missing keys with Google Translate or DeepL Pro
|
54
|
+
translate_missing: translate missing keys with Google Translate or DeepL Pro, optionally match
|
55
|
+
a pattern
|
54
56
|
tree_convert: convert tree between formats
|
55
57
|
tree_filter: filter tree by key pattern
|
56
58
|
tree_merge: merge trees
|
data/config/locales/ru.yml
CHANGED
@@ -8,6 +8,7 @@ ru:
|
|
8
8
|
default_text: 'По умолчанию: %{value}'
|
9
9
|
desc:
|
10
10
|
all_locales: Не ожидать, что маски ключа начинаются с локали. Применять маски ко всем локалям.
|
11
|
+
config: Путь к файлу конфигурации
|
11
12
|
confirm: Подтвердить автоматом
|
12
13
|
data_format: 'Формат данных: %{valid_text}.'
|
13
14
|
keep_order: Keep the order of the keys
|
data/i18n-tasks.gemspec
CHANGED
@@ -16,16 +16,21 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength
|
|
16
16
|
|
17
17
|
It analyses code statically for key usages, such as `I18n.t('some.key')`, in order to report keys that are missing or unused,
|
18
18
|
pre-fill missing keys (optionally from Google Translate), and remove unused keys.
|
19
|
-
TEXT
|
19
|
+
TEXT
|
20
20
|
s.post_install_message = <<~TEXT
|
21
21
|
# Install default configuration:
|
22
22
|
cp $(bundle exec i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
|
23
23
|
# Add an RSpec for missing and unused keys:
|
24
24
|
cp $(bundle exec i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
|
25
|
-
|
25
|
+
# Or for minitest:
|
26
|
+
cp $(bundle exec i18n-tasks gem-path)/templates/minitest/i18n_test.rb test/
|
27
|
+
TEXT
|
26
28
|
s.homepage = 'https://github.com/glebm/i18n-tasks'
|
27
|
-
s.metadata = {
|
28
|
-
|
29
|
+
s.metadata = {
|
30
|
+
'issue_tracker' => 'https://github.com/glebm/i18n-tasks',
|
31
|
+
'rubygems_mfa_required' => 'true'
|
32
|
+
}
|
33
|
+
s.required_ruby_version = '>= 2.6', '< 4.0'
|
29
34
|
|
30
35
|
s.files = `git ls-files`.split($/)
|
31
36
|
s.files -= s.files.grep(%r{^(doc/|\.|spec/)}) + %w[CHANGES.md config/i18n-tasks.yml Gemfile]
|
@@ -35,6 +40,7 @@ TEXT
|
|
35
40
|
|
36
41
|
s.add_dependency 'activesupport', '>= 4.0.2'
|
37
42
|
s.add_dependency 'ast', '>= 2.1.0'
|
43
|
+
s.add_dependency 'better_html', '>= 1.0', '< 3.0'
|
38
44
|
s.add_dependency 'erubi'
|
39
45
|
s.add_dependency 'highline', '>= 2.0.0'
|
40
46
|
s.add_dependency 'i18n'
|
@@ -42,11 +48,11 @@ TEXT
|
|
42
48
|
s.add_dependency 'rails-i18n'
|
43
49
|
s.add_dependency 'rainbow', '>= 2.2.2', '< 4.0'
|
44
50
|
s.add_dependency 'terminal-table', '>= 1.5.1'
|
45
|
-
s.add_development_dependency 'axlsx', '~> 2.0'
|
46
51
|
s.add_development_dependency 'bundler', '~> 2.0', '>= 2.0.1'
|
52
|
+
s.add_development_dependency 'overcommit', '~> 0.58.0'
|
47
53
|
s.add_development_dependency 'rake'
|
48
54
|
s.add_development_dependency 'rspec', '~> 3.3'
|
49
|
-
s.add_development_dependency 'rubocop', '~>
|
55
|
+
s.add_development_dependency 'rubocop', '~> 1.27.0'
|
50
56
|
s.add_development_dependency 'simplecov'
|
51
57
|
s.add_development_dependency 'yard'
|
52
58
|
|
data/lib/i18n/tasks/base_task.rb
CHANGED
data/lib/i18n/tasks/cli.rb
CHANGED
@@ -15,28 +15,38 @@ class I18n::Tasks::CLI
|
|
15
15
|
|
16
16
|
def start(argv)
|
17
17
|
auto_output_coloring do
|
18
|
+
exit 1 if run(argv) == :exit1
|
19
|
+
rescue OptionParser::ParseError => e
|
20
|
+
error e.message, 64
|
21
|
+
rescue I18n::Tasks::CommandError => e
|
18
22
|
begin
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
rescue I18n::Tasks::CommandError => e
|
23
|
-
begin
|
24
|
-
error e.message, 78
|
25
|
-
ensure
|
26
|
-
log_verbose e.backtrace * "\n"
|
27
|
-
end
|
28
|
-
rescue Errno::EPIPE
|
29
|
-
# ignore Errno::EPIPE which is throw when pipe breaks, e.g.:
|
30
|
-
# i18n-tasks missing | head
|
31
|
-
exit 1
|
23
|
+
error e.message, 78
|
24
|
+
ensure
|
25
|
+
log_verbose e.backtrace * "\n"
|
32
26
|
end
|
27
|
+
rescue Errno::EPIPE
|
28
|
+
# ignore Errno::EPIPE which is throw when pipe breaks, e.g.:
|
29
|
+
# i18n-tasks missing | head
|
30
|
+
exit 1
|
33
31
|
end
|
34
32
|
rescue ExecutionError => e
|
35
33
|
exit e.exit_code
|
36
34
|
end
|
37
35
|
|
38
36
|
def run(argv)
|
39
|
-
|
37
|
+
argv.each_with_index do |arg, i|
|
38
|
+
if ['--config', '-c'].include?(arg)
|
39
|
+
_, config_file = argv.slice!(i, 2)
|
40
|
+
if File.exist?(config_file)
|
41
|
+
@config_file = config_file
|
42
|
+
break
|
43
|
+
else
|
44
|
+
error "Config file doesn't exist: #{config_file}", 128
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
I18n.with_locale(base_task(config_file: @config_file).internal_locale) do
|
40
50
|
name, *options = parse!(argv.dup)
|
41
51
|
context.run(name, *options)
|
42
52
|
end
|
@@ -54,8 +64,8 @@ class I18n::Tasks::CLI
|
|
54
64
|
|
55
65
|
private
|
56
66
|
|
57
|
-
def base_task
|
58
|
-
@base_task ||= I18n::Tasks::BaseTask.new
|
67
|
+
def base_task(config_file: nil)
|
68
|
+
@base_task ||= I18n::Tasks::BaseTask.new(config_file: config_file)
|
59
69
|
end
|
60
70
|
|
61
71
|
def parse!(argv)
|
@@ -195,7 +205,7 @@ class I18n::Tasks::CLI
|
|
195
205
|
end
|
196
206
|
end
|
197
207
|
|
198
|
-
def auto_output_coloring(coloring = ENV['I18N_TASKS_COLOR'] ||
|
208
|
+
def auto_output_coloring(coloring = ENV['I18N_TASKS_COLOR'] || $stdout.isatty)
|
199
209
|
coloring_was = Rainbow.enabled
|
200
210
|
Rainbow.enabled = coloring
|
201
211
|
yield
|
@@ -16,6 +16,7 @@ module I18n::Tasks
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def run(name, opts = {})
|
19
|
+
log_stderr "#{Rainbow('#StandWith').bg(:blue)}#{Rainbow('Ukraine').bg(:yellow)}"
|
19
20
|
name = name.to_sym
|
20
21
|
public_name = name.to_s.tr '_', '-'
|
21
22
|
log_verbose "task: #{public_name}(#{opts.map { |k, v| "#{k}: #{v.inspect}" } * ', '})"
|
@@ -12,7 +12,7 @@ module I18n::Tasks
|
|
12
12
|
t('i18n_tasks.cmd.args.desc.pattern_router')
|
13
13
|
|
14
14
|
cmd :normalize,
|
15
|
-
pos:
|
15
|
+
pos: '[locale ...]',
|
16
16
|
desc: t('i18n_tasks.cmd.desc.normalize'),
|
17
17
|
args: %i[locales pattern_router]
|
18
18
|
|
@@ -29,7 +29,7 @@ module I18n::Tasks
|
|
29
29
|
def check_normalized(opt)
|
30
30
|
non_normalized = i18n.non_normalized_paths locales: opt[:locales]
|
31
31
|
terminal_report.check_normalized_results(non_normalized)
|
32
|
-
:
|
32
|
+
:exit1 unless non_normalized.empty?
|
33
33
|
end
|
34
34
|
|
35
35
|
cmd :mv,
|
@@ -37,6 +37,7 @@ module I18n::Tasks
|
|
37
37
|
desc: t('i18n_tasks.cmd.desc.mv')
|
38
38
|
def mv(opt = {})
|
39
39
|
fail CommandError, 'requires FROM_KEY_PATTERN and TO_KEY_PATTERN' if opt[:arguments].size < 2
|
40
|
+
|
40
41
|
from_pattern = opt[:arguments].shift
|
41
42
|
to_pattern = opt[:arguments].shift
|
42
43
|
forest = i18n.data_forest
|
@@ -50,6 +51,7 @@ module I18n::Tasks
|
|
50
51
|
desc: t('i18n_tasks.cmd.desc.rm')
|
51
52
|
def rm(opt = {})
|
52
53
|
fail CommandError, 'requires KEY_PATTERN' if opt[:arguments].empty?
|
54
|
+
|
53
55
|
forest = i18n.data_forest
|
54
56
|
results = opt[:arguments].each_with_object({}) do |key_pattern, h|
|
55
57
|
h.merge! forest.mv_key!(compile_key_pattern(key_pattern), '', root: false)
|
@@ -59,7 +61,7 @@ module I18n::Tasks
|
|
59
61
|
end
|
60
62
|
|
61
63
|
cmd :data,
|
62
|
-
pos:
|
64
|
+
pos: '[locale ...]',
|
63
65
|
desc: t('i18n_tasks.cmd.desc.data'),
|
64
66
|
args: %i[locales out_format]
|
65
67
|
|
@@ -68,7 +70,7 @@ module I18n::Tasks
|
|
68
70
|
end
|
69
71
|
|
70
72
|
cmd :data_merge,
|
71
|
-
pos:
|
73
|
+
pos: '[tree ...]',
|
72
74
|
desc: t('i18n_tasks.cmd.desc.data_merge'),
|
73
75
|
args: %i[data_format nostdin]
|
74
76
|
|
@@ -79,7 +81,7 @@ module I18n::Tasks
|
|
79
81
|
end
|
80
82
|
|
81
83
|
cmd :data_write,
|
82
|
-
pos:
|
84
|
+
pos: '[tree]',
|
83
85
|
desc: t('i18n_tasks.cmd.desc.data_write'),
|
84
86
|
args: %i[data_format nostdin]
|
85
87
|
|
@@ -90,7 +92,7 @@ module I18n::Tasks
|
|
90
92
|
end
|
91
93
|
|
92
94
|
cmd :data_remove,
|
93
|
-
pos:
|
95
|
+
pos: '[tree]',
|
94
96
|
desc: t('i18n_tasks.cmd.desc.data_remove'),
|
95
97
|
args: %i[data_format nostdin]
|
96
98
|
|
@@ -7,14 +7,14 @@ module I18n::Tasks
|
|
7
7
|
include Command::Collection
|
8
8
|
|
9
9
|
cmd :eq_base,
|
10
|
-
pos:
|
10
|
+
pos: '[locale ...]',
|
11
11
|
desc: t('i18n_tasks.cmd.desc.eq_base'),
|
12
12
|
args: %i[locales out_format]
|
13
13
|
|
14
14
|
def eq_base(opt = {})
|
15
15
|
forest = i18n.eq_base_keys(opt)
|
16
16
|
print_forest forest, opt, :eq_base_keys
|
17
|
-
:
|
17
|
+
:exit1 unless forest.empty?
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -7,21 +7,22 @@ module I18n::Tasks
|
|
7
7
|
include Command::Collection
|
8
8
|
|
9
9
|
cmd :health,
|
10
|
-
pos:
|
10
|
+
pos: '[locale ...]',
|
11
11
|
desc: t('i18n_tasks.cmd.desc.health'),
|
12
|
-
args: %i[locales out_format]
|
12
|
+
args: %i[locales out_format config]
|
13
13
|
|
14
14
|
def health(opt = {})
|
15
15
|
forest = i18n.data_forest(opt[:locales])
|
16
16
|
stats = i18n.forest_stats(forest)
|
17
17
|
fail CommandError, t('i18n_tasks.health.no_keys_detected') if stats[:key_count].zero?
|
18
|
+
|
18
19
|
terminal_report.forest_stats forest, stats
|
19
20
|
[
|
20
21
|
missing(**opt),
|
21
22
|
unused(**opt),
|
22
23
|
check_consistent_interpolations(**opt),
|
23
24
|
check_normalized(**opt)
|
24
|
-
].detect { |result| result == :
|
25
|
+
].detect { |result| result == :exit1 }
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
@@ -14,7 +14,7 @@ module I18n::Tasks
|
|
14
14
|
def check_consistent_interpolations(opt = {})
|
15
15
|
forest = i18n.inconsistent_interpolations(**opt.slice(:locales, :base_locale))
|
16
16
|
print_forest forest, opt, :inconsistent_interpolations
|
17
|
-
:
|
17
|
+
:exit1 unless forest.empty?
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -23,23 +23,31 @@ module I18n::Tasks
|
|
23
23
|
)
|
24
24
|
|
25
25
|
cmd :missing,
|
26
|
-
pos:
|
26
|
+
pos: '[locale ...]',
|
27
27
|
desc: t('i18n_tasks.cmd.desc.missing'),
|
28
|
-
args: %i[locales out_format missing_types]
|
28
|
+
args: %i[locales out_format missing_types pattern]
|
29
29
|
|
30
30
|
def missing(opt = {})
|
31
31
|
forest = i18n.missing_keys(**opt.slice(:locales, :base_locale, :types))
|
32
|
+
if opt[:pattern]
|
33
|
+
pattern_re = i18n.compile_key_pattern(opt[:pattern])
|
34
|
+
forest.select_keys! { |full_key, _node| full_key =~ pattern_re }
|
35
|
+
end
|
32
36
|
print_forest forest, opt, :missing_keys
|
33
|
-
:
|
37
|
+
:exit1 unless forest.empty?
|
34
38
|
end
|
35
39
|
|
36
40
|
cmd :translate_missing,
|
37
|
-
pos:
|
41
|
+
pos: '[locale ...]',
|
38
42
|
desc: t('i18n_tasks.cmd.desc.translate_missing'),
|
39
|
-
args: [:locales, :locale_to_translate_from, arg(:out_format).from(1), :translation_backend]
|
43
|
+
args: [:locales, :locale_to_translate_from, arg(:out_format).from(1), :translation_backend, :pattern]
|
40
44
|
|
41
45
|
def translate_missing(opt = {})
|
42
46
|
missing = i18n.missing_diff_forest opt[:locales], opt[:from]
|
47
|
+
if opt[:pattern]
|
48
|
+
pattern_re = i18n.compile_key_pattern(opt[:pattern])
|
49
|
+
missing.select_keys! { |full_key, _node| full_key =~ pattern_re }
|
50
|
+
end
|
43
51
|
translated = i18n.translate_forest missing, from: opt[:from], backend: opt[:backend].to_sym
|
44
52
|
i18n.data.merge! translated
|
45
53
|
log_stderr t('i18n_tasks.translate_missing.translated', count: translated.leaves.count)
|
@@ -47,18 +55,23 @@ module I18n::Tasks
|
|
47
55
|
end
|
48
56
|
|
49
57
|
cmd :add_missing,
|
50
|
-
pos:
|
58
|
+
pos: '[locale ...]',
|
51
59
|
desc: t('i18n_tasks.cmd.desc.add_missing'),
|
52
|
-
args: [:locales, :out_format, arg(:value) + [{ default: '%{value_or_default_or_human_key}' }],
|
60
|
+
args: [:locales, :out_format, :pattern, arg(:value) + [{ default: '%{value_or_default_or_human_key}' }],
|
53
61
|
['--nil-value', 'Set value to nil. Takes precedence over the value argument.']]
|
54
62
|
|
55
|
-
|
56
|
-
|
63
|
+
# Merge base locale first, as this may affect the value for the other locales
|
64
|
+
def add_missing(opt = {})
|
65
|
+
[
|
57
66
|
[i18n.base_locale] & opt[:locales],
|
58
67
|
opt[:locales] - [i18n.base_locale]
|
59
68
|
].reject(&:empty?).each_with_object(i18n.empty_forest) do |locales, added|
|
60
69
|
forest = i18n.missing_keys(locales: locales, **opt.slice(:types, :base_locale))
|
61
70
|
.set_each_value!(opt[:'nil-value'] ? nil : opt[:value])
|
71
|
+
if opt[:pattern]
|
72
|
+
pattern_re = i18n.compile_key_pattern(opt[:pattern])
|
73
|
+
forest.select_keys! { |full_key, _node| full_key =~ pattern_re }
|
74
|
+
end
|
62
75
|
i18n.data.merge! forest
|
63
76
|
added.merge! forest
|
64
77
|
end.tap do |added|
|
@@ -8,7 +8,7 @@ module I18n::Tasks
|
|
8
8
|
include I18n::Tasks::KeyPatternMatching
|
9
9
|
|
10
10
|
cmd :tree_translate,
|
11
|
-
pos:
|
11
|
+
pos: '[tree (or stdin)]',
|
12
12
|
desc: t('i18n_tasks.cmd.desc.tree_translate'),
|
13
13
|
args: [:locale_to_translate_from, arg(:data_format).from(1), :translation_backend]
|
14
14
|
|
@@ -18,7 +18,7 @@ module I18n::Tasks
|
|
18
18
|
end
|
19
19
|
|
20
20
|
cmd :tree_merge,
|
21
|
-
pos:
|
21
|
+
pos: '[[tree] [tree] ... (or stdin)]',
|
22
22
|
desc: t('i18n_tasks.cmd.desc.tree_merge'),
|
23
23
|
args: %i[data_format nostdin]
|
24
24
|
|
@@ -27,7 +27,7 @@ module I18n::Tasks
|
|
27
27
|
end
|
28
28
|
|
29
29
|
cmd :tree_filter,
|
30
|
-
pos:
|
30
|
+
pos: '[pattern] [tree (or stdin)]',
|
31
31
|
desc: t('i18n_tasks.cmd.desc.tree_filter'),
|
32
32
|
args: %i[data_format pattern]
|
33
33
|
|
@@ -52,6 +52,7 @@ module I18n::Tasks
|
|
52
52
|
args: %i[data_format all_locales]
|
53
53
|
def tree_mv(opt = {})
|
54
54
|
fail CommandError, 'requires FROM_KEY_PATTERN and TO_KEY_PATTERN' if opt[:arguments].size < 2
|
55
|
+
|
55
56
|
from_pattern = opt[:arguments].shift
|
56
57
|
to_pattern = opt[:arguments].shift
|
57
58
|
forest = forest_pos_or_stdin!(opt)
|
@@ -60,7 +61,7 @@ module I18n::Tasks
|
|
60
61
|
end
|
61
62
|
|
62
63
|
cmd :tree_subtract,
|
63
|
-
pos:
|
64
|
+
pos: '[[tree] [tree] ... (or stdin)]',
|
64
65
|
desc: t('i18n_tasks.cmd.desc.tree_subtract'),
|
65
66
|
args: %i[data_format nostdin]
|
66
67
|
|
@@ -71,7 +72,7 @@ module I18n::Tasks
|
|
71
72
|
end
|
72
73
|
|
73
74
|
cmd :tree_set_value,
|
74
|
-
pos:
|
75
|
+
pos: '[VALUE] [tree (or stdin)]',
|
75
76
|
desc: t('i18n_tasks.cmd.desc.tree_set_value'),
|
76
77
|
args: %i[value data_format nostdin pattern]
|
77
78
|
|
@@ -80,12 +81,13 @@ module I18n::Tasks
|
|
80
81
|
forest = forest_pos_or_stdin!(opt)
|
81
82
|
key_pattern = opt[:pattern]
|
82
83
|
fail CommandError, 'pass value (-v, --value)' if value.blank?
|
84
|
+
|
83
85
|
forest.set_each_value!(value, key_pattern)
|
84
86
|
print_forest forest, opt
|
85
87
|
end
|
86
88
|
|
87
89
|
cmd :tree_convert,
|
88
|
-
pos:
|
90
|
+
pos: '[tree (or stdin)]',
|
89
91
|
desc: t('i18n_tasks.cmd.desc.tree_convert'),
|
90
92
|
args: [arg(:data_format).dup.tap { |a| a[0..1] = ['-f', '--from FORMAT'] },
|
91
93
|
arg(:out_format).dup.tap { |a| a[0..1] = ['-t', '--to FORMAT'] }]
|
@@ -16,7 +16,7 @@ module I18n::Tasks
|
|
16
16
|
t('i18n_tasks.cmd.args.desc.keep_order')
|
17
17
|
|
18
18
|
cmd :find,
|
19
|
-
pos:
|
19
|
+
pos: '[pattern]',
|
20
20
|
desc: t('i18n_tasks.cmd.desc.find'),
|
21
21
|
args: %i[out_format pattern strict]
|
22
22
|
|
@@ -27,18 +27,18 @@ module I18n::Tasks
|
|
27
27
|
end
|
28
28
|
|
29
29
|
cmd :unused,
|
30
|
-
pos:
|
30
|
+
pos: '[locale ...]',
|
31
31
|
desc: t('i18n_tasks.cmd.desc.unused'),
|
32
32
|
args: %i[locales out_format strict]
|
33
33
|
|
34
34
|
def unused(opt = {})
|
35
35
|
forest = i18n.unused_keys(**opt.slice(:locales, :strict))
|
36
36
|
print_forest forest, opt, :unused_keys
|
37
|
-
:
|
37
|
+
:exit1 unless forest.empty?
|
38
38
|
end
|
39
39
|
|
40
40
|
cmd :remove_unused,
|
41
|
-
pos:
|
41
|
+
pos: '[locale ...]',
|
42
42
|
desc: t('i18n_tasks.cmd.desc.remove_unused'),
|
43
43
|
args: %i[locales out_format strict keep_order confirm pattern]
|
44
44
|
|
@@ -66,6 +66,7 @@ module I18n::Tasks
|
|
66
66
|
|
67
67
|
def confirm_remove_unused!(unused_keys, opt)
|
68
68
|
return if ENV['CONFIRM'] || opt[:confirm]
|
69
|
+
|
69
70
|
locales = Rainbow(unused_keys.flat_map { |root| root.key.split('+') }.sort.uniq * ', ').bright
|
70
71
|
msg = [
|
71
72
|
Rainbow(t('i18n_tasks.remove_unused.confirm', count: unused_keys.leaves.count, locales: locales)).red,
|
@@ -10,8 +10,8 @@ module I18n::Tasks
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
def t(*args)
|
14
|
-
I18n.t(*args)
|
13
|
+
def t(*args, **kwargs)
|
14
|
+
I18n.t(*args, **kwargs)
|
15
15
|
end
|
16
16
|
|
17
17
|
module ClassMethods
|
@@ -42,8 +42,8 @@ module I18n::Tasks
|
|
42
42
|
end
|
43
43
|
|
44
44
|
# late-bound I18n.t for module bodies
|
45
|
-
def t(*args, **
|
46
|
-
proc { I18n.t(*args, **
|
45
|
+
def t(*args, **kwargs)
|
46
|
+
proc { I18n.t(*args, **kwargs) }
|
47
47
|
end
|
48
48
|
|
49
49
|
# if class is a module, merge DSL definitions when it is included
|
@@ -16,6 +16,7 @@ module I18n::Tasks
|
|
16
16
|
|
17
17
|
def call(value, *)
|
18
18
|
return @valid.first unless value.present?
|
19
|
+
|
19
20
|
if @valid.include?(value)
|
20
21
|
value
|
21
22
|
else
|
@@ -37,6 +38,7 @@ module I18n::Tasks
|
|
37
38
|
def call(values, *)
|
38
39
|
values = Array(values)
|
39
40
|
return @valid if values == %w[all]
|
41
|
+
|
40
42
|
invalid = values - @valid
|
41
43
|
if invalid.empty?
|
42
44
|
if values.empty?
|
@@ -5,12 +5,13 @@ module I18n::Tasks
|
|
5
5
|
module OptionParsers
|
6
6
|
module Locale
|
7
7
|
module Validator
|
8
|
-
VALID_LOCALE_RE = /\A\w[\w
|
8
|
+
VALID_LOCALE_RE = /\A\w[\w\-.]*\z/i.freeze
|
9
9
|
|
10
10
|
def validate!(locale)
|
11
11
|
if VALID_LOCALE_RE !~ locale
|
12
12
|
fail CommandError, I18n.t('i18n_tasks.cmd.errors.invalid_locale', invalid: locale)
|
13
13
|
end
|
14
|
+
|
14
15
|
locale
|
15
16
|
end
|
16
17
|
end
|