i18n-tasks 1.0.15 → 1.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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +85 -13
  3. data/Rakefile +4 -4
  4. data/bin/i18n-tasks +3 -3
  5. data/config/locales/en.yml +6 -0
  6. data/config/locales/ru.yml +7 -0
  7. data/i18n-tasks.gemspec +28 -41
  8. data/lib/i18n/tasks/base_task.rb +19 -19
  9. data/lib/i18n/tasks/cli.rb +37 -30
  10. data/lib/i18n/tasks/command/collection.rb +4 -4
  11. data/lib/i18n/tasks/command/commander.rb +5 -5
  12. data/lib/i18n/tasks/command/commands/check_prism.rb +126 -0
  13. data/lib/i18n/tasks/command/commands/data.rb +33 -33
  14. data/lib/i18n/tasks/command/commands/eq_base.rb +3 -3
  15. data/lib/i18n/tasks/command/commands/health.rb +6 -5
  16. data/lib/i18n/tasks/command/commands/interpolations.rb +14 -3
  17. data/lib/i18n/tasks/command/commands/meta.rb +6 -6
  18. data/lib/i18n/tasks/command/commands/missing.rb +25 -25
  19. data/lib/i18n/tasks/command/commands/tree.rb +33 -33
  20. data/lib/i18n/tasks/command/commands/usages.rb +24 -24
  21. data/lib/i18n/tasks/command/dsl.rb +1 -1
  22. data/lib/i18n/tasks/command/option_parsers/enum.rb +5 -5
  23. data/lib/i18n/tasks/command/option_parsers/locale.rb +4 -4
  24. data/lib/i18n/tasks/command/options/common.rb +16 -16
  25. data/lib/i18n/tasks/command/options/data.rb +18 -18
  26. data/lib/i18n/tasks/command/options/locales.rb +32 -32
  27. data/lib/i18n/tasks/commands.rb +14 -12
  28. data/lib/i18n/tasks/concurrent/cache.rb +1 -1
  29. data/lib/i18n/tasks/concurrent/cached_value.rb +1 -1
  30. data/lib/i18n/tasks/configuration.rb +22 -21
  31. data/lib/i18n/tasks/console_context.rb +11 -11
  32. data/lib/i18n/tasks/data/adapter/json_adapter.rb +1 -1
  33. data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +5 -5
  34. data/lib/i18n/tasks/data/file_formats.rb +3 -3
  35. data/lib/i18n/tasks/data/file_system.rb +5 -5
  36. data/lib/i18n/tasks/data/file_system_base.rb +26 -26
  37. data/lib/i18n/tasks/data/language_names.rb +202 -0
  38. data/lib/i18n/tasks/data/router/conservative_router.rb +3 -3
  39. data/lib/i18n/tasks/data/router/isolating_router.rb +19 -19
  40. data/lib/i18n/tasks/data/router/pattern_router.rb +5 -5
  41. data/lib/i18n/tasks/data/tree/node.rb +27 -27
  42. data/lib/i18n/tasks/data/tree/nodes.rb +10 -10
  43. data/lib/i18n/tasks/data/tree/siblings.rb +20 -20
  44. data/lib/i18n/tasks/data/tree/traversal.rb +5 -5
  45. data/lib/i18n/tasks/data.rb +4 -4
  46. data/lib/i18n/tasks/html_keys.rb +2 -2
  47. data/lib/i18n/tasks/ignore_keys.rb +9 -9
  48. data/lib/i18n/tasks/interpolations.rb +21 -1
  49. data/lib/i18n/tasks/key_pattern_matching.rb +8 -8
  50. data/lib/i18n/tasks/logging.rb +2 -1
  51. data/lib/i18n/tasks/missing_keys.rb +24 -8
  52. data/lib/i18n/tasks/plural_keys.rb +6 -4
  53. data/lib/i18n/tasks/references.rb +4 -4
  54. data/lib/i18n/tasks/reports/base.rb +18 -14
  55. data/lib/i18n/tasks/reports/terminal.rb +64 -47
  56. data/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb +3 -3
  57. data/lib/i18n/tasks/scanners/ast_matchers/default_i18n_subject_matcher.rb +3 -3
  58. data/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb +10 -10
  59. data/lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb +1 -1
  60. data/lib/i18n/tasks/scanners/erb_ast_scanner.rb +69 -10
  61. data/lib/i18n/tasks/scanners/file_scanner.rb +5 -5
  62. data/lib/i18n/tasks/scanners/files/caching_file_finder.rb +3 -3
  63. data/lib/i18n/tasks/scanners/files/caching_file_finder_provider.rb +3 -3
  64. data/lib/i18n/tasks/scanners/files/caching_file_reader.rb +2 -2
  65. data/lib/i18n/tasks/scanners/files/file_finder.rb +8 -8
  66. data/lib/i18n/tasks/scanners/files/file_reader.rb +1 -1
  67. data/lib/i18n/tasks/scanners/local_ruby_parser.rb +8 -8
  68. data/lib/i18n/tasks/scanners/occurrence_from_position.rb +1 -1
  69. data/lib/i18n/tasks/scanners/pattern_mapper.rb +7 -7
  70. data/lib/i18n/tasks/scanners/pattern_scanner.rb +20 -20
  71. data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +8 -8
  72. data/lib/i18n/tasks/scanners/prism_scanners/arguments_visitor.rb +8 -1
  73. data/lib/i18n/tasks/scanners/prism_scanners/nodes.rb +101 -61
  74. data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +169 -105
  75. data/lib/i18n/tasks/scanners/relative_keys.rb +8 -8
  76. data/lib/i18n/tasks/scanners/results/key_occurrences.rb +3 -3
  77. data/lib/i18n/tasks/scanners/results/occurrence.rb +14 -10
  78. data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +1 -1
  79. data/lib/i18n/tasks/scanners/ruby_key_literals.rb +6 -6
  80. data/lib/i18n/tasks/scanners/ruby_parser_factory.rb +1 -1
  81. data/lib/i18n/tasks/scanners/ruby_scanner.rb +225 -0
  82. data/lib/i18n/tasks/scanners/scanner.rb +2 -2
  83. data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +1 -1
  84. data/lib/i18n/tasks/split_key.rb +4 -4
  85. data/lib/i18n/tasks/stats.rb +3 -3
  86. data/lib/i18n/tasks/translation.rb +5 -5
  87. data/lib/i18n/tasks/translators/base_translator.rb +40 -14
  88. data/lib/i18n/tasks/translators/deepl_translator.rb +17 -14
  89. data/lib/i18n/tasks/translators/google_translator.rb +169 -25
  90. data/lib/i18n/tasks/translators/openai_translator.rb +34 -23
  91. data/lib/i18n/tasks/translators/watsonx_translator.rb +16 -16
  92. data/lib/i18n/tasks/translators/yandex_translator.rb +8 -8
  93. data/lib/i18n/tasks/unused_keys.rb +1 -1
  94. data/lib/i18n/tasks/used_keys.rb +32 -33
  95. data/lib/i18n/tasks/version.rb +1 -1
  96. data/lib/i18n/tasks.rb +17 -17
  97. data/templates/config/i18n-tasks.yml +12 -0
  98. data/templates/minitest/i18n_test.rb +3 -3
  99. data/templates/rspec/i18n_spec.rb +7 -7
  100. metadata +25 -185
  101. data/lib/i18n/tasks/scanners/prism_scanner.rb +0 -83
  102. data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +0 -145
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c2d36ee3f03672492a67bc2b98344a65051392dfaf0891aee1cbe1c01126884
4
- data.tar.gz: 1e8ff7f7b02d804be0c4d077424cec4dfbb5881b3bcada145b1c2878e3913e2b
3
+ metadata.gz: 3231dd8c4f3772a49ed9db6862f6d34c9fabed656a1ba3588f2a0d24be158d50
4
+ data.tar.gz: b5db9e81af6eff9256acf3b84bbbcea4a738c987f0485e04e127eba0236f01fd
5
5
  SHA512:
