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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +28 -11
  3. data/config/locales/en.yml +5 -3
  4. data/config/locales/ru.yml +1 -0
  5. data/i18n-tasks.gemspec +12 -6
  6. data/lib/i18n/tasks/base_task.rb +2 -1
  7. data/lib/i18n/tasks/cli.rb +27 -17
  8. data/lib/i18n/tasks/command/commander.rb +1 -0
  9. data/lib/i18n/tasks/command/commands/data.rb +8 -6
  10. data/lib/i18n/tasks/command/commands/eq_base.rb +2 -2
  11. data/lib/i18n/tasks/command/commands/health.rb +4 -3
  12. data/lib/i18n/tasks/command/commands/interpolations.rb +1 -1
  13. data/lib/i18n/tasks/command/commands/meta.rb +1 -1
  14. data/lib/i18n/tasks/command/commands/missing.rb +22 -9
  15. data/lib/i18n/tasks/command/commands/tree.rb +8 -6
  16. data/lib/i18n/tasks/command/commands/usages.rb +5 -4
  17. data/lib/i18n/tasks/command/dsl.rb +4 -4
  18. data/lib/i18n/tasks/command/option_parsers/enum.rb +2 -0
  19. data/lib/i18n/tasks/command/option_parsers/locale.rb +2 -1
  20. data/lib/i18n/tasks/command/options/common.rb +5 -0
  21. data/lib/i18n/tasks/command/options/data.rb +4 -1
  22. data/lib/i18n/tasks/command/options/locales.rb +5 -5
  23. data/lib/i18n/tasks/concurrent/cached_value.rb +2 -2
  24. data/lib/i18n/tasks/configuration.rb +17 -10
  25. data/lib/i18n/tasks/console_context.rb +1 -1
  26. data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +9 -2
  27. data/lib/i18n/tasks/data/file_formats.rb +3 -1
  28. data/lib/i18n/tasks/data/file_system_base.rb +7 -6
  29. data/lib/i18n/tasks/data/router/conservative_router.rb +2 -1
  30. data/lib/i18n/tasks/data/router/pattern_router.rb +3 -1
  31. data/lib/i18n/tasks/data/tree/node.rb +6 -3
  32. data/lib/i18n/tasks/data/tree/nodes.rb +6 -7
  33. data/lib/i18n/tasks/data/tree/siblings.rb +10 -4
  34. data/lib/i18n/tasks/data/tree/traversal.rb +34 -11
  35. data/lib/i18n/tasks/html_keys.rb +4 -6
  36. data/lib/i18n/tasks/ignore_keys.rb +4 -3
  37. data/lib/i18n/tasks/interpolations.rb +10 -4
  38. data/lib/i18n/tasks/key_pattern_matching.rb +3 -2
  39. data/lib/i18n/tasks/locale_pathname.rb +1 -1
  40. data/lib/i18n/tasks/missing_keys.rb +4 -0
  41. data/lib/i18n/tasks/plural_keys.rb +5 -6
  42. data/lib/i18n/tasks/references.rb +4 -2
  43. data/lib/i18n/tasks/reports/base.rb +4 -3
  44. data/lib/i18n/tasks/reports/terminal.rb +8 -6
  45. data/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb +118 -0
  46. data/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb +91 -0
  47. data/lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb +69 -0
  48. data/lib/i18n/tasks/scanners/erb_ast_processor.rb +74 -0
  49. data/lib/i18n/tasks/scanners/erb_ast_scanner.rb +48 -0
  50. data/lib/i18n/tasks/scanners/file_scanner.rb +4 -3
  51. data/lib/i18n/tasks/scanners/files/caching_file_finder.rb +0 -3
  52. data/lib/i18n/tasks/scanners/files/file_finder.rb +3 -2
  53. data/lib/i18n/tasks/scanners/local_ruby_parser.rb +85 -0
  54. data/lib/i18n/tasks/scanners/occurrence_from_position.rb +3 -3
  55. data/lib/i18n/tasks/scanners/pattern_mapper.rb +1 -1
  56. data/lib/i18n/tasks/scanners/pattern_scanner.rb +8 -5
  57. data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +4 -2
  58. data/lib/i18n/tasks/scanners/relative_keys.rb +19 -4
  59. data/lib/i18n/tasks/scanners/results/occurrence.rb +17 -1
  60. data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +9 -34
  61. data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +91 -154
  62. data/lib/i18n/tasks/scanners/ruby_key_literals.rb +4 -4
  63. data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +2 -0
  64. data/lib/i18n/tasks/split_key.rb +3 -1
  65. data/lib/i18n/tasks/string_interpolation.rb +1 -0
  66. data/lib/i18n/tasks/translation.rb +3 -3
  67. data/lib/i18n/tasks/translators/base_translator.rb +5 -3
  68. data/lib/i18n/tasks/translators/deepl_translator.rb +10 -2
  69. data/lib/i18n/tasks/translators/google_translator.rb +2 -0
  70. data/lib/i18n/tasks/translators/yandex_translator.rb +2 -0
  71. data/lib/i18n/tasks/used_keys.rb +21 -14
  72. data/lib/i18n/tasks/version.rb +1 -1
  73. data/lib/i18n/tasks.rb +17 -7
  74. data/templates/config/i18n-tasks.yml +21 -1
  75. metadata +44 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ac2026537e1679b2e306eddee2c97ca569d6c498d1a9850e512702626ab9c19
