i18n-tasks 1.0.14 → 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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +138 -39
  3. data/Rakefile +4 -4
  4. data/bin/i18n-tasks +3 -3
  5. data/config/locales/en.yml +17 -1
  6. data/config/locales/ru.yml +18 -1
  7. data/i18n-tasks.gemspec +28 -38
  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 +28 -26
  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 +8 -7
  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 +33 -24
  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 +26 -20
  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 +2 -2
  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 +9 -9
  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 +48 -0
  73. data/lib/i18n/tasks/scanners/prism_scanners/nodes.rb +374 -0
  74. data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +337 -0
  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 +27 -0
  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 +8 -5
  87. data/lib/i18n/tasks/translators/base_translator.rb +43 -13
  88. data/lib/i18n/tasks/translators/deepl_translator.rb +22 -14
  89. data/lib/i18n/tasks/translators/google_translator.rb +178 -26
  90. data/lib/i18n/tasks/translators/openai_translator.rb +56 -31
  91. data/lib/i18n/tasks/translators/watsonx_translator.rb +155 -0
  92. data/lib/i18n/tasks/translators/yandex_translator.rb +13 -9
  93. data/lib/i18n/tasks/unused_keys.rb +1 -1
  94. data/lib/i18n/tasks/used_keys.rb +32 -32
  95. data/lib/i18n/tasks/version.rb +1 -1
  96. data/lib/i18n/tasks.rb +17 -16
  97. data/templates/config/i18n-tasks.yml +14 -2
  98. data/templates/minitest/i18n_test.rb +3 -3
  99. data/templates/rspec/i18n_spec.rb +7 -7
  100. metadata +38 -172
  101. data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +0 -145
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
3
+ require "set"
4
4
  module I18n::Tasks
5
5
  module MissingKeys # rubocop:disable Metrics/ModuleLength