6
- metadata.gz: 1955ea4adbf216f1e4014ea4b5f3873d45a14304a7c5d36b1692f5bd6924f358dd2bd90154d3a857678b50860ee1c248a2d914eef98af54062f8f03aced8a0ce
7
- data.tar.gz: 9a3e08a0a8ab27242701ea48dc68be4635e7f553d647e7f27d1449be51a014f0b0fa8d35bccd67ff2402ad9d25400f4463d702bac9cf5e285786211d4c451635
6
+ metadata.gz: 601af6d9cf98382b36cb7eebec4672c8918440f63899ef8e91cda798de0685971e331e352420c0f7d3c2f4a5dc056d80f7b0feb1b04e7aa847c69ebd273ff214
7
+ data.tar.gz: d8e0c5b7bb14a6ab061f60360e869f293b2c608fde5a02f7ebb90495f8c0d0adec69d4665818bd96b2fe6f3c590434aa94ad35462e2777bd9925ac3150aa4273
data/README.md CHANGED
@@ -24,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
24
24
  Add i18n-tasks to the Gemfile:
25
25
 
26
26
  ```ruby
27
- gem 'i18n-tasks', '~> 1.0.15', group: :development
27
+ gem 'i18n-tasks', '~> 1.1.0', group: :development
28
28
  ```
