i18n-tasks 0.9.27 → 0.9.32

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -5
  3. data/config/locales/en.yml +6 -0
  4. data/config/locales/ru.yml +10 -0
  5. data/i18n-tasks.gemspec +10 -6
  6. data/lib/i18n/tasks.rb +1 -0
  7. data/lib/i18n/tasks/command/commander.rb +1 -1
  8. data/lib/i18n/tasks/command/commands/health.rb +4 -4
  9. data/lib/i18n/tasks/command/commands/interpolations.rb +1 -1
  10. data/lib/i18n/tasks/command/commands/missing.rb +1 -1
  11. data/lib/i18n/tasks/command/commands/usages.rb +10 -4
  12. data/lib/i18n/tasks/command/dsl.rb +2 -2
  13. data/lib/i18n/tasks/configuration.rb +1 -0
  14. data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +1 -1
  15. data/lib/i18n/tasks/data/file_formats.rb +2 -0
  16. data/lib/i18n/tasks/data/tree/node.rb +2 -3
  17. data/lib/i18n/tasks/data/tree/nodes.rb +1 -2
  18. data/lib/i18n/tasks/data/tree/siblings.rb +12 -4
  19. data/lib/i18n/tasks/data/tree/traversal.rb +6 -8
  20. data/lib/i18n/tasks/missing_keys.rb +69 -8
  21. data/lib/i18n/tasks/plural_keys.rb +23 -2
  22. data/lib/i18n/tasks/reports/terminal.rb +6 -10
  23. data/lib/i18n/tasks/scanners/pattern_scanner.rb +7 -5
  24. data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +2 -2
  25. data/lib/i18n/tasks/translation.rb +4 -1
  26. data/lib/i18n/tasks/translators/deepl_translator.rb +6 -1
  27. data/lib/i18n/tasks/translators/yandex_translator.rb +61 -0
  28. data/lib/i18n/tasks/used_keys.rb +2 -1
  29. data/lib/i18n/tasks/version.rb +1 -1
  30. data/templates/config/i18n-tasks.yml +1 -1
  31. data/templates/minitest/i18n_test.rb +8 -1
  32. data/templates/rspec/i18n_spec.rb +9 -2
  33. metadata +71 -35
  34. data/lib/i18n/tasks/rainbow_utils.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3259a9c8894c28f8372fefdd509e2dc8240d40d8ead58b284fc9887f9c30d2c4
4
- data.tar.gz: cd9003f49d146342de7466f42478f2bddd94cfcb108752d1a2fb8109900a3abe
3
+ metadata.gz: bcf38124b967eb00a21ca02f9e894a22941d95e590024971144131cd61856f70
4
+ data.tar.gz: 9ffb6f291f36f0015ad22ad26981d7b605dad695f723517bb928287cd091ecaa
5
5
  SHA512:
6
- metadata.gz: 4a05cf473e34cc265fd70ad193aa849698dafca63b6d76542170d6d119b9d4c453ae06cd1b23d2c9ce3b6943e0d3e14865bb62ffe4cc3c2ab591e432530a2a28
7
- data.tar.gz: 6d9fe81237a10d0fec00ab1bb588d45beb3085ef8342dd644bcc059547b079971aad039d1f17203d16439eece482ca83904f29fdf6e99d6cabe7481667b336a9
6
+ metadata.gz: 8c27ffd3ab866c82237e99b1bea2afd444b2e9e82d0ef4b48031d46da697c079273ba6a88c4beb20a3795044096a614523aab289a46f86afc6f7e66e8719ed1e
7
+ data.tar.gz: fcb4deae9fdcadef52e7e4a6b85871fcbd33b437bc4a7a11f2c4ad42d60f2b835596bbaa86fe6ad4d30382195fd884c0728aa6fdf35fdb1b78cedb2f617bac2a
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # i18n-tasks [![Build Status][badge-travis]][travis] [![Coverage Status][badge-coverage]][coverage] [![Gemnasium][badge-gemnasium]][gemnasium] [![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-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)
2
2
 
3
3
  i18n-tasks helps you find and manage missing and unused translations.
4
4
 
@@ -22,7 +22,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
22
22
  Add i18n-tasks to the Gemfile:
23
23
 
24
24
  ```ruby
25
- gem 'i18n-tasks', '~> 0.9.27'
25
+ gem 'i18n-tasks', '~> 0.9.32'
26
26
  ```
27
27
 