6
6
  MISSING_TYPES = %w[
@@ -21,7 +21,7 @@ module I18n::Tasks
21
21
  # @return [Siblings]
22
22
  def missing_keys(locales: nil, types: nil, base_locale: nil)
23
23
  locales ||= self.locales
24
- types ||= missing_keys_types
24
+ types ||= missing_keys_types
25
25
  base = base_locale || self.base_locale
26
26
  types.inject(empty_forest) do |f, type|
27
27
  f.merge! send(:"missing_#{type}_forest", locales, base)
@@ -88,7 +88,7 @@ module I18n::Tasks
88
88
 
89
89
  # Loads rails-i18n pluralization config for the given locale.
90
90
  def load_rails_i18n_pluralization!(locale)
91
- path = File.join(Gem::Specification.find_by_name('rails-i18n').gem_dir, 'rails', 'pluralization', "#{locale}.rb")
91
+ path = File.join(Gem::Specification.find_by_name("rails-i18n").gem_dir, "rails", "pluralization", "#{locale}.rb")
92
92
  eval(File.read(path), binding, path) # rubocop:disable Security/Eval
93
93
  end
94
94
 
@@ -98,7 +98,7 @@ module I18n::Tasks
98
98
  locale_key_missing? locale, depluralize_key(key, compared_to)
99
99
  end.set_root_key!(locale, type: :missing_diff).keys do |_key, node|
100
100
  # change path and locale to base
101
- data = { locale: locale, missing_diff_locale: node.data[:locale] }
101
+ data = {locale: locale, missing_diff_locale: node.data[:locale]}
102
102
  if node.data.key?(:path)
103
103
  data[:path] = LocalePathname.replace_locale(node.data[:path], node.data[:locale], locale)
104
104
  end
@@ -108,8 +108,24 @@ module I18n::Tasks
108
108
 
109
109
  # keys used in the code missing translations in locale
110
110
  def missing_used_tree(locale)
111
- used_tree(strict: true).select_keys do |key, _node|
112
- locale_key_missing?(locale, key)
111
+ used_tree(strict: true).select_keys do |key, node|
112
+ occurrences = node.data[:occurrences] || []
113
+
114
+ # An occurrence may carry candidate keys (for relative lookups). If any
115
+ # candidate key exists in the locale, the usage is considered present.
116
+ occurrences_all_missing = occurrences.all? do |occ|
117
+ candidates = if occ.respond_to?(:candidate_keys) && occ.candidate_keys.present?
118
+ occ.candidate_keys
119
+ else
120
+ # fallback to the scanned key
121
+ [key]
122
+ end
123
+
124
+ # Occurrence is missing iff all its candidates are missing
125
+ candidates.all? { |c| locale_key_missing?(locale, c) }
126
+ end
127
+
128
+ occurrences_all_missing
113
129
  end.set_root_key!(locale, type: :missing_used)
114
130
  end
115
131
 
@@ -130,7 +146,7 @@ module I18n::Tasks
130
146
  # @yieldreturn [Boolean] whether to collapse the node
131
147
  def collapse_same_key_in_locales!(forest)
132
148
  locales_and_node_by_key = {}
133
- to_remove = []
149
+ to_remove = []
134
150
  forest.each do |root|
135
151
  locale = root.key
136
152
  root.keys do |key, node|
@@ -146,7 +162,7 @@ module I18n::Tasks
146
162
  end
147
163
  forest.remove_nodes_and_emptied_ancestors! to_remove
148
164
  locales_and_node_by_key.each_with_object({}) do |(key, (locales, node)), inv|
149
- (inv[locales.sort.join('+')] ||= []) << [key, node]
165
+ (inv[locales.sort.join("+")] ||= []) << [key, node]
150
166
  end.map do |locales, keys_nodes|
151
167
  keys_nodes.each do |(key, node)|
152
168
  forest["#{locales}.#{key}"] = node
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
3
+ require "set"
4
4
  module I18n::Tasks::PluralKeys
5
5
  # Ref: http://cldr.unicode.org/index/cldr-spec/plural-rules
6
6
  CLDR_CATEGORY_KEYS = %w[zero one two few many other].freeze
7
7
  PLURAL_KEY_SUFFIXES = Set.new CLDR_CATEGORY_KEYS
8
- PLURAL_KEY_RE = /\.(?:#{CLDR_CATEGORY_KEYS * '|'})$/.freeze
8
+ PLURAL_KEY_RE = /\.(?:#{CLDR_CATEGORY_KEYS * "|"})$/
9
+ # Ref: https://unicode-org.github.io/cldr/ldml/tr35-numbers.html#Explicit_0_1_rules
10
+ EXPLICIT_0_1 = %w[0 1].freeze
9
11
 
10
12
  def collapse_plural_nodes!(tree)
11
13
  tree.leaves.map(&:parent).compact.uniq.each do |node|
12
14
  children = node.children
13
15
  next unless plural_forms?(children)
14
16
 
15
- node.value = children.to_hash
17
+ node.value = children.to_hash
16
18
  node.children = nil
17
19
  node.data.merge! children.first.data
18
20
  end
@@ -56,6 +58,6 @@ module I18n::Tasks::PluralKeys
56
58
  end
57
59
 
58
60
  def plural_suffix?(key)
59
- PLURAL_KEY_SUFFIXES.include?(key)
61
+ PLURAL_KEY_SUFFIXES.include?(key) || EXPLICIT_0_1.include?(key)
60
62
  end
61
63
  end
@@ -7,9 +7,9 @@ module I18n::Tasks
7
7
  # 2. Resolved references -- all the used references in their fully resolved form.
8
8
  # 3. Reference keys -- all the used reference keys.
9
9
  def process_references(usages,
10
- data_refs = merge_reference_trees(data_forest.select_keys { |_, node| node.reference? }))
11
- fail ArgumentError, 'usages must be a Data::Tree::Instance' unless usages.is_a?(Data::Tree::Siblings)
12
- fail ArgumentError, 'all_references must be a Data::Tree::Instance' unless data_refs.is_a?(Data::Tree::Siblings)
10
+ data_refs = merge_reference_trees(data_forest.select_keys { |_, node| node.reference? }))
11
+ fail ArgumentError, "usages must be a Data::Tree::Instance" unless usages.is_a?(Data::Tree::Siblings)
12
+ fail ArgumentError, "all_references must be a Data::Tree::Instance" unless data_refs.is_a?(Data::Tree::Siblings)
13
13
 
14
14
  raw_refs = empty_forest
15
15
  resolved_refs = empty_forest
@@ -90,7 +90,7 @@ module I18n::Tasks
90
90
  on_leaves_merge: lambda do |node, other|
91
91
  if node.value != other.value
92
92
  log_warn(
93
- 'Conflicting references: ' \
93
+ "Conflicting references: " \
94
94
  "#{node.full_key(root: false)} ⮕ #{node.value} in #{node.data[:locale]}, " \
95
95
  "but ⮕ #{other.value} in #{other.data[:locale]}"
96
96
  )
@@ -15,55 +15,59 @@ module I18n::Tasks::Reports
15
15
  protected
16
16
 
17
17
  def missing_type_info(type)
18
- ::I18n::Tasks::MissingKeys::MISSING_TYPES[type.to_s.sub(/\Amissing_/, '').to_sym]
18
+ ::I18n::Tasks::MissingKeys::MISSING_TYPES[type.to_s.sub(/\Amissing_/, "").to_sym]
19
19
  end
20
20
 
21
21
  def missing_title(forest)
22
- "Missing translations (#{forest.leaves.count || ''})"
22
+ "Missing translations (#{forest.leaves.count || ""})"
23
23
  end
24
24
 
25
25
  def inconsistent_interpolations_title(forest)
26
- "Inconsistent interpolations (#{forest.leaves.count || ''})"
26
+ "Inconsistent interpolations (#{forest.leaves.count || ""})"
27
+ end
28
+
29
+ def reserved_interpolations_title(forest)
30
+ "Reserved interpolations (#{forest.leaves.count || "∅"})"
27
31
  end
28
32
 
29
33
  def unused_title(key_values)
30
- "Unused keys (#{key_values.count || ''})"
34
+ "Unused keys (#{key_values.count || ""})"
31
35
  end
32
36
 
33
37
  def eq_base_title(key_values, locale = base_locale)
34
- "Same value as #{locale} (#{key_values.count || ''})"
38
+ "Same value as #{locale} (#{key_values.count || ""})"
35
39
  end
36
40
 
37
41
  def used_title(keys_nodes, filter)
38
42
  used_n = keys_nodes.map { |_k, node| node.data[:occurrences].size }.reduce(:+).to_i
39
- "#{keys_nodes.size} key#{'s' if keys_nodes.size != 1}#{" matching '#{filter}'" if filter}" \
40
- "#{" (#{used_n} usage#{'s' if used_n != 1})" if used_n.positive?}"
43
+ "#{keys_nodes.size} key#{"s" if keys_nodes.size != 1}#{" matching '#{filter}'" if filter}" \
44
+ "#{" (#{used_n} usage#{"s" if used_n != 1})" if used_n.positive?}"
41
45
  end
42
46
 
43
47
  # Sort keys by their attributes in order
44
48
  # @param [Hash] order e.g. {locale: :asc, type: :desc, key: :asc}
45
- def sort_by_attr!(objects, order = { locale: :asc, key: :asc })
49
+ def sort_by_attr!(objects, order = {locale: :asc, key: :asc})
46
50
  order_keys = order.keys
47
51
  objects.sort! do |a, b|
48
52
  by = order_keys.detect { |k| a[k] != b[k] }
49
- order[by] == :desc ? b[by] <=> a[by] : a[by] <=> b[by]
53
+ (order[by] == :desc) ? b[by] <=> a[by] : a[by] <=> b[by]
50
54
  end
51
55
  objects
52
56
  end
53
57
 
54
58
  def forest_to_attr(forest)
55
59
  forest.keys(root: false).map do |key, node|
56
- { key: key, value: node.value, type: node.data[:type], locale: node.root.key, data: node.data }
60
+ {key: key, value: node.value, type: node.data[:type], locale: node.root.key, data: node.data}
57
61
  end
58
62
  end
59
63
 
60
64
  def format_locale(locale)
61
- return '' unless locale
65
+ return "" unless locale
62
66
 
63
- if locale.split('+') == task.locales.sort
64
- 'all'
67
+ if locale.split("+") == task.locales.sort
68
+ "all"
65
69
  else
66
- locale.tr '+', ' '
70
+ locale.tr "+", " "
67
71
  end
68
72
  end
69
73
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/reports/base'
4
- require 'terminal-table'
3
+ require "i18n/tasks/reports/base"
4
+ require "terminal-table"
5
5
  module I18n
6
6
  module Tasks
7
7
  module Reports
@@ -10,17 +10,17 @@ module I18n
10
10
  forest = collapse_missing_tree! forest
11
11
  if forest.present?
12
12
  print_title missing_title(forest)
13
- print_table headings: [Rainbow(I18n.t('i18n_tasks.common.locale')).cyan.bright,
14
- Rainbow(I18n.t('i18n_tasks.common.key')).cyan.bright,
15
- I18n.t('i18n_tasks.missing.details_title')] do |t|
13
+ print_table headings: [Rainbow(I18n.t("i18n_tasks.common.locale")).cyan.bright,
14
+ Rainbow(I18n.t("i18n_tasks.common.key")).cyan.bright,
15
+ I18n.t("i18n_tasks.missing.details_title")] do |t|
16
16
  t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
17
- [{ value: Rainbow(format_locale(a[:locale])).cyan, alignment: :center },
18
- format_key(a[:key], a[:data]),
19
- missing_key_info(a)]
17
+ [{value: Rainbow(format_locale(a[:locale])).cyan, alignment: :center},
18
+ format_key(a[:key], a[:data]),
19
+ missing_key_info(a)]
20
20
  end
21
21
  end
22
22
  else
23
- print_success I18n.t('i18n_tasks.missing.none')
23
+ print_success I18n.t("i18n_tasks.missing.none")
24
24
  end
25
25
  end
26
26
 
@@ -29,7 +29,24 @@ module I18n
29
29
  print_title inconsistent_interpolations_title(forest)
30
30
  show_tree(forest)
31
31
  else
32
- print_success I18n.t('i18n_tasks.inconsistent_interpolations.none')
32
+ print_success I18n.t("i18n_tasks.inconsistent_interpolations.none")
33
+ end
34
+ end
35
+
36
+ def reserved_interpolations(forest = task.reserved_interpolations)
37
+ if forest.present?
38
+ print_title reserved_interpolations_title(forest)
39
+ print_table headings: [Rainbow(I18n.t("i18n_tasks.common.locale")).cyan.bright,
40
+ Rainbow(I18n.t("i18n_tasks.common.key")).cyan.bright,
41
+ I18n.t("i18n_tasks.reserved_interpolations.details_title")] do |t|
42
+ t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a|
43
+ [{value: Rainbow(format_locale(a[:locale])).cyan, alignment: :center},
44
+ format_key(a[:key], a[:data]),
45
+ a[:value].join(", ")]
46
+ end
47
+ end
48
+ else
49
+ print_success I18n.t("i18n_tasks.reserved_interpolations.none")
33
50
  end
34
51
  end
35
52
 
@@ -45,7 +62,7 @@ module I18n
45
62
  print_occurrences node, key
46
63
  end
47
64
  else
48
- print_error I18n.t('i18n_tasks.usages.none')
65
+ print_error I18n.t("i18n_tasks.usages.none")
49
66
  end
50
67
  end
51
68
 
@@ -55,7 +72,7 @@ module I18n
55
72
  print_title unused_title(keys)
56
73
  print_locale_key_value_data_table keys
57
74
  else
58
- print_success I18n.t('i18n_tasks.unused.none')
75
+ print_success I18n.t("i18n_tasks.unused.none")
59
76
  end
60
77
  end
61
78
 
@@ -65,7 +82,7 @@ module I18n
65
82
  print_title eq_base_title(keys)
66
83
  print_locale_key_value_data_table keys
67
84
  else
68
- print_info Rainbow('No translations are the same as base value').cyan
85
+ print_info Rainbow("No translations are the same as base value").cyan
69
86
  end
70
87
  end
71
88
 
@@ -74,39 +91,39 @@ module I18n
74
91
  end
75
92
 
76
93
  def forest_stats(forest, stats = task.forest_stats(forest))
77
- text = if stats[:locale_count] == 1
78
- I18n.t('i18n_tasks.data_stats.text_single_locale', **stats)
79
- else
80
- I18n.t('i18n_tasks.data_stats.text', **stats)
81
- end
82
- title = Rainbow(I18n.t('i18n_tasks.data_stats.title', **stats.slice(:locales))).bright
94
+ text = if stats[:locale_count] == 1
95
+ I18n.t("i18n_tasks.data_stats.text_single_locale", **stats)
96
+ else
97
+ I18n.t("i18n_tasks.data_stats.text", **stats)
98
+ end
99
+ title = Rainbow(I18n.t("i18n_tasks.data_stats.title", **stats.slice(:locales))).bright
83
100
  print_info "#{Rainbow(title).cyan} #{Rainbow(text).cyan}"
84
101
  end
85
102
 
86
103
  def mv_results(results)
87
104
  results.each do |(from, to)|
88
105
  if to
89
- print_info "#{Rainbow(from).cyan} #{Rainbow('').yellow.bright} #{Rainbow(to).cyan}"
106
+ print_info "#{Rainbow(from).cyan} #{Rainbow("").yellow.bright} #{Rainbow(to).cyan}"
90
107
  else
91
- print_info "#{Rainbow(from).red}#{Rainbow(' 🗑').red.bright}"
108
+ print_info "#{Rainbow(from).red}#{Rainbow(" 🗑").red.bright}"
92
109
  end
93
110
  end
94
111
  end
95
112
 
96
113
  def cp_results(results)
97
114
  results.each do |(from, to)|
98
- print_info "#{Rainbow(from).cyan} #{Rainbow('+').yellow.bright} #{Rainbow(to).green}"
115
+ print_info "#{Rainbow(from).cyan} #{Rainbow("+").yellow.bright} #{Rainbow(to).green}"
99
116
  end
100
117
  end
101
118
 
102
119
  def check_normalized_results(non_normalized)
103
120
  if non_normalized.empty?
104
- print_success 'All data is normalized'
121
+ print_success "All data is normalized"
105
122
  return
106
123
  end
107
- log_stderr Rainbow('The following data requires normalization:').yellow
124
+ log_stderr Rainbow("The following data requires normalization:").yellow
108
125
  puts non_normalized
109
- log_stderr Rainbow('Run `i18n-tasks normalize` to fix').yellow
126
+ log_stderr Rainbow("Run `i18n-tasks normalize` to fix").yellow
110
127
  end
111
128
 
112
129
  private
@@ -116,7 +133,7 @@ module I18n
116
133
  when :missing_used
117
134
  first_occurrence leaf
118
135
  when :missing_plural
119
- leaf[:data][:missing_keys].join(', ')
136
+ leaf[:data][:missing_keys].join(", ")
120
137
  else
121
138
  "#{Rainbow(leaf[:data][:missing_diff_locale]).cyan} " \
122
139
  "#{format_value(leaf[:value].is_a?(String) ? leaf[:value].strip : leaf[:value])}"
@@ -127,16 +144,16 @@ module I18n
127
144
  if data[:ref_info]
128
145
  from, to = data[:ref_info]
129
146
  resolved = key[0...to.length]
130
- after = key[to.length..]
147
+ after = key[to.length..]
131
148
  " #{Rainbow(from).yellow}#{Rainbow(after).cyan}\n" \
132
- "#{Rainbow('').yellow.bright} #{Rainbow(resolved).yellow.bright}"
149
+ "#{Rainbow("").yellow.bright} #{Rainbow(resolved).yellow.bright}"
133
150
  else
134
151
  Rainbow(key).cyan
135
152
  end
136
153
  end
137
154
 
138
155
  def format_value(val)
139
- val.is_a?(Symbol) ? "#{Rainbow('').yellow.bright}#{Rainbow(val).yellow}" : val.to_s.strip
156
+ val.is_a?(Symbol) ? "#{Rainbow("").yellow.bright}#{Rainbow(val).yellow}" : val.to_s.strip
140
157
  end
141
158
 
142
159
  def format_reference_desc(node_data)
@@ -144,19 +161,19 @@ module I18n
144
161
 
145
162
  case node_data[:ref_type]
146
163
  when :reference_usage
147
- Rainbow('(ref)').yellow.bright
164
+ Rainbow("(ref)").yellow.bright
148
165
  when :reference_usage_resolved
149
- Rainbow('(resolved ref)').yellow.bright
166
+ Rainbow("(resolved ref)").yellow.bright
150
167
  when :reference_usage_key
151
- Rainbow('(ref key)').yellow.bright
168
+ Rainbow("(ref key)").yellow.bright
152
169
  end
153
170
  end
154
171
 
155
172
  def print_occurrences(node, full_key = node.full_key)
156
173
  occurrences = node.data[:occurrences]
157
174
  puts [Rainbow(full_key).bright,
158
- format_reference_desc(node.data),
159
- (Rainbow(occurrences.size).green if occurrences.size > 1)].compact.join ' '
175
+ format_reference_desc(node.data),
176
+ (Rainbow(occurrences.size).green if occurrences.size > 1)].compact.join " "
160
177
  occurrences.each do |occurrence|
161
178
  puts " #{key_occurrence full_key, occurrence}"
162
179
  end
@@ -164,25 +181,25 @@ module I18n
164
181
 
165
182
  def print_locale_key_value_data_table(locale_key_value_datas)
166
183
  if locale_key_value_datas.present?
167
- print_table headings: [Rainbow(I18n.t('i18n_tasks.common.locale')).cyan.bright,
168
- Rainbow(I18n.t('i18n_tasks.common.key')).cyan.bright,
169
- I18n.t('i18n_tasks.common.value')] do |t|
184
+ print_table headings: [Rainbow(I18n.t("i18n_tasks.common.locale")).cyan.bright,
185
+ Rainbow(I18n.t("i18n_tasks.common.key")).cyan.bright,
186
+ I18n.t("i18n_tasks.common.value")] do |t|
170
187
  t.rows = locale_key_value_datas.map do |(locale, k, v, data)|
171
- [{ value: Rainbow(locale).cyan, alignment: :center }, format_key(k, data), format_value(v)]
188
+ [{value: Rainbow(locale).cyan, alignment: :center}, format_key(k, data), format_value(v)]
172
189
  end
173
190
  end
174
191
  else
175
- puts 'ø'
192
+ puts "ø"
176
193
  end
177
194
  end
178
195
 
179
196
  def print_title(title)
180
- log_stderr "#{Rainbow(title.strip).bright} #{Rainbow('|').faint} " \
197
+ log_stderr "#{Rainbow(title.strip).bright} #{Rainbow("|").faint} " \
181
198
  "#{"i18n-tasks v#{I18n::Tasks::VERSION}"}"
182
199
  end
183
200
 
184
201
  def print_success(message)
185
- log_stderr Rainbow("✓ #{I18n.t('i18n_tasks.cmd.encourage').sample} #{message}").green.bright
202
+ log_stderr Rainbow("✓ #{I18n.t("i18n_tasks.cmd.encourage").sample} #{message}").green.bright
186
203
  end
187
204
 
188
205
  def print_error(message)
@@ -194,16 +211,16 @@ module I18n
194
211
  end
195
212
 
196
213
  def indent(txt, n = 2)
197
- txt.gsub(/^/, ' ' * n)
214
+ txt.gsub(/^/, " " * n)
198
215
  end
199
216
 
200
- def print_table(opts, &block)
201
- puts ::Terminal::Table.new(opts, &block)
217
+ def print_table(opts, &)
218
+ puts ::Terminal::Table.new(opts, &)
202
219
  end
203
220
 
204
221
  def key_occurrence(full_key, occurrence)
205
222
  location = Rainbow("#{occurrence.path}:#{occurrence.line_num}").green
206
- source = highlight_key(occurrence.raw_key || full_key, occurrence.line, occurrence.line_pos..-1).strip
223
+ source = highlight_key(occurrence.raw_key || full_key, occurrence.line, occurrence.line_pos..-1).strip
207
224
  "#{location} #{source}"
208
225
  end
209
226
 
@@ -214,8 +231,8 @@ module I18n
214
231
  first = occurrences.first
215
232
  [
216
233
  Rainbow("#{first.path}:#{first.line_num}").green,
217
- ("(#{I18n.t 'i18n_tasks.common.n_more', count: occurrences.length - 1})" if occurrences.length > 1)
218
- ].compact.join(' ')
234
+ ("(#{I18n.t "i18n_tasks.common.n_more", count: occurrences.length - 1})" if occurrences.length > 1)
235
+ ].compact.join(" ")
219
236
  end
220
237
 
221
238
  def highlight_key(full_key, line, range = (0..-1))
@@ -7,7 +7,7 @@ module I18n::Tasks::Scanners::AstMatchers
7
7
  end
8
8
 
9
9
  def convert_to_key_occurrences(send_node, _method_name, location: send_node.loc)
10
- fail('Not implemented')
10
+ fail("Not implemented")
11
11
  end
12
12
 
13
13
  protected
@@ -32,7 +32,7 @@ module I18n::Tasks::Scanners::AstMatchers
32
32
  elsif %i[true false].include?(node.type)
33
33
  node.type.to_s
34
34
  elsif node.type == :nil
35
- ''
35
+ ""
36
36
  elsif node.type == :array && array_join_with
37
37
  extract_array_as_string(
38
38
  node,
@@ -109,7 +109,7 @@ module I18n::Tasks::Scanners::AstMatchers
109
109
  if array_reject_blank
110
110
  children_strings.reject! do |x|
111
111
  # empty strings and nils in the scope argument are ignored by i18n
112
- x == ''
112
+ x == ""
113
113
  end
114
114
  end
115
115
  children_strings.join(array_join_with)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/scanners/ast_matchers/base_matcher'
4
- require 'i18n/tasks/scanners/results/occurrence'
3
+ require "i18n/tasks/scanners/ast_matchers/base_matcher"
4
+ require "i18n/tasks/scanners/results/occurrence"
5
5
 
6
6
  module I18n::Tasks::Scanners::AstMatchers
7
7
  class DefaultI18nSubjectMatcher < BaseMatcher
@@ -10,7 +10,7 @@ module I18n::Tasks::Scanners::AstMatchers
10
10
  return unless children[1] == :default_i18n_subject
11
11
 
12
12
  key = @scanner.absolute_key(
13
- '.subject',
13
+ ".subject",
14
14
  location.expression.source_buffer.name,
15
15
  calling_method: method_name
16
16
  )
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/scanners/ast_matchers/base_matcher'
4
- require 'i18n/tasks/scanners/results/occurrence'
3
+ require "i18n/tasks/scanners/ast_matchers/base_matcher"
4
+ require "i18n/tasks/scanners/results/occurrence"
5
5
 
6
6
  module I18n::Tasks::Scanners::AstMatchers
7
7
  class MessageReceiversMatcher < BaseMatcher
@@ -64,25 +64,25 @@ module I18n::Tasks::Scanners::AstMatchers
64
64
  def process_options(node:, key:)
65
65
  return [key, nil] if node&.type != :hash
66
66
 
67
- scope_node = extract_hash_pair(node, 'scope')
67
+ scope_node = extract_hash_pair(node, "scope")
68
68
 
69
69
  if scope_node
70
70
  scope = extract_string(
71
71
  scope_node.children[1],
72
- array_join_with: '.',
72
+ array_join_with: ".",
73
73
  array_flatten: true,
74
74
  array_reject_blank: true
75
75
  )
76
76
  return nil if scope.nil? && scope_node.type != :nil
77
77
 
78
- key = [scope, key].join('.') unless scope == ''
78
+ key = [scope, key].join(".") unless scope == ""
79
79
  end
80
- if default_arg_node = extract_hash_pair(node, 'default')
80
+ if (default_arg_node = extract_hash_pair(node, "default"))
81
81
  default_arg = if default_arg_node.children[1]&.type == :hash
82
- extract_hash(default_arg_node.children[1])
83
- else
84
- extract_string(default_arg_node.children[1])
85
- end
82
+ extract_hash(default_arg_node.children[1])
83
+ else
84
+ extract_string(default_arg_node.children[1])
85
+ end
86
86
  end
87
87
 
88
88
  [key, default_arg]
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/scanners/results/occurrence'
3
+ require "i18n/tasks/scanners/results/occurrence"
4
4
 
5
5
  module I18n::Tasks::Scanners::AstMatchers
6
6
  class RailsModelMatcher < BaseMatcher
@@ -16,7 +16,7 @@ module I18n::Tasks::Scanners::AstMatchers
16
16
  receiver = children[0]
17
17
  method_name = children[1]
18
18
 
19
- return unless method_name == :human_attribute_name && receiver.type == :const
19
+ return unless method_name == :human_attribute_name && receiver&.type == :const
20
20
 
21
21
  value = children[2]
22
22