29
29
 
30
30
  Copy the default [configuration file](#configuration):
@@ -227,6 +227,27 @@ See the full list of tasks with `i18n-tasks --help`.
227
227
  ✔ Reference keys (keys with `:symbol` values) are fully supported. These keys are copied as-is in
228
228
  `add/translate-missing`, and can be looked up by reference or value in `find`.
229
229
 
230
+ #### Unexpected normalization
231
+ `i18n-tasks` uses a yaml parser and emitter called `Psych` under the hood. `Psych` has it's own heuristic on when
232
+ to use `|`, `>`, or `""` for multi-line strings. This can have some unexpected consequences, eg when normalizing:
233
+ ```yaml
234
+ a: |
235
+ Lorem ipsum dolor sit amet, consectetur
236
+ Lorem ipsum dolor sit amet, consectetur
237
+ b: |
238
+ Lorem ipsum dolor sit amet, consectetur
239
+ Lorem ipsum dolor sit amet, consectetur
240
+ ```
241
+ we get the result
242
+ ```yaml
243
+ a: |
244
+ Lorem ipsum dolor sit amet, consectetur
245
+ Lorem ipsum dolor sit amet, consectetur
246
+ b: "Lorem ipsum dolor sit amet, consectetur \nLorem ipsum dolor sit amet, consectetur\n"
247
+ ```
248
+ The only difference between `a` and `b` is that `b` has an extra trailing space in each line.
249
+ This is an unfortunate side effect of `i18n-tasks` using `Psych`.
250
+
230
251
  #### `t()` keyword arguments
231
252
 
232
253
  ✔ `scope` keyword argument is fully supported by the AST scanner, and also by the Regexp scanner but only when it is the first argument.
@@ -366,6 +387,22 @@ truck.contents.description_body ⮕ truck.attributes.description.body
366
387
  If you store data somewhere but in the filesystem, e.g. in the database or mongodb, you can implement a custom adapter.
367
388
  If you have implemented a custom adapter please share it on [the wiki][wiki].
368
389
 
390
+ #### Rails credentials
391
+
392
+ If you use Rails credentials and want to load e.g. credentials for translation backends, convert your `i18n-tasks.yml`to `i18n-tasks.yml.erb` and add
393
+ a `require "./config/application"` line to load Rails.
394
+
395
+ ```yaml
396
+ # config/i18n-tasks.yml.erb
397
+ <% require "./config/application" %>
398
+
399
+ # ...
400
+
401
+ translation:
402
+ backend: google
403
+ google_translate_api_key: <%= Rails.application.credentials.google_translate_api_key %>
404
+ ```
405
+
369
406
  ### Usage search
370
407
 
371
408
  i18n-tasks uses an AST scanner for `.rb` and `.html.erb` files, and a regexp scanner for all other files.
@@ -392,6 +429,24 @@ For more complex cases, you can implement a [custom scanner][custom-scanner-docs
392
429
 
393
430
  See the [config file][config] to find out more.
394
431
 
432
+ ### Environment Variables and Dotenv
433
+
434
+ i18n-tasks supports loading environment variables from `.env` files using the [dotenv](https://github.com/bkeepers/dotenv) gem.
435
+ This is particularly useful for storing translation API keys and other sensitive configuration.
436
+
437
+ If you have `dotenv` in your Gemfile, i18n-tasks will automatically load environment variables from `.env` files
438
+ before executing commands. This means you can store your API keys in a `.env` file:
439
+
440
+ ```bash
441
+ # .env
442
+ GOOGLE_TRANSLATE_API_KEY=your_google_api_key
443
+ DEEPL_AUTH_KEY=your_deepl_api_key
444
+ OPENAI_API_KEY=your_openai_api_key
445
+ ```
446
+
447
+ The dotenv integration works seamlessly - no additional configuration is required. If `dotenv` is not available,
448
+ i18n-tasks will continue to work normally using system environment variables.
449
+
395
450
  <a name="google-translation-config"></a>
396
451
  ### Google Translate
397
452
 
@@ -444,7 +499,7 @@ translation:
444
499
  or via environment variables:
445
500
 
446
501
  ```bash
447
- DEEPL_API_KEY=<DeepL Pro API key>
502
+ DEEPL_AUTH_KEY=<DeepL Pro API key>
448
503
  DEEPL_HOST=<optional>
449
504
  DEEPL_VERSION=<optional>
450
505
  ```
@@ -509,30 +564,47 @@ WATSONX_PROJECT_ID=<watsonx project id>
509
564
  WATSONX_MODEL=<optional>
510
565
  ```
511
566
 
512
- ### Contextual Rails Parser
567
+ ### Prism-based scanner for Ruby and ERB files
568
+
569
+ There is a scanner based on [Prism](https://github.com/ruby/prism) usable in two different modes.
513
570
 
514
- There is an experimental feature to parse Rails with more context. `i18n-tasks` will support:
571
+ - `rails` mode parses Rails code and handles context such as controllers, before_actions, model translations and more.
572
+ - `ruby` mode parses Ruby code only, and works similar to the existing whitequark/parser-implementation.
573
+ - The parser is used for both ruby and ERB files.
574
+
575
+ #### `rails` mode
576
+
577
+ It handles the following cases:
515
578
  - Translations called in `before_actions`
516
579
  - Translations called in nested methods
517
580
  - `Model.human_attribute_name` calls
518
581
  - `Model.model_name.human` calls
519
582
 
520
- Enabled it by adding the scanner in your `config/i18n-tasks.yml`:
521
-
522
- ```ruby
523
- <% I18n::Tasks.add_scanner(
524
- 'I18n::Tasks::Scanners::PrismScanner',
525
- only: %w(*.rb)
526
- ) %>
583
+ Enabled it by adding:
584
+ ```yaml
585
+ search:
586
+ prism: "rails"
527
587
  ```
528
588
 
529
- To only enable Ruby-scanning and not any Rails support, please add config under the `search` section:
589
+ to your `config/i18n-tasks.yml` file.
590
+
591
+ #### `ruby` mode
592
+
593
+ It finds all `I18n.t`, `I18n.translate`, `t` and `translate` calls in Ruby code. Enabled it by adding:
530
594
 
531
595
  ```yaml
532
596
  search:
533
- prism_visitor: "ruby" # default "rails"
597
+ prism: "ruby"
534
598
  ```
535
599
 
600
+ The goal is to replace the whitequark/parser-based scanner with this one in the future.
601
+
602
+ #### Help us out with testing
603
+
604
+ Please install the latest version of the gem and run `i18n-tasks check-prism` which will parse everything with the whitequark/parser-based scanner and then everything with the Prism-scanner and try to compare the results.
605
+
606
+ Open up issues with any parser crashes, missed translations or false positives.
607
+
536
608
  ## Interactive console
537
609
 
538
610
  `i18n-tasks irb` starts an IRB session in i18n-tasks context. Type `guide` for more information.
data/Rakefile CHANGED
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
5
  RSpec::Core::RakeTask.new(:rspec)
6
6
  task default: :rspec
7
7
 
8
8
  task :irb do
9
9
  # $: << File.expand_path('lib', __FILE__)
10
- require 'i18n/tasks'
11
- require 'i18n/tasks/commands'
10
+ require "i18n/tasks"
11
+ require "i18n/tasks/commands"
12
12
  I18n::Tasks::Commands.new(I18n::Tasks::BaseTask.new).irb
13
13
  end
data/bin/i18n-tasks CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative '../spec/bin_simplecov_helper' if ENV['I18N_TASKS_BIN_SIMPLECOV_COVERAGE']
4
+ require_relative "../spec/bin_simplecov_helper" if ENV["I18N_TASKS_BIN_SIMPLECOV_COVERAGE"]
5
5
 
6
6
  # prevent i18n gem warning
7
- require 'i18n'
7
+ require "i18n"
8
8
  i18n_gem_config = I18n.config
9
9
  if i18n_gem_config.respond_to?(:enforce_available_locales=) && i18n_gem_config.enforce_available_locales.nil?
10
10
  i18n_gem_config.enforce_available_locales = true
11
11
  end
12
12
 
13
- require 'i18n/tasks/cli'
13
+ require "i18n/tasks/cli"
14
14
 
15
15
  I18n::Tasks::CLI.start(ARGV)
@@ -34,6 +34,9 @@ en:
34
34
  add_missing: add missing keys to locale data, optionally match a pattern
35
35
  check_consistent_interpolations: verify that all translations use correct interpolation variables
36
36
  check_normalized: verify that all translation data is normalized
37
+ check_prism: verify that Prism-scanner produces the same (or better) results as the default
38
+ parser
39
+ check_reserved_interpolations: verify that all translations avoid reserved interpolation variables
37
40
  config: display i18n-tasks configuration
38
41
  cp: copy the keys in locale data that match the given pattern
39
42
  data: show locale data
@@ -124,6 +127,9 @@ en:
124
127
  other: "%{count} translation will be removed from %{locales}."
125
128
  noop: No unused keys to remove
126
129
  removed: Removed %{count} keys
130
+ reserved_interpolations:
131
+ details_title: Reserved interpolation keys
132
+ none: No reserved interpolations found.
127
133
  translate_missing:
128
134
  translated: Translated %{count} keys
129
135
  unused:
@@ -32,6 +32,10 @@ ru:
32
32
  check_consistent_interpolations: убедитесь, что во всех переводах используются правильные
33
33
  интерполяционные переменные
34
34
  check_normalized: проверить, что все файлы переводов нормализованы
35
+ check_prism: убедитесь, что Prism-scanner выдает те же (или лучшие) результаты, что и анализатор
36
+ по умолчанию
37
+ check_reserved_interpolations: проверьте, что все переводы обходятся без зарезервированных
38
+ интерполяционных переменных
35
39
  config: показать конфигурацию
36
40
  cp: скопируйте ключи в данных локали, соответствующие заданному шаблону
37
41
  data: показать данные переводов
@@ -125,6 +129,9 @@ ru:
125
129
  other: Переводы (%{count}) будут удалены из %{locales}.
126
130
  noop: Нет неиспользуемых ключей
127
131
  removed: Удалены ключи (%{count})
132
+ reserved_interpolations:
133
+ details_title: Зарезервированные ключи интерполяции
134
+ none: NЗарезервированных интерполяций не обнаружено.
128
135
  translate_missing:
129
136
  translated: Переведены ключи (%{count})
130
137
  unused:
data/i18n-tasks.gemspec CHANGED
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'i18n/tasks/version'
5
+ require "i18n/tasks/version"
6
6
 
7
7
  Gem::Specification.new do |s|
8
- s.name = 'i18n-tasks'
9
- s.version = I18n::Tasks::VERSION
10
- s.authors = ['glebm']
11
- s.email = ['glex.spb@gmail.com']
12
- s.license = 'MIT'
13
- s.summary = 'Manage localization and translation with the awesome power of static analysis'
14
- s.description = <<~TEXT
8
+ s.name = "i18n-tasks"
9
+ s.version = I18n::Tasks::VERSION
10
+ s.authors = ["glebm"]
11
+ s.email = ["glex.spb@gmail.com"]
12
+ s.license = "MIT"
13
+ s.summary = "Manage localization and translation with the awesome power of static analysis"
14
+ s.description = <<~TEXT
15
15
  i18n-tasks helps you find and manage missing and unused translations.
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,
@@ -25,42 +25,29 @@ Gem::Specification.new do |s|
25
25
  # Or for minitest:
26
26
  cp $(bundle exec i18n-tasks gem-path)/templates/minitest/i18n_test.rb test/
27
27
  TEXT
28
- s.homepage = 'https://github.com/glebm/i18n-tasks'
28
+ s.homepage = "https://github.com/glebm/i18n-tasks"
29
29
  s.metadata = {
30
- 'changelog_uri' => 'https://github.com/glebm/i18n-tasks/blob/main/CHANGES.md',
31
- 'issue_tracker' => 'https://github.com/glebm/i18n-tasks',
32
- 'rubygems_mfa_required' => 'true',
33
- 'source_code_uri' => 'https://github.com/glebm/i18n-tasks'
30
+ "changelog_uri" => "https://github.com/glebm/i18n-tasks/blob/main/CHANGES.md",
31
+ "issue_tracker" => "https://github.com/glebm/i18n-tasks",
32
+ "rubygems_mfa_required" => "true",
33
+ "source_code_uri" => "https://github.com/glebm/i18n-tasks"
34
34
  }
35
- s.required_ruby_version = '>= 2.6', '< 4.0'
35
+ s.required_ruby_version = ">= 3.1"
36
36
 
37
37
  s.files = `git ls-files`.split($/)
38
38
  s.files -= s.files.grep(%r{^(doc/|\.|spec/)}) + %w[CHANGES.md config/i18n-tasks.yml Gemfile]
39
- s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } - %w[i18n-tasks.cmd]
40
- s.require_paths = ['lib']
39
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } - %w[i18n-tasks.cmd]
40
+ s.require_paths = ["lib"]
41
41
 
42
- s.add_dependency 'activesupport', '>= 4.0.2'
43
- s.add_dependency 'ast', '>= 2.1.0'
44
- s.add_dependency 'erubi'
45
- s.add_dependency 'highline', '>= 2.0.0'
46
- s.add_dependency 'i18n'
47
- s.add_dependency 'parser', '>= 3.2.2.1'
48
- s.add_dependency 'rails-i18n'
49
- s.add_dependency 'rainbow', '>= 2.2.2', '< 4.0'
50
- s.add_dependency 'ruby-progressbar', '~> 1.8', '>= 1.8.1'
51
- s.add_dependency 'terminal-table', '>= 1.5.1'
52
- s.add_development_dependency 'bundler', '~> 2.0', '>= 2.0.1'
53
- s.add_development_dependency 'overcommit', '~> 0.58.0'
54
- s.add_development_dependency 'rake'
55
- s.add_development_dependency 'rspec', '~> 3.3'
56
- s.add_development_dependency 'rubocop', '~> 1.50.1'
57
- s.add_development_dependency 'rubocop-rake', '~> 0.6.0'
58
- s.add_development_dependency 'rubocop-rspec', '~> 2.19.0'
59
- s.add_development_dependency 'simplecov'
60
- s.add_development_dependency 'yard'
61
-
62
- # Translation backends
63
- s.add_development_dependency 'deepl-rb', '>= 2.1.0'
64
- s.add_development_dependency 'easy_translate', '>= 0.5.1' # Google Translate
65
- s.add_development_dependency 'yandex-translator', '>= 0.3.3'
42
+ s.add_dependency "activesupport", ">= 4.0.2"
43
+ s.add_dependency "ast", ">= 2.1.0"
44
+ s.add_dependency "erubi"
45
+ s.add_dependency "highline", ">= 3.0.0"
46
+ s.add_dependency "i18n"
47
+ s.add_dependency "parser", ">= 3.2.2.1"
48
+ s.add_dependency "prism"
49
+ s.add_dependency "rails-i18n"
50
+ s.add_dependency "rainbow", ">= 2.2.2", "< 4.0"
51
+ s.add_dependency "ruby-progressbar", "~> 1.8", ">= 1.8.1"
52
+ s.add_dependency "terminal-table", ">= 1.5.1"
66
53
  end
@@ -1,24 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/command_error'
4
- require 'i18n/tasks/split_key'
5
- require 'i18n/tasks/key_pattern_matching'
6
- require 'i18n/tasks/logging'
7
- require 'i18n/tasks/plural_keys'
8
- require 'i18n/tasks/references'
9
- require 'i18n/tasks/html_keys'
10
- require 'i18n/tasks/used_keys'
11
- require 'i18n/tasks/ignore_keys'
12
- require 'i18n/tasks/missing_keys'
13
- require 'i18n/tasks/interpolations'
14
- require 'i18n/tasks/unused_keys'
15
- require 'i18n/tasks/translation'
16
- require 'i18n/tasks/locale_pathname'
17
- require 'i18n/tasks/locale_list'
18
- require 'i18n/tasks/string_interpolation'
19
- require 'i18n/tasks/data'
20
- require 'i18n/tasks/configuration'
21
- require 'i18n/tasks/stats'
3
+ require "i18n/tasks/command_error"
4
+ require "i18n/tasks/split_key"
5
+ require "i18n/tasks/key_pattern_matching"
6
+ require "i18n/tasks/logging"
7
+ require "i18n/tasks/plural_keys"
8
+ require "i18n/tasks/references"
9
+ require "i18n/tasks/html_keys"
10
+ require "i18n/tasks/used_keys"
11
+ require "i18n/tasks/ignore_keys"
12
+ require "i18n/tasks/missing_keys"
13
+ require "i18n/tasks/interpolations"
14
+ require "i18n/tasks/unused_keys"
15
+ require "i18n/tasks/translation"
16
+ require "i18n/tasks/locale_pathname"
17
+ require "i18n/tasks/locale_list"
18
+ require "i18n/tasks/string_interpolation"
19
+ require "i18n/tasks/data"
20
+ require "i18n/tasks/configuration"
21
+ require "i18n/tasks/stats"
22
22
 
23
23
  module I18n
24
24
  module Tasks
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks'
4
- require 'i18n/tasks/commands'
5
- require 'optparse'
3
+ require "i18n/tasks"
4
+ require "i18n/tasks/commands"
5
+ require "optparse"
6
6
 
7
7
  class I18n::Tasks::CLI
8
8
  include ::I18n::Tasks::Logging
@@ -11,9 +11,9 @@ class I18n::Tasks::CLI
11
11
  new.start(argv)
12
12
  end
13
13
 
14
- def initialize; end
15
-
16
14
  def start(argv)
15
+ load_dotenv
16
+
17
17
  auto_output_coloring do
18
18
  exit 1 if run(argv) == :exit1
19
19
  rescue OptionParser::ParseError => e
@@ -35,7 +35,7 @@ class I18n::Tasks::CLI
35
35
 
36
36
  def run(argv)
37
37
  argv.each_with_index do |arg, i|
38
- next unless ['--config', '-c'].include?(arg)
38
+ next unless ["--config", "-c"].include?(arg)
39
39
 
40
40
  _, config_file = argv.slice!(i, 2)
41
41
  if File.exist?(config_file)
@@ -59,7 +59,7 @@ class I18n::Tasks::CLI
59
59
  def commands
60
60
  # load base task to initialize plugins
61
61
  base_task
62
- @commands ||= ::I18n::Tasks::Commands.cmds.transform_keys { |k| k.to_s.tr('_', '-') }
62
+ @commands ||= ::I18n::Tasks::Commands.cmds.transform_keys { |k| k.to_s.tr("_", "-") }
63
63
  end
64
64
 
65
65
  private
@@ -72,7 +72,7 @@ class I18n::Tasks::CLI
72
72
  command = parse_command! argv
73
73
  options = optparse! command, argv
74
74
  parse_options! options, command, argv
75
- [command.tr('-', '_'), options.update(arguments: argv)]
75
+ [command.tr("-", "_"), options.update(arguments: argv)]
76
76
  end
77
77
 
78
78
  def optparse!(command, argv)
@@ -85,8 +85,8 @@ class I18n::Tasks::CLI
85
85
 
86
86
  def optparse_command!(command, argv)
87
87
  cmd_conf = commands[command]
88
- flags = cmd_conf[:args].dup
89
- options = {}
88
+ flags = cmd_conf[:args].dup
89
+ options = {}
90
90
  OptionParser.new("Usage: #{program_name} #{command} [options] #{cmd_conf[:pos]}".strip) do |op|
91
91
  flags.each do |flag|
92
92
  op.on(*optparse_args(flag)) { |v| options[option_name(flag)] = v }
@@ -98,9 +98,9 @@ class I18n::Tasks::CLI
98
98
  end
99
99
 
100
100
  def optparse_no_command!(argv)
101
- argv << '--help' if argv.empty?
101
+ argv << "--help" if argv.empty?
102
102
  OptionParser.new("Usage: #{program_name} [command] [options]") do |op|
103
- op.on('-v', '--version', 'Print the version') do
103
+ op.on("-v", "--version", "Print the version") do
104
104
  puts I18n::Tasks::VERSION
105
105
  exit
106
106
  end
@@ -111,12 +111,12 @@ class I18n::Tasks::CLI
111
111
 
112
112
  def allow_help_arg_first!(argv)
113
113
  # allow `i18n-tasks --help command` in addition to `i18n-tasks command --help`
114
- argv[0], argv[1] = argv[1], argv[0] if %w[-h --help].include?(argv[0]) && argv[1] && !argv[1].start_with?('-')
114
+ argv[0], argv[1] = argv[1], argv[0] if %w[-h --help].include?(argv[0]) && argv[1] && !argv[1].start_with?("-")
115
115
  end
116
116
 
117
117
  def parse_command!(argv)
118
118
  allow_help_arg_first! argv
119
- if argv[0] && !argv[0].start_with?('-')
119
+ if argv[0] && !argv[0].start_with?("-")
120
120
  if commands.keys.include?(argv[0])
121
121
  argv.shift
122
122
  else
@@ -126,28 +126,28 @@ class I18n::Tasks::CLI
126
126
  end
127
127
 
128
128
  def verbose_option(op)
129
- op.on('--verbose', 'Verbose output') do
129
+ op.on("--verbose", "Verbose output") do
130
130
  ::I18n::Tasks.verbose = true
131
131
  end
132
132
  end
133
133
 
134
134
  def help_option(op)
135
- op.on('-h', '--help', 'Show this message') do
136
- $stderr.puts op
135
+ op.on("-h", "--help", "Show this message") do
136
+ warn op
137
137
  exit
138
138
  end
139
139
  end
140
140
 
141
141
  # @param [OptionParser] op
142
142
  def commands_summary(op)
143
- op.separator ''
144
- op.separator 'Available commands:'
145
- op.separator ''
143
+ op.separator ""
144
+ op.separator "Available commands:"
145
+ op.separator ""
146
146
  commands.each do |cmd, cmd_conf|
147
- op.separator " #{cmd.ljust(op.summary_width + 1, ' ')}#{try_call cmd_conf[:desc]}"
147
+ op.separator " #{cmd.ljust(op.summary_width + 1, " ")}#{try_call cmd_conf[:desc]}"
148
148
  end
149
- op.separator ''
150
- op.separator 'See `i18n-tasks <command> --help` for more information on a specific command.'
149
+ op.separator ""
150
+ op.separator "See `i18n-tasks <command> --help` for more information on a specific command."
151
151
  end
152
152
 
153
153
  def optparse_args(flag)
@@ -155,14 +155,14 @@ class I18n::Tasks::CLI
155
155
  args.map! { |v| try_call v }
156
156
  conf = args.extract_options!
157
157
  if conf.key?(:default)
158
- args[-1] = "#{args[-1]}. #{I18n.t('i18n_tasks.cmd.args.default_text', value: conf[:default])}"
158
+ args[-1] = "#{args[-1]}. #{I18n.t("i18n_tasks.cmd.args.default_text", value: conf[:default])}"
159
159
  end
160
160
  args
161
161
  end
162
162
 
163
163
  def parse_options!(options, command, argv)
164
164
  commands[command][:args].each do |flag|
165
- name = option_name flag
165
+ name = option_name flag
166
166
  options[name] = parse_option flag, options[name], argv, context
167
167
  end
168
168
  end
@@ -170,7 +170,7 @@ class I18n::Tasks::CLI
170
170
  def parse_option(flag, val, argv, context)
171
171
  conf = flag.last.is_a?(Hash) ? flag.last : {}
172
172
  if conf[:consume_positional]
173
- val = Array(val) + Array(flag.include?(Array) ? argv.flat_map { |x| x.split(',') } : argv)
173
+ val = Array(val) + Array(flag.include?(Array) ? argv.flat_map { |x| x.split(",") } : argv)
174
174
  end
175
175
  val = conf[:default] if val.nil? && conf.key?(:default)
176
176
  val = conf[:parser].call(val, context) if conf.key?(:parser)
@@ -179,8 +179,8 @@ class I18n::Tasks::CLI
179
179
 
180
180
  def option_name(flag)
181
181
  flag.detect do |f|
182
- f.start_with?('--')
183
- end.sub(/\A--(\[no-\])?/, '').sub(/[^\-\w].*\z/, '').to_sym
182
+ f.start_with?("--")
183
+ end.sub(/\A--(\[no-\])?/, "").sub(/[^\-\w].*\z/, "").to_sym
184
184
  end
185
185
 
186
186
  def try_call(v)
@@ -205,11 +205,18 @@ class I18n::Tasks::CLI
205
205
  end
206
206
  end
207
207
 
208
- def auto_output_coloring(coloring = ENV['I18N_TASKS_COLOR'] || $stdout.isatty)
209
- coloring_was = Rainbow.enabled
208
+ def auto_output_coloring(coloring = ENV["I18N_TASKS_COLOR"] || $stdout.isatty)
209
+ coloring_was = Rainbow.enabled
210
210
  Rainbow.enabled = coloring
211
211
  yield
212
212
  ensure
213
213
  Rainbow.enabled = coloring_was
214
214
  end
215
+
216
+ def load_dotenv
217
+ require "dotenv"
218
+ Dotenv.load
219
+ rescue LoadError
220
+ # dotenv not available, continue without it
221
+ end
215
222
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/command/dsl'
4
- require 'i18n/tasks/command/options/common'
5
- require 'i18n/tasks/command/options/locales'
6
- require 'i18n/tasks/command/options/data'
3
+ require "i18n/tasks/command/dsl"
4
+ require "i18n/tasks/command/options/common"
5
+ require "i18n/tasks/command/options/locales"
6
+ require "i18n/tasks/command/options/data"
7
7
 
8
8
  module I18n::Tasks
9
9
  module Command
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/cli'
4
- require 'i18n/tasks/reports/terminal'
3
+ require "i18n/tasks/cli"
4
+ require "i18n/tasks/reports/terminal"
5
5
 
6
6
  module I18n::Tasks
7
7
  module Command
@@ -16,10 +16,10 @@ 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
+ log_stderr "#{Rainbow("#StandWith").bg(:blue)}#{Rainbow("Ukraine").bg(:yellow)}"
20
20
  name = name.to_sym
21
- public_name = name.to_s.tr '_', '-'
22
- log_verbose "task: #{public_name}(#{opts.map { |k, v| "#{k}: #{v.inspect}" } * ', '})"
21
+ public_name = name.to_s.tr "_", "-"
22
+ log_verbose "task: #{public_name}(#{opts.map { |k, v| "#{k}: #{v.inspect}" } * ", "})"
23
23
  if opts.empty? || method(name).arity.zero?
24
24
  send name
25
25
  else