i18n-tasks 0.9.27 → 0.9.32

Sign up to get free protection for your applications and to get access to all the features.
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