28
28
  Copy the default [configuration file](#configuration):
@@ -45,7 +45,7 @@ $ cp $(i18n-tasks gem-path)/templates/minitest/i18n_test.rb test/
45
45
 
46
46
  ## Usage
47
47
 
48
- Run `i18n-tasks` to get the list of all the tasks with short descriptions.
48
+ Run `bundle exec i18n-tasks` to get the list of all the tasks with short descriptions.
49
49
 
50
50
  ### Check health
51
51
 
@@ -104,6 +104,17 @@ $ i18n-tasks translate-missing --backend=deepl
104
104
  $ i18n-tasks translate-missing --backend=deepl --from=en fr nl
105
105
  ```
106
106
 
107
+ ### Yandex Translate missing keys
108
+
109
+ Translate missing values with Yandex Translate ([more below on the API key](#yandex-translation-config)).
110
+
111
+ ```console
112
+ $ i18n-tasks translate-missing --backend=yandex
113
+
114
+ # accepts from and locales options:
115
+ $ i18n-tasks translate-missing --from=en es fr
116
+ ```
117
+
107
118
  ### Find usages
108
119
 
109
120
  See where the keys are used with `i18n-tasks find`:
@@ -324,6 +335,16 @@ A special syntax similar to file glob patterns is used throughout i18n-tasks to
324
335
  | `:` | matches a single key |
325
336
  | `{a, b.c}` | match any in set, can use `:` and `*`, match is captured |
326
337
 
338
+ Example of usage:
339
+
340
+ ```sh
341
+ $ bundle exec i18n-tasks mv "{:}.contents.{*}_body" "\1.attributes.\2.body"
342
+
343
+ car.contents.name_body ⮕ car.attributes.name.body
344
+ car.contents.description_body ⮕ car.attributes.description.body
345
+ truck.contents.name_body ⮕ truck.attributes.name.body
346
+ truck.contents.description_body ⮕ truck.attributes.description.body
347
+ ```
327
348
 
328
349
  #### Custom adapters
329
350
 
@@ -387,6 +408,17 @@ translation:
387
408
  deepl_api_key: <Deep Pro API key>
388
409
  ```
389
410
 
411
+ <a name="yandex-translation-config"></a>
412
+ ### Yandex Translate
413
+
414
+ `i18n-tasks translate-missing` requires a Yandex API key, get it at [Yandex](https://tech.yandex.com/translate).
415
+
416
+ ```yaml
417
+ # config/i18n-tasks.yml
418
+ translation:
419
+ yandex_api_key: <Yandex API key>
420
+ ```
421
+
390
422
  ## Interactive console
391
423
 
392
424
  `i18n-tasks irb` starts an IRB session in i18n-tasks context. Type `guide` for more information.
@@ -405,8 +437,6 @@ Custom tasks can be added easily, see the examples [on the wiki](https://github.
405
437
  [badge-travis]: https://img.shields.io/travis/glebm/i18n-tasks.svg
406
438
  [coverage]: https://codeclimate.com/github/glebm/i18n-tasks
407
439
  [badge-coverage]: https://api.codeclimate.com/v1/badges/5d173e90ada8df07cedc/test_coverage
408
- [gemnasium]: https://gemnasium.com/glebm/i18n-tasks
409
- [badge-gemnasium]: https://gemnasium.com/glebm/i18n-tasks.svg
410
440
  [config]: https://github.com/glebm/i18n-tasks/blob/master/templates/config/i18n-tasks.yml
411
441
  [wiki]: https://github.com/glebm/i18n-tasks/wiki "i18n-tasks wiki"
412
442
  [i18n-gem]: https://github.com/svenfuchs/i18n "svenfuchs/i18n on Github"
@@ -120,3 +120,9 @@ en:
120
120
  none: Every translation is in use.
121
121
  usages:
122
122
  none: No key usages found.
123
+ yandex_translate:
124
+ errors:
125
+ no_api_key: >-
126
+ Set Yandex API key via YANDEX_API_KEY environment variable or translation.yandex_api_key
127
+ in config/i18n-tasks.yml. Get the key at https://tech.yandex.com/translate.
128
+ no_results: Yandex returned no results.
@@ -69,6 +69,8 @@ ru:
69
69
  invalid_format: 'Неизвестный формат %{invalid}. Форматы: %{valid}.'
70
70
  invalid_locale: Неверный язык %{invalid}
71
71
  invalid_missing_type:
72
+ few: 'Неизвестные типы: %{invalid}. Типы: %{valid}.'
73
+ many: 'Неизвестные типы: %{invalid}. Типы: %{valid}.'
72
74
  one: 'Неизвестный тип %{invalid}. Типы: %{valid}.'
73
75
  other: 'Неизвестные типы: %{invalid}. Типы: %{valid}.'
74
76
  pass_forest: Передайте дерево
@@ -108,6 +110,8 @@ ru:
108
110
  none: Всё переведено.
109
111
  remove_unused:
110
112
  confirm:
113
+ few: Переводы (%{count}) будут удалены из %{locales}.
114
+ many: Переводы (%{count}) будут удалены из %{locales}.
111
115
  one: "%{count} перевод будут удалён из %{locales}."
112
116
  other: Переводы (%{count}) будут удалены из %{locales}.
113
117
  noop: Нет неиспользуемых ключей
@@ -118,3 +122,9 @@ ru:
118
122
  none: Все переводы используются.
119
123
  usages:
120
124
  none: Не найдено использований.
125
+ yandex_translate:
126
+ errors:
127
+ no_api_key: |-
128
+ Установить ключ API Яндекса с помощью переменной среды YANDEX_API_KEY или translation.yandex_api_key
129
+ в config / i18n-tasks.yml. Получите ключ по адресу https://tech.yandex.com/translate/.
130
+ no_results: Яндекс не дал результатов.
@@ -19,13 +19,13 @@ Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength
19
19
  TEXT
20
20
  s.post_install_message = <<~TEXT
21
21
  # Install default configuration:
22
- cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
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
- cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
24
+ cp $(bundle exec i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
25
25
  TEXT
26
26
  s.homepage = 'https://github.com/glebm/i18n-tasks'
27
27
  s.metadata = { 'issue_tracker' => 'https://github.com/glebm/i18n-tasks' } if s.respond_to?(:metadata=)
28
- s.required_ruby_version = '~> 2.3' if s.respond_to?(:required_ruby_version=)
28
+ s.required_ruby_version = '>= 2.3', '< 4.0' if s.respond_to?(:required_ruby_version=)
29
29
 
30
30
  s.files = `git ls-files`.split($/)
31
31
  s.files -= s.files.grep(%r{^(doc/|\.|spec/)}) + %w[CHANGES.md config/i18n-tasks.yml Gemfile]
@@ -39,15 +39,19 @@ TEXT
39
39
  s.add_dependency 'highline', '>= 2.0.0'
40
40
  s.add_dependency 'i18n'
41
41
  s.add_dependency 'parser', '>= 2.2.3.0'
42
+ s.add_dependency 'rails-i18n'
42
43
  s.add_dependency 'rainbow', '>= 2.2.2', '< 4.0'
43
44
  s.add_dependency 'terminal-table', '>= 1.5.1'
44
45
  s.add_development_dependency 'axlsx', '~> 2.0'
45
- s.add_development_dependency 'bundler', '~> 1.3'
46
- s.add_development_dependency 'deepl-rb', '>= 2.1.0'
47
- s.add_development_dependency 'easy_translate', '>= 0.5.1'
46
+ s.add_development_dependency 'bundler', '~> 2.0', '>= 2.0.1'
48
47
  s.add_development_dependency 'rake'
49
48
  s.add_development_dependency 'rspec', '~> 3.3'
50
49
  s.add_development_dependency 'rubocop', '~> 0.53.0'
51
50
  s.add_development_dependency 'simplecov'
52
51
  s.add_development_dependency 'yard'
52
+
53
+ # Translation backends
54
+ s.add_development_dependency 'deepl-rb', '>= 2.1.0'
55
+ s.add_development_dependency 'easy_translate', '>= 0.5.1' # Google Translate
56
+ s.add_development_dependency 'yandex-translator', '>= 0.3.3'
53
57
  end
@@ -64,6 +64,7 @@ require 'i18n/tasks/base_task'
64
64
 
65
65
  # Add internal locale data to i18n gem load path
66
66
  require 'i18n'
67
+
67
68
  Dir[File.join(I18n::Tasks.gem_path, 'config', 'locales', '*.yml')].each do |locale_file|
68
69
  I18n.config.load_path << locale_file
69
70
  end
@@ -22,7 +22,7 @@ module I18n::Tasks
22
22
  if opts.empty? || method(name).arity.zero?
23
23
  send name
24
24
  else
25
- send name, opts
25
+ send name, **opts
26
26
  end
27
27
  end
28
28
 
@@ -17,10 +17,10 @@ module I18n::Tasks
17
17
  fail CommandError, t('i18n_tasks.health.no_keys_detected') if stats[:key_count].zero?
18
18
  terminal_report.forest_stats forest, stats
19
19
  [
20
- missing(opt),
21
- unused(opt),
22
- check_consistent_interpolations(opt),
23
- check_normalized(opt)
20
+ missing(**opt),
21
+ unused(**opt),
22
+ check_consistent_interpolations(**opt),
23
+ check_normalized(**opt)
24
24
  ].detect { |result| result == :exit_1 }
25
25
  end
26
26
  end
@@ -12,7 +12,7 @@ module I18n::Tasks
12
12
  args: %i[locales out_format]
13
13
 
14
14
  def check_consistent_interpolations(opt = {})
15
- forest = i18n.inconsistent_interpolations(opt.slice(:locales, :base_locale))
15
+ forest = i18n.inconsistent_interpolations(**opt.slice(:locales, :base_locale))
16
16
  print_forest forest, opt, :inconsistent_interpolations
17
17
  :exit_1 unless forest.empty?
18
18
  end
@@ -28,7 +28,7 @@ module I18n::Tasks
28
28
  args: %i[locales out_format missing_types]
29
29
 
30
30
  def missing(opt = {})
31
- forest = i18n.missing_keys(opt.slice(:locales, :base_locale, :types))
31
+ forest = i18n.missing_keys(**opt.slice(:locales, :base_locale, :types))
32
32
  print_forest forest, opt, :missing_keys
33
33
  :exit_1 unless forest.empty?
34
34
  end
@@ -32,7 +32,7 @@ module I18n::Tasks
32
32
  args: %i[locales out_format strict]
33
33
 
34
34
  def unused(opt = {})
35
- forest = i18n.unused_keys(opt.slice(:locales, :strict))
35
+ forest = i18n.unused_keys(**opt.slice(:locales, :strict))
36
36
  print_forest forest, opt, :unused_keys
37
37
  :exit_1 unless forest.empty?
38
38
  end
@@ -40,10 +40,16 @@ module I18n::Tasks
40
40
  cmd :remove_unused,
41
41
  pos: '[locale ...]',
42
42
  desc: t('i18n_tasks.cmd.desc.remove_unused'),
43
- args: %i[locales out_format strict keep_order confirm]
43
+ args: %i[locales out_format strict keep_order confirm pattern]
44
+
45
+ def remove_unused(opt = {}) # rubocop:disable Metrics/AbcSize
46
+ unused_keys = i18n.unused_keys(**opt.slice(:locales, :strict))
47
+
48
+ if opt[:pattern]
49
+ pattern_re = i18n.compile_key_pattern(opt[:pattern])
50
+ unused_keys = unused_keys.select_keys { |full_key, _node| full_key =~ pattern_re }
51
+ end
44
52
 
45
- def remove_unused(opt = {})
46
- unused_keys = i18n.unused_keys(opt.slice(:locales, :strict))
47
53
  if unused_keys.present?
48
54
  terminal_report.unused_keys(unused_keys)
49
55
  confirm_remove_unused!(unused_keys, opt)
@@ -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, **opts)
46
+ proc { I18n.t(*args, **opts) }
47
47
  end
48
48
 
49
49
  # if class is a module, merge DSL definitions when it is included
@@ -59,6 +59,7 @@ module I18n::Tasks::Configuration # rubocop:disable Metrics/ModuleLength
59
59
  conf = (config[:translation] || {}).with_indifferent_access
60
60
  conf[:google_translate_api_key] = ENV['GOOGLE_TRANSLATE_API_KEY'] if ENV.key?('GOOGLE_TRANSLATE_API_KEY')
61
61
  conf[:deepl_api_key] = ENV['DEEPL_AUTH_KEY'] if ENV.key?('DEEPL_AUTH_KEY')
62
+ conf[:yandex_api_key] = ENV['YANDEX_API_KEY'] if ENV.key?('YANDEX_API_KEY')
62
63
  conf
63
64
  end
64
65
  end
@@ -9,7 +9,7 @@ module I18n::Tasks
9
9
  # @return [Hash] locale tree
10
10
  def parse(str, options)
11
11
  if YAML.method(:load).arity.abs == 2
12
- YAML.load(str, options || {})
12
+ YAML.load(str, **(options || {}))
13
13
  else
14
14
  # older jruby and rbx 2.2.7 do not accept options
15
15
  YAML.load(str)
@@ -38,6 +38,8 @@ module I18n
38
38
  # @return [Hash]
39
39
  def load_file(path)
40
40
  adapter_parse read_file(path), self.class.adapter_name_for_path(path)
41
+ rescue CommandError => e
42
+ raise(e.class, "#{e.message} (file: #{path})")
41
43
  end
42
44
 
43
45
  # @return [String]
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'i18n/tasks/data/tree/traversal'
4
4
  require 'i18n/tasks/data/tree/siblings'
5
- require 'i18n/tasks/rainbow_utils'
6
5
  module I18n::Tasks::Data::Tree
7
6
  class Node # rubocop:disable Metrics/ClassLength
8
7
  include Enumerable
@@ -28,7 +27,7 @@ module I18n::Tasks::Data::Tree
28
27
  end
29
28
 
30
29
  def derive(new_attr = {})
31
- self.class.new(attributes.merge(new_attr))
30
+ self.class.new(**attributes.merge(new_attr))
32
31
  end
33
32
 
34
33
  def children=(children)
@@ -164,7 +163,7 @@ module I18n::Tasks::Data::Tree
164
163
 
165
164
  def inspect(level = 0)
166
165
  label = if key.nil?
167
- I18n::Tasks::RainbowUtils.faint_color('∅')
166
+ Rainbow('∅').faint
168
167
  else
169
168
  [Rainbow(key).color(1 + level % 15),
170
169
  (": #{format_value_for_inspect(value)}" if leaf?),
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'i18n/tasks/data/tree/traversal'
4
- require 'i18n/tasks/rainbow_utils'
5
4
  module I18n::Tasks::Data::Tree
6
5
  # A list of nodes
7
6
  class Nodes
@@ -48,7 +47,7 @@ module I18n::Tasks::Data::Tree
48
47
  if present?
49
48
  map(&:inspect) * "\n"
50
49
  else
51
- I18n::Tasks::RainbowUtils.faint_color('{∅}')
50
+ Rainbow('{∅}').faint
52
51
  end
53
52
  end
54
53
 
@@ -11,6 +11,7 @@ module I18n::Tasks::Data::Tree
11
11
  # siblings' keys are unique
12
12
  class Siblings < Nodes # rubocop:disable Metrics/ClassLength
13
13
  include ::I18n::Tasks::SplitKey
14
+ include ::I18n::Tasks::PluralKeys
14
15
 
15
16
  attr_reader :parent, :key_to_node
16
17
 
@@ -100,12 +101,12 @@ module I18n::Tasks::Data::Tree
100
101
  key: key_part,
101
102
  parent: parent,
102
103
  children: [],
103
- warn_about_add_children_to_leaf: @warn_add_children_to_leaf
104
+ warn_about_add_children_to_leaf: @warn_about_add_children_to_leaf
104
105
  )
105
106
  append! child
106
107
  end
107
108
  unless child.children
108
- warn_add_children_to_leaf child if @warn_about_add_children_to_leaf
109
+ conditionally_warn_add_children_to_leaf(child, [])
109
110
  child.children = []
110
111
  end
111
112
  child.children.set rest, node
@@ -187,7 +188,7 @@ module I18n::Tasks::Data::Tree
187
188
  if our.children
188
189
  our.children.merge!(node.children)
189
190
  else
190
- warn_add_children_to_leaf our
191
+ conditionally_warn_add_children_to_leaf(our, node.children)
191
192
  our.children = node.children
192
193
  end
193
194
  elsif on_leaves_merge
@@ -228,6 +229,13 @@ module I18n::Tasks::Data::Tree
228
229
  end
229
230
  end
230
231
 
232
+ def conditionally_warn_add_children_to_leaf(node, children)
233
+ return unless @warn_about_add_children_to_leaf
234
+ return if plural_forms?(children)
235
+
236
+ warn_add_children_to_leaf(node)
237
+ end
238
+
231
239
  def warn_add_children_to_leaf(node)
232
240
  ::I18n::Tasks::Logging.log_warn "'#{node.full_key}' was a leaf, now has children (value <- scope conflict)"
233
241
  end
@@ -265,7 +273,7 @@ module I18n::Tasks::Data::Tree
265
273
  build_forest(opts) do |forest|
266
274
  key_attrs.each do |(full_key, attr)|
267
275
  fail "Invalid key #{full_key.inspect}" if full_key.end_with?('.')
268
- node = ::I18n::Tasks::Data::Tree::Node.new(attr.merge(key: split_key(full_key).last))
276
+ node = ::I18n::Tasks::Data::Tree::Node.new(**attr.merge(key: split_key(full_key).last))
269
277
  yield(full_key, node) if block
270
278
  forest[full_key] = node
271
279
  end
@@ -60,14 +60,12 @@ module I18n::Tasks
60
60
  self
61
61
  end
62
62
 
63
- def key_names(opts = {})
64
- opts[:root] = false unless opts.key?(:root)
65
- keys(opts).map { |key, _node| key }
63
+ def key_names(root: false)
64
+ keys(root: root).map { |key, _node| key }
66
65
  end
67
66
 
68
- def key_values(opts = {})
69
- opts[:root] = false unless opts.key?(:root)
70
- keys(opts).map { |key, node| [key, node.value] }
67
+ def key_values(root: false)
68
+ keys(root: root).map { |key, node| [key, node.value] }
71
69
  end
72
70
 
73
71
  def root_key_values(sort = false)
@@ -146,12 +144,12 @@ module I18n::Tasks
146
144
  # @return [Siblings]
147
145
  def intersect_keys(other_tree, key_opts = {}, &block)
148
146
  if block
149
- select_keys(key_opts) do |key, node|
147
+ select_keys(**key_opts.slice(:root)) do |key, node|
150
148
  other_node = other_tree[key]
151
149
  other_node && yield(key, node, other_node)
152
150
  end
153
151
  else
154
- select_keys(key_opts) { |key, _node| other_tree[key] }
152
+ select_keys(**key_opts.slice(:root)) { |key, _node| other_tree[key] }
155
153
  end
156
154
  end
157
155
 
@@ -2,21 +2,22 @@
2
2
 
3
3
  require 'set'
4
4
  module I18n::Tasks
5
- module MissingKeys
6
- MISSING_TYPES = {
7
- used: { glyph: '✗', summary: 'used in code but missing from base locale' },
8
- diff: { glyph: '∅', summary: 'translated in one locale but not in the other' }
9
- }.freeze
5
+ module MissingKeys # rubocop:disable Metrics/ModuleLength
6
+ MISSING_TYPES = %w[
7
+ used
8
+ diff
9
+ plural
10
+ ].freeze
10
11
 
11
12
  def self.missing_keys_types
12
- @missing_keys_types ||= MISSING_TYPES.keys
13
+ @missing_keys_types ||= MISSING_TYPES
13
14
  end
14
15
 
15
16
  def missing_keys_types
16
17
  MissingKeys.missing_keys_types
17
18
  end
18
19
 
19
- # @param types [:used, :diff] all if `nil`.
20
+ # @param types [:used, :diff, :plural] all if `nil`.
20
21
  # @return [Siblings]
21
22
  def missing_keys(locales: nil, types: nil, base_locale: nil)
22
23
  locales ||= self.locales
@@ -55,6 +56,39 @@ module I18n::Tasks
55
56
  end
56
57
  end
57
58
 
59
+ def missing_plural_forest(locales, _base = base_locale)
60
+ locales.each_with_object(empty_forest) do |locale, forest|
61
+ required_keys = required_plural_keys_for_locale(locale)
62
+ next if required_keys.empty?
63
+ tree = empty_forest
64
+ plural_nodes data[locale] do |node|
65
+ children = node.children
66
+ present_keys = Set.new(children.map { |c| c.key.to_sym })
67
+ next if present_keys.superset?(required_keys)
68
+ tree[node.full_key] = node.derive(
69
+ value: children.to_hash,
70
+ children: nil,
71
+ data: node.data.merge(missing_keys: (required_keys - present_keys).to_a)
72
+ )
73
+ end
74
+ tree.set_root_key!(locale, type: :missing_plural)
75
+ forest.merge!(tree)
76
+ end
77
+ end
78
+
79
+ def required_plural_keys_for_locale(locale)
80
+ @plural_keys_for_locale ||= {}
81
+ return @plural_keys_for_locale[locale] if @plural_keys_for_locale.key?(locale)
82
+
83
+ @plural_keys_for_locale[locale] = plural_keys_for_locale(locale)
84
+ end
85
+
86
+ # Loads rails-i18n pluralization config for the given locale.
87
+ def load_rails_i18n_pluralization!(locale)
88
+ path = File.join(Gem::Specification.find_by_name('rails-i18n').gem_dir, 'rails', 'pluralization', "#{locale}.rb")
89
+ eval(File.read(path), binding, path) # rubocop:disable Security/Eval
90
+ end
91
+
58
92
  # keys present in compared_to, but not in locale
59
93
  def missing_diff_tree(locale, compared_to = base_locale)
60
94
  data[compared_to].select_keys do |key, _node|
@@ -85,7 +119,7 @@ module I18n::Tasks
85
119
  end
86
120
 
87
121
  def locale_key_missing?(locale, key)
88
- !key_value?(key, locale) && !external_key?(key) && !ignore_key?(key, :missing)
122
+ !key_value?(key, locale) && !external_key?(key, locale) && !ignore_key?(key, :missing)
89
123
  end
90
124
 
91
125
  # @param [::I18n::Tasks::Data::Tree::Siblings] forest
@@ -116,5 +150,32 @@ module I18n::Tasks
116
150
  end
117
151
  forest
118
152
  end
153
+
154
+ private
155
+
156
+ def plural_keys_for_locale(locale)
157
+ configuration = load_rails_i18n_pluralization!(locale)
158
+ if configuration[locale.to_sym].nil?
159
+ alternate_locale = alternate_locale_from(locale)
160
+ return Set.new if configuration[alternate_locale.to_sym].nil?
161
+
162
+ return set_from_rails_i18n_pluralization(configuration, alternate_locale)
163
+ end
164
+ set_from_rails_i18n_pluralization(configuration, locale)
165
+ rescue SystemCallError, IOError
166
+ Set.new
167
+ end
168
+
169
+ def alternate_locale_from(locale)
170
+ re = /(\w{2})-*(\w{2,3})*/
171
+ match = locale.match(re)
172
+ language_code = match[1]
173
+ country_code = match[2]
174
+ "#{language_code}-#{country_code.upcase}"
175
+ end
176
+
177
+ def set_from_rails_i18n_pluralization(configuration, locale)
178
+ Set.new(configuration[locale.to_sym][:i18n][:plural][:keys])
179
+ end
119
180
  end
120
181
  end
@@ -2,8 +2,10 @@
2
2
 
3
3
  require 'set'
4
4
  module I18n::Tasks::PluralKeys
5
- PLURAL_KEY_SUFFIXES = Set.new %w[zero one two few many other]
6
- PLURAL_KEY_RE = /\.(?:#{PLURAL_KEY_SUFFIXES.to_a * '|'})$/
5
+ # Ref: http://cldr.unicode.org/index/cldr-spec/plural-rules
6
+ CLDR_CATEGORY_KEYS = %w[zero one two few many other].freeze
7
+ PLURAL_KEY_SUFFIXES = Set.new CLDR_CATEGORY_KEYS
8
+ PLURAL_KEY_RE = /\.(?:#{CLDR_CATEGORY_KEYS * '|'})$/
7
9
 
8
10
  def collapse_plural_nodes!(tree)
9
11
  tree.leaves.map(&:parent).compact.uniq.each do |node|
@@ -31,10 +33,29 @@ module I18n::Tasks::PluralKeys
31
33
  end
32
34
  end
33
35
 
36
+ # @param [::I18n::Tasks::Data::Tree::Traversal] tree
37
+ # @yieldparam node [::I18n::Tasks::Data::Tree::Node] plural node
38
+ def plural_nodes(tree)
39
+ return to_enum(:plural_nodes, tree) unless block_given?
40
+ visited = Set.new
41
+ tree.leaves do |node|
42
+ parent = node.parent
43
+ next if !parent || visited.include?(parent)
44
+ yield parent if plural_forms?(parent.children)
45
+ visited.add(parent)
46
+ end
47
+ self
48
+ end
49
+
34
50
  def plural_forms?(s)
51
+ return false if non_plural_other?(s)
35
52
  s.present? && s.all? { |node| node.leaf? && plural_suffix?(node.key) }
36
53
  end
37
54
 
55
+ def non_plural_other?(s)
56
+ s.size == 1 && s.first.leaf? && (!s.first.value.is_a?(String) || !s.first.value.include?('%{count}'))
57
+ end
58
+
38
59
  def plural_suffix?(key)
39
60
  PLURAL_KEY_SUFFIXES.include?(key)
40
61
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'i18n/tasks/reports/base'
4
- require 'i18n/tasks/rainbow_utils'
5
4
  require 'terminal-table'
6
5
  module I18n
7
6
  module Tasks
@@ -34,11 +33,6 @@ module I18n
34
33
  end
35
34
  end
36
35
 
37
- def icon(type)
38
- glyph = missing_type_info(type)[:glyph]
39
- { missing_used: Rainbow(glyph).red, missing_diff: Rainbow(glyph).yellow }[type]
40
- end
41
-
42
36
  def used_keys(used_tree = task.used_tree)
43
37
  # For the used tree we may have usage nodes that are not leaves as references.
44
38
  keys_nodes = used_tree.nodes.select { |node| node.data[:occurrences].present? }.map do |node|
@@ -81,11 +75,11 @@ module I18n
81
75
 
82
76
  def forest_stats(forest, stats = task.forest_stats(forest))
83
77
  text = if stats[:locale_count] == 1
84
- I18n.t('i18n_tasks.data_stats.text_single_locale', stats)
78
+ I18n.t('i18n_tasks.data_stats.text_single_locale', **stats)
85
79
  else
86
- I18n.t('i18n_tasks.data_stats.text', stats)
80
+ I18n.t('i18n_tasks.data_stats.text', **stats)
87
81
  end
88
- title = Rainbow(I18n.t('i18n_tasks.data_stats.title', stats.slice(:locales))).bright
82
+ title = Rainbow(I18n.t('i18n_tasks.data_stats.title', **stats.slice(:locales))).bright
89
83
  print_info "#{Rainbow(title).cyan} #{Rainbow(text).cyan}"
90
84
  end
91
85
 
@@ -114,6 +108,8 @@ module I18n
114
108
  def missing_key_info(leaf)
115
109
  if leaf[:type] == :missing_used
116
110
  first_occurrence leaf
111
+ elsif leaf[:type] == :missing_plural
112
+ leaf[:data][:missing_keys].join(', ')
117
113
  else
118
114
  "#{Rainbow(leaf[:data][:missing_diff_locale]).cyan} "\
119
115
  "#{format_value(leaf[:value].is_a?(String) ? leaf[:value].strip : leaf[:value])}"
@@ -173,7 +169,7 @@ module I18n
173
169
  end
174
170
 
175
171
  def print_title(title)
176
- log_stderr "#{Rainbow(title.strip).bright} #{I18n::Tasks::RainbowUtils.faint_color('|')} " \
172
+ log_stderr "#{Rainbow(title.strip).bright} #{Rainbow('|').faint} " \
177
173
  "#{"i18n-tasks v#{I18n::Tasks::VERSION}"}"
178
174
  end
179
175
 
@@ -12,13 +12,15 @@ module I18n::Tasks::Scanners
12
12
  include OccurrenceFromPosition
13
13
  include RubyKeyLiterals
14
14
 
15
- TRANSLATE_CALL_RE = /(?<=^|[^\w'\-.]|[^\w'\-]I18n\.|I18n\.)t(?:ranslate)?/
15
+ TRANSLATE_CALL_RE = /(?<=^|[^\w'\-.]|[^\w'\-]I18n\.|I18n\.)t(?:!|ranslate!?)?/
16
16
  IGNORE_LINES = {
17
- 'opal' => /^\s*#(?!\si18n-tasks-use)/,
18
- 'haml' => /^\s*-\s*#(?!\si18n-tasks-use)/,
19
- 'slim' => %r{^\s*(?:-#|/)(?!\si18n-tasks-use)},
20
17
  'coffee' => /^\s*#(?!\si18n-tasks-use)/,
21
- 'erb' => /^\s*<%\s*#(?!\si18n-tasks-use)/
18
+ 'erb' => /^\s*<%\s*#(?!\si18n-tasks-use)/,
19
+ 'es6' => %r{^\s*//(?!\si18n-tasks-use)},
20
+ 'haml' => /^\s*-\s*#(?!\si18n-tasks-use)/,
21
+ 'js' => %r{^\s*//(?!\si18n-tasks-use)},
22
+ 'opal' => /^\s*#(?!\si18n-tasks-use)/,
23
+ 'slim' => %r{^\s*(?:-#|/)(?!\si18n-tasks-use)}
22
24
  }.freeze
23
25
 
24
26
  def initialize(**args)
@@ -15,10 +15,10 @@ module I18n::Tasks::Scanners
15
15
  include AST::Sexp
16
16
 
17
17
  MAGIC_COMMENT_PREFIX = /\A.\s*i18n-tasks-use\s+/
18
- RECEIVER_MESSAGES = [nil, AST::Node.new(:const, [nil, :I18n])].product(%i[t translate])
18
+ RECEIVER_MESSAGES = [nil, AST::Node.new(:const, [nil, :I18n])].product(%i[t t! translate translate!])
19
19
 
20
20
  def initialize(**args)
21
- super(args)
21
+ super(**args)
22
22
  @parser = ::Parser::CurrentRuby.new
23
23
  @magic_comment_parser = ::Parser::CurrentRuby.new
24
24
  @call_finder = RubyAstCallFinder.new(
@@ -2,12 +2,13 @@
2
2
 
3
3
  require 'i18n/tasks/translators/deepl_translator.rb'
4
4
  require 'i18n/tasks/translators/google_translator.rb'
5
+ require 'i18n/tasks/translators/yandex_translator.rb'
5
6
 
6
7
  module I18n::Tasks
7
8
  module Translation
8
9
  # @param [I18n::Tasks::Tree::Siblings] forest to translate to the locales of its root nodes
9
10
  # @param [String] from locale
10
- # @param [:deepl, :google] backend
11
+ # @param [:deepl, :google, :yandex] backend
11
12
  # @return [I18n::Tasks::Tree::Siblings] translated forest
12
13
  def translate_forest(forest, from:, backend: :google)
13
14
  case backend
@@ -15,6 +16,8 @@ module I18n::Tasks
15
16
  Translators::DeeplTranslator.new(self).translate_forest(forest, from)
16
17
  when :google
17
18
  Translators::GoogleTranslator.new(self).translate_forest(forest, from)
19
+ when :yandex
20
+ Translators::YandexTranslator.new(self).translate_forest(forest, from)
18
21
  else
19
22
  fail CommandError, "invalid backend: #{backend}"
20
23
  end
@@ -17,7 +17,12 @@ module I18n::Tasks::Translators
17
17
  protected
18
18
 
19
19
  def translate_values(list, from:, to:, **options)
20
- DeepL.translate(list, to_deepl_compatible_locale(from), to_deepl_compatible_locale(to), options).map(&:text)
20
+ result = DeepL.translate(list, to_deepl_compatible_locale(from), to_deepl_compatible_locale(to), options).map(&:text)
21
+ if result.is_a?(DeepL::Resources::Text)
22
+ result.text
23
+ else
24
+ result.map(&:text)
25
+ end
21
26
  end
22
27
 
23
28
  def options_for_translate_values(**options)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'i18n/tasks/translators/base_translator'
4
+
5
+ module I18n::Tasks::Translators
6
+ class YandexTranslator < BaseTranslator
7
+ def initialize(*)
8
+ begin
9
+ require 'yandex-translator'
10
+ rescue LoadError
11
+ raise ::I18n::Tasks::CommandError, "Add gem 'yandex-translator' to your Gemfile to use this command"
12
+ end
13
+ super
14
+ end
15
+
16
+ protected
17
+
18
+ def translate_values(list, **options)
19
+ list.map { |item| translator.translate(item, options) }
20
+ end
21
+
22
+ def options_for_translate_values(from:, to:, **options)
23
+ options.merge(
24
+ from: to_yandex_compatible_locale(from),
25
+ to: to_yandex_compatible_locale(to)
26
+ )
27
+ end
28
+
29
+ def options_for_html
30
+ { format: 'html' }
31
+ end
32
+
33
+ def options_for_plain
34
+ { format: 'plain' }
35
+ end
36
+
37
+ def no_results_error_message
38
+ I18n.t('i18n_tasks.yandex_translate.errors.no_results')
39
+ end
40
+
41
+ private
42
+
43
+ # Convert 'es-ES' to 'es'
44
+ def to_yandex_compatible_locale(locale)
45
+ return locale unless locale.include?('-')
46
+ locale.split('-', 2).first
47
+ end
48
+
49
+ def translator
50
+ @translator ||= Yandex::Translator.new(api_key)
51
+ end
52
+
53
+ def api_key
54
+ @api_key ||= begin
55
+ key = @i18n_tasks.translation_config[:yandex_api_key]
56
+ fail ::I18n::Tasks::CommandError, I18n.t('i18n_tasks.yandex_translate.errors.no_api_key') if key.blank?
57
+ key
58
+ end
59
+ end
60
+ end
61
+ end
@@ -25,7 +25,8 @@ module I18n::Tasks
25
25
  }.freeze
26
26
 
27
27
  ALWAYS_EXCLUDE = %w[*.jpg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less
28
- *.yml *.json *.zip *.tar.gz *.swf *.flv].freeze
28
+ *.yml *.json *.zip *.tar.gz *.swf *.flv *.mp3 *.wav *.flac *.webm *.mp4 *.ogg *.opus
29
+ *.webp].freeze
29
30
 
30
31
  # Find all keys in the source and return a forest with the keys in absolute form and their occurrences.
31
32
  #
@@ -2,6 +2,6 @@
2
2
 
3
3
  module I18n
4
4
  module Tasks
5
- VERSION = '0.9.27'
5
+ VERSION = '0.9.32'
6
6
  end
7
7
  end
@@ -32,7 +32,7 @@ data:
32
32
  # This data is not considered unused and is never written to.
33
33
  external:
34
34
  ## Example (replace %#= with %=):
35
- # - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
35
+ # - "<%#= %x[bundle info vagrant --path].chomp %>/templates/locales/%{locale}.yml"
36
36
 
37
37
  ## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, or a custom class.
38
38
  # router: conservative_router
@@ -23,7 +23,14 @@ class I18nTest < ActiveSupport::TestCase
23
23
  non_normalized = @i18n.non_normalized_paths
24
24
  error_message = "The following files need to be normalized:\n" \
25
25
  "#{non_normalized.map { |path| " #{path}" }.join("\n")}\n" \
26
- 'Please run `i18n-tasks normalize` to fix'
26
+ "Please run `i18n-tasks normalize' to fix"
27
27
  assert_empty non_normalized, error_message
28
28
  end
29
+
30
+ def test_no_inconsistent_interpolations
31
+ inconsistent_interpolations = @i18n.inconsistent_interpolations
32
+ error_message = "#{inconsistent_interpolations.leaves.count} i18n keys have inconsistent interpolations.\n" \
33
+ "Please run `i18n-tasks check-consistent-interpolations' to show them"
34
+ assert_empty inconsistent_interpolations, error_message
35
+ end
29
36
  end
@@ -2,10 +2,11 @@
2
2
 
3
3
  require 'i18n/tasks'
4
4
 
5
- RSpec.describe 'I18n' do
5
+ RSpec.describe I18n do
6
6
  let(:i18n) { I18n::Tasks::BaseTask.new }
7
7
  let(:missing_keys) { i18n.missing_keys }
8
8
  let(:unused_keys) { i18n.unused_keys }
9
+ let(:inconsistent_interpolations) { i18n.inconsistent_interpolations }
9
10
 
10
11
  it 'does not have missing keys' do
11
12
  expect(missing_keys).to be_empty,
@@ -21,7 +22,13 @@ RSpec.describe 'I18n' do
21
22
  non_normalized = i18n.non_normalized_paths
22
23
  error_message = "The following files need to be normalized:\n" \
23
24
  "#{non_normalized.map { |path| " #{path}" }.join("\n")}\n" \
24
- 'Please run `i18n-tasks normalize` to fix'
25
+ "Please run `i18n-tasks normalize' to fix"
25
26
  expect(non_normalized).to be_empty, error_message
26
27
  end
28
+
29
+ it 'does not have inconsistent interpolations' do
30
+ error_message = "#{inconsistent_interpolations.leaves.count} i18n keys have inconsistent interpolations.\n" \
31
+ "Run `i18n-tasks check-consistent-interpolations' to show them"
32
+ expect(inconsistent_interpolations).to be_empty, error_message
33
+ end
27
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.27
4
+ version: 0.9.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - glebm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-23 00:00:00.000000000 Z
11
+ date: 2020-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: 2.2.3.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rails-i18n
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rainbow
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -148,42 +162,20 @@ dependencies:
148
162
  requirements:
149
163
  - - "~>"
150
164
  - !ruby/object:Gem::Version
151
- version: '1.3'
152
- type: :development
153
- prerelease: false
154
- version_requirements: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - "~>"
157
- - !ruby/object:Gem::Version
158
- version: '1.3'
159
- - !ruby/object:Gem::Dependency
160
- name: deepl-rb
161
- requirement: !ruby/object:Gem::Requirement
162
- requirements:
165
+ version: '2.0'
163
166
  - - ">="
164
167
  - !ruby/object:Gem::Version
165
- version: 2.1.0
168
+ version: 2.0.1
166
169
  type: :development
167
170
  prerelease: false
168
171
  version_requirements: !ruby/object:Gem::Requirement
169
172
  requirements:
170
- - - ">="
171
- - !ruby/object:Gem::Version
172
- version: 2.1.0
173
- - !ruby/object:Gem::Dependency
174
- name: easy_translate
175
- requirement: !ruby/object:Gem::Requirement
176
- requirements:
177
- - - ">="
173
+ - - "~>"
178
174
  - !ruby/object:Gem::Version
179
- version: 0.5.1
180
- type: :development
181
- prerelease: false
182
- version_requirements: !ruby/object:Gem::Requirement
183
- requirements:
175
+ version: '2.0'
184
176
  - - ">="
185
177
  - !ruby/object:Gem::Version
186
- version: 0.5.1
178
+ version: 2.0.1
187
179
  - !ruby/object:Gem::Dependency
188
180
  name: rake
189
181
  requirement: !ruby/object:Gem::Requirement
@@ -254,6 +246,48 @@ dependencies:
254
246
  - - ">="
255
247
  - !ruby/object:Gem::Version
256
248
  version: '0'
249
+ - !ruby/object:Gem::Dependency
250
+ name: deepl-rb
251
+ requirement: !ruby/object:Gem::Requirement
252
+ requirements:
253
+ - - ">="
254
+ - !ruby/object:Gem::Version
255
+ version: 2.1.0
256
+ type: :development
257
+ prerelease: false
258
+ version_requirements: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - ">="
261
+ - !ruby/object:Gem::Version
262
+ version: 2.1.0
263
+ - !ruby/object:Gem::Dependency
264
+ name: easy_translate
265
+ requirement: !ruby/object:Gem::Requirement
266
+ requirements:
267
+ - - ">="
268
+ - !ruby/object:Gem::Version
269
+ version: 0.5.1
270
+ type: :development
271
+ prerelease: false
272
+ version_requirements: !ruby/object:Gem::Requirement
273
+ requirements:
274
+ - - ">="
275
+ - !ruby/object:Gem::Version
276
+ version: 0.5.1
277
+ - !ruby/object:Gem::Dependency
278
+ name: yandex-translator
279
+ requirement: !ruby/object:Gem::Requirement
280
+ requirements:
281
+ - - ">="
282
+ - !ruby/object:Gem::Version
283
+ version: 0.3.3
284
+ type: :development
285
+ prerelease: false
286
+ version_requirements: !ruby/object:Gem::Requirement
287
+ requirements:
288
+ - - ">="
289
+ - !ruby/object:Gem::Version
290
+ version: 0.3.3
257
291
  description: |
258
292
  i18n-tasks helps you find and manage missing and unused translations.
259
293
 
@@ -320,7 +354,6 @@ files:
320
354
  - lib/i18n/tasks/logging.rb
321
355
  - lib/i18n/tasks/missing_keys.rb
322
356
  - lib/i18n/tasks/plural_keys.rb
323
- - lib/i18n/tasks/rainbow_utils.rb
324
357
  - lib/i18n/tasks/references.rb
325
358
  - lib/i18n/tasks/reports/base.rb
326
359
  - lib/i18n/tasks/reports/terminal.rb
@@ -349,6 +382,7 @@ files:
349
382
  - lib/i18n/tasks/translators/base_translator.rb
350
383
  - lib/i18n/tasks/translators/deepl_translator.rb
351
384
  - lib/i18n/tasks/translators/google_translator.rb
385
+ - lib/i18n/tasks/translators/yandex_translator.rb
352
386
  - lib/i18n/tasks/unused_keys.rb
353
387
  - lib/i18n/tasks/used_keys.rb
354
388
  - lib/i18n/tasks/version.rb
@@ -362,25 +396,27 @@ metadata:
362
396
  issue_tracker: https://github.com/glebm/i18n-tasks
363
397
  post_install_message: |
364
398
  # Install default configuration:
365
- cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
399
+ cp $(bundle exec i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
366
400
  # Add an RSpec for missing and unused keys:
367
- cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
401
+ cp $(bundle exec i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/
368
402
  rdoc_options: []
369
403
  require_paths:
370
404
  - lib
371
405
  required_ruby_version: !ruby/object:Gem::Requirement
372
406
  requirements:
373
- - - "~>"
407
+ - - ">="
374
408
  - !ruby/object:Gem::Version
375
409
  version: '2.3'
410
+ - - "<"
411
+ - !ruby/object:Gem::Version
412
+ version: '4.0'
376
413
  required_rubygems_version: !ruby/object:Gem::Requirement
377
414
  requirements:
378
415
  - - ">="
379
416
  - !ruby/object:Gem::Version
380
417
  version: '0'
381
418
  requirements: []
382
- rubyforge_project:
383
- rubygems_version: 2.7.7
419
+ rubygems_version: 3.1.2
384
420
  signing_key:
385
421
  specification_version: 4
386
422
  summary: Manage localization and translation with the awesome power of static analysis
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module I18n
4
- module Tasks
5
- module RainbowUtils
6
- # TODO: This method can be removed after below PR is released.
7
- # https://github.com/sickill/rainbow/pull/53
8
- def self.faint_color(str)
9
- presenter = Rainbow(str)
10
- return presenter unless Rainbow.enabled
11
- Rainbow::Presenter.new(Rainbow::StringUtils.wrap_with_sgr(presenter, [2]))
12
- end
13
- end
14
- end
15
- end