4
- data.tar.gz: 49a908c2b2a9144919076a5c3948fa537486739dc9b02968366c7ec0d3ccf62b
3
+ metadata.gz: e77c4c19ca040adf1879259e8ad1d963e181eb1682e97ffa99f5b09d1b59e803
4
+ data.tar.gz: cc5ed0a16159c8f14a9006ab3908cfdc93b5dbc338603d54846d0ae4f84abb80
5
5
  SHA512:
6
- metadata.gz: 639a786f1b1dfa5ee217e658975ac8b01b0966a4afdea49b69fdb4d1d4ff8cf9e48aab98eae0c6475a5c71fa4e8caccee88f1239205317550a03505a015fc932
7
- data.tar.gz: d5a7ed2e9e1f4d2bf0474eb821e00713eff0fe486ef3cc92da12bc6e227bfecc987c5d2e70538e2c4436aba8958a763c8640d46c68d5ff2fdbf18e364d3d6cbc
6
+ metadata.gz: 978d87d9e73dc5f4e183a7fdebabeec9864c3b1ba530f722bc3a80428b1c422eeda55f2bbd2b7ec8c1a67649ee128db4ae9ab0fc24db4b8cf386f7740b0f34d4
7
+ data.tar.gz: 46a0b0cbb262c17aaf1195293edd1fc04d665dd85d5f8d1b399f1ae565db2eb99e3e281fb1a34a5cb3629aadee2c2b279642d8beca623814fcd9b75c0c502b5e
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # i18n-tasks [![Build Status][badge-travis]][travis] [![Coverage Status][badge-coverage]][coverage] [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/glebm/i18n-tasks?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
1
+ # i18n-tasks [![Build Status][badge-ci]][ci] [![Coverage Status][badge-coverage]][coverage] [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/glebm/i18n-tasks?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2
+
3
+ [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](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.9.33'
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 in `fr` but not `en` from `fr`:
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: Project -> APIS & AUTH -> Credentials -> Public API access -> Key for server applications.
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: <Deep Pro 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
- [travis]: https://travis-ci.org/glebm/i18n-tasks
437
- [badge-travis]: https://img.shields.io/travis/glebm/i18n-tasks.svg
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/master/templates/config/i18n-tasks.yml
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/master/lib/i18n/tasks/data/file_system_base.rb
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
@@ -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
@@ -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
- TEXT
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 = { 'issue_tracker' => 'https://github.com/glebm/i18n-tasks' } if s.respond_to?(:metadata=)
28
- s.required_ruby_version = '>= 2.3', '< 4.0' if s.respond_to?(:required_ruby_version=)
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', '~> 0.53.0'
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
 
@@ -39,7 +39,8 @@ module I18n
39
39
  include Data
40
40
  include Stats
41
41
 
42
- def initialize(config = {})
42
+ def initialize(config_file: nil, **config)
43
+ @config_override = config_file
43
44
  self.config = config || {}
44
45
  end
45
46
 
@@ -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
- exit 1 if run(argv) == :exit_1
20
- rescue OptionParser::ParseError => e
21
- error e.message, 64
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
- I18n.with_locale(base_task.internal_locale) do
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'] || STDOUT.isatty)
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: '[locale ...]',
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
- :exit_1 unless non_normalized.empty?
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: '[locale ...]',
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: '[tree ...]',
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: '[tree]',
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: '[tree]',
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: '[locale ...]',
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
- :exit_1 unless forest.empty?
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: '[locale ...]',
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 == :exit_1 }
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
- :exit_1 unless forest.empty?
17
+ :exit1 unless forest.empty?
18
18
  end
19
19
  end
20
20
  end
@@ -7,7 +7,7 @@ module I18n::Tasks
7
7
  include Command::Collection
8
8
 
9
9
  cmd :config,
10
- pos: '[section ...]',
10
+ pos: '[section ...]',
11
11
  desc: t('i18n_tasks.cmd.desc.config')
12
12
 
13
13
  def config(opts = {})
@@ -23,23 +23,31 @@ module I18n::Tasks
23
23
  )
24
24
 
25
25
  cmd :missing,
26
- pos: '[locale ...]',
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
- :exit_1 unless forest.empty?
37
+ :exit1 unless forest.empty?
34
38
  end
35
39
 
36
40
  cmd :translate_missing,
37
- pos: '[locale ...]',
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: '[locale ...]',
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
- def add_missing(opt = {}) # rubocop:disable Metrics/AbcSize
56
- [ # Merge base locale first, as this may affect the value for the other locales
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: '[tree (or stdin)]',
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: '[[tree] [tree] ... (or stdin)]',
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: '[pattern] [tree (or stdin)]',
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: '[[tree] [tree] ... (or stdin)]',
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: '[VALUE] [tree (or stdin)]',
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: '[tree (or stdin)]',
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: '[pattern]',
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: '[locale ...]',
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
- :exit_1 unless forest.empty?
37
+ :exit1 unless forest.empty?
38
38
  end
39
39
 
40
40
  cmd :remove_unused,
41
- pos: '[locale ...]',
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, **opts)
46
- proc { I18n.t(*args, **opts) }
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\-\.]*\z/i
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