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,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/key_pattern_matching'
4
- require 'i18n/tasks/data/tree/node'
3
+ require "i18n/tasks/key_pattern_matching"
4
+ require "i18n/tasks/data/tree/node"
5
5
 
6
6
  module I18n::Tasks
7
7
  module Data::Router
@@ -52,11 +52,11 @@ module I18n::Tasks
52
52
  source_path = source_path.dup
53
53
 
54
54
  config_read_patterns.each do |pattern|
55
- regexp = Glob.new(format(pattern, locale: '(*)')).to_regexp
55
+ regexp = Glob.new(format(pattern, locale: "(*)")).to_regexp
56
56
  next unless source_path.match?(regexp)
57
57
 
58
58
  source_path.match(regexp) do |match_data|
59
- (1..match_data.size - 1).reverse_each do |capture_index|
59
+ (1..(match_data.size - 1)).reverse_each do |capture_index|
60
60
  capture_begin, capture_end = match_data.offset(capture_index)
61
61
  source_path.slice!(Range.new(capture_begin, capture_end, true))
62
62
  source_path.insert(capture_begin, locale.to_s)
@@ -92,29 +92,29 @@ module I18n::Tasks
92
92
  end
93
93
 
94
94
  case char
95
- when '**' then '(?:[^/]+/)*'
96
- when '*' then '.*'
97
- when '?' then '.'
98
- when '.' then '\.'
99
- when '{'
95
+ when "**" then "(?:[^/]+/)*"
96
+ when "*" then ".*"
97
+ when "?" then "."
98
+ when "." then '\.'
99
+ when "{"
100
100
  curlies += 1
101
- '('
102
- when '}'
101
+ "("
102
+ when "}"
103
103
  if curlies.positive?
104
104
  curlies -= 1
105
- ')'
105
+ ")"
106
106
  else
107
107
  char
108
108
  end
109
- when ','
109
+ when ","
110
110
  if curlies.positive?
111
- '|'
111
+ "|"
112
112
  else
113
113
  char
114
114
  end
115
- when '\\'
115
+ when "\\"
116
116
  escaping = true
117
- '\\'
117
+ "\\"
118
118
  else char
119
119
  end
120
120
  end.join
@@ -130,10 +130,10 @@ module I18n::Tasks
130
130
  out = []
131
131
  until chars.empty?
132
132
  char = chars.shift
133
- if char == '*' && chars.first == '*'
133
+ if char == "*" && chars.first == "*"
134
134
  chars.shift
135
- chars.shift if chars.first == '/'
136
- out.push('**')
135
+ chars.shift if chars.first == "/"
136
+ out.push("**")
137
137
  else
138
138
  out.push(char)
139
139
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/key_pattern_matching'
4
- require 'i18n/tasks/data/tree/node'
3
+ require "i18n/tasks/key_pattern_matching"
4
+ require "i18n/tasks/data/tree/node"
5
5
 
6
6
  module I18n::Tasks
7
7
  module Data::Router
@@ -37,7 +37,7 @@ module I18n::Tasks
37
37
  pattern, path = routes.detect { |route| route[0] =~ key }
38
38
  if pattern
39
39
  key_match = $~
40
- path = format(path, locale: locale)
40
+ path = format(path, locale: locale)
41
41
  path.gsub!(/\\\d+/) { |m| key_match[m[1..].to_i] }
42
42
  (out[path] ||= Set.new) << "#{locale}.#{key}"
43
43
  else
@@ -46,14 +46,14 @@ module I18n::Tasks
46
46
  end
47
47
  out.each do |dest, keys|
48
48
  block.yield dest,
49
- forest.select_keys(root: true) { |key, _| keys.include?(key) }
49
+ forest.select_keys(root: true) { |key, _| keys.include?(key) }
50
50
  end
51
51
  end
52
52
 
53
53
  private
54
54
 
55
55
  def compile_routes(routes)
56
- routes.map { |x| x.is_a?(String) ? ['*', x] : x }.map do |x|
56
+ routes.map { |x| x.is_a?(String) ? ["*", x] : x }.map do |x|
57
57
  [compile_key_pattern(x[0]), x[1]]
58
58
  end
59
59
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/data/tree/traversal'
4
- require 'i18n/tasks/data/tree/siblings'
3
+ require "i18n/tasks/data/tree/traversal"
4
+ require "i18n/tasks/data/tree/siblings"
5
5
  module I18n::Tasks::Data::Tree
6
6
  class Node # rubocop:disable Metrics/ClassLength
7
7
  include Enumerable
@@ -23,26 +23,26 @@ module I18n::Tasks::Data::Tree
23
23
  # rubocop:enable Metrics/ParameterLists
24
24
 
25
25
  def attributes
26
- { key: @key, value: @value, data: @data.try(:clone), parent: @parent, children: @children }
26
+ {key: @key, value: @value, data: @data.try(:clone), parent: @parent, children: @children}
27
27
  end
28
28
 
29
29
  def derive(new_attr = {})
30
- self.class.new(**attributes.merge(new_attr))
30
+ self.class.new(**attributes, **new_attr)
31
31
  end
32
32
 
33
33
  def children=(children)
34
34
  @children = case children
35
- when Siblings
36
- children.parent == self ? children : children.derive(parent: self)
37
- when NilClass
38
- nil
39
- else
40
- Siblings.new(
41
- nodes: children,
42
- parent: self,
43
- warn_about_add_children_to_leaf: @warn_about_add_children_to_leaf
44
- )
45
- end
35
+ when Siblings
36
+ (children.parent == self) ? children : children.derive(parent: self)
37
+ when NilClass
38
+ nil
39
+ else
40
+ Siblings.new(
41
+ nodes: children,
42
+ parent: self,
43
+ warn_about_add_children_to_leaf: @warn_about_add_children_to_leaf
44
+ )
45
+ end
46
46
  dirty!
47
47
  end
48
48
 
@@ -90,7 +90,7 @@ module I18n::Tasks::Data::Tree
90
90
  children.get(key)
91
91
  end
92
92
 
93
- alias [] get
93
+ alias_method :[], :get
94
94
 
95
95
  # append and reparent nodes
96
96
  def append!(nodes)
@@ -138,7 +138,7 @@ module I18n::Tasks::Data::Tree
138
138
  node
139
139
  end
140
140
 
141
- alias []= set
141
+ alias_method :[]=, :set
142
142
 
143
143
  def to_nodes
144
144
  Nodes.new([self])
@@ -154,9 +154,9 @@ module I18n::Tasks::Data::Tree
154
154
  if key.nil?
155
155
  children_hash
156
156
  elsif leaf?
157
- { key => value }
157
+ {key => value}
158
158
  else
159
- { key => children_hash }
159
+ {key => children_hash}
160
160
  end
161
161
  end
162
162
  end
@@ -166,18 +166,18 @@ module I18n::Tasks::Data::Tree
166
166
 
167
167
  def inspect(level = 0)
168
168
  label = if key.nil?
169
- Rainbow('').faint
170
- else
171
- [Rainbow(key).color(1 + (level % 15)),
172
- (": #{format_value_for_inspect(value)}" if leaf?),
173
- (" #{data}" if data?)].compact.join
174
- end
175
- [' ' * level, label, ("\n#{children.map { |c| c.inspect(level + 1) }.join("\n")}" if children?)].compact.join
169
+ Rainbow("").faint
170
+ else
171
+ [Rainbow(key).color(1 + (level % 15)),
172
+ (": #{format_value_for_inspect(value)}" if leaf?),
173
+ (" #{data}" if data?)].compact.join
174
+ end
175
+ [" " * level, label, ("\n#{children.map { |c| c.inspect(level + 1) }.join("\n")}" if children?)].compact.join
176
176
  end
177
177
 
178
178
  def format_value_for_inspect(value)
179
179
  if value.is_a?(Symbol)
180
- "#{Rainbow('').bright.yellow}#{Rainbow(value).yellow}"
180
+ "#{Rainbow("").bright.yellow}#{Rainbow(value).yellow}"
181
181
  else
182
182
  Rainbow(value).cyan
183
183
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/data/tree/traversal'
3
+ require "i18n/tasks/data/tree/traversal"
4
4
  module I18n::Tasks::Data::Tree
5
5
  # A list of nodes
6
6
  class Nodes
@@ -20,7 +20,7 @@ module I18n::Tasks::Data::Tree
20
20
  end
21
21
 
22
22
  def attributes
23
- { nodes: @list }
23
+ {nodes: @list}
24
24
  end
25
25
 
26
26
  def derive(new_attr = {})
@@ -32,10 +32,10 @@ module I18n::Tasks::Data::Tree
32
32
 
33
33
  def to_hash(sort = false)
34
34
  (@hash ||= {})[sort] ||= if sort
35
- sort_by(&:key)
36
- else
37
- self
38
- end.map { |node| node.to_hash(sort) }.reduce({}, :deep_merge!)
35
+ sort_by(&:key)
36
+ else
37
+ self
38
+ end.map { |node| node.to_hash(sort) }.reduce({}, :deep_merge!)
39
39
  end
40
40
 
41
41
  delegate :to_json, to: :to_hash
@@ -45,7 +45,7 @@ module I18n::Tasks::Data::Tree
45
45
  if present?
46
46
  map(&:inspect) * "\n"
47
47
  else
48
- Rainbow('{∅}').faint
48
+ Rainbow("{∅}").faint
49
49
  end
50
50
  end
51
51
 
@@ -67,14 +67,14 @@ module I18n::Tasks::Data::Tree
67
67
  derive.append!(other)
68
68
  end
69
69
 
70
- alias << append
70
+ alias_method :<<, :append
71
71
 
72
72
  def merge!(nodes)
73
73
  @list += nodes.to_a
74
74
  dirty!
75
75
  self
76
76
  end
77
- alias + merge!
77
+ alias_method :+, :merge!
78
78
 
79
79
  def children(&block)
80
80
  return to_enum(:children) { map { |c| c.children ? c.children.size : 0 }.reduce(:+) } unless block
@@ -84,7 +84,7 @@ module I18n::Tasks::Data::Tree
84
84
  end
85
85
  end
86
86
 
87
- alias children? any?
87
+ alias_method :children?, :any?
88
88
 
89
89
  protected
90
90
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
- require 'i18n/tasks/split_key'
5
- require 'i18n/tasks/data/tree/nodes'
3
+ require "set"
4
+ require "i18n/tasks/split_key"
5
+ require "i18n/tasks/data/tree/nodes"
6
6
 
7
7
  module I18n::Tasks::Data::Tree
8
8
  # Siblings represents a subtree sharing a common parent
@@ -17,7 +17,7 @@ module I18n::Tasks::Data::Tree
17
17
  def initialize(opts = {})
18
18
  super(nodes: opts[:nodes])
19
19
  @parent = opts[:parent] || first.try(:parent)
20
- @list.map! { |node| node.parent == @parent ? node : node.derive(parent: @parent) }
20
+ @list.map! { |node| (node.parent == @parent) ? node : node.derive(parent: @parent) }
21
21
  @key_to_node = @list.each_with_object({}) { |node, h| h[node.key] = node }
22
22
  @warn_about_add_children_to_leaf = opts.fetch(:warn_about_add_children_to_leaf, true)
23
23
  end
@@ -52,7 +52,7 @@ module I18n::Tasks::Data::Tree
52
52
  new_key = to_pattern.gsub(/\\\d+/) { |m| match[m[1..].to_i] }
53
53
  old_key_to_new_key[full_key] = new_key
54
54
  moved_forest.merge!(Siblings.new.tap do |forest|
55
- forest[[(node.root.try(:key) unless root), new_key].compact.join('.')] =
55
+ forest[[(node.root.try(:key) unless root), new_key].compact.join(".")] =
56
56
  node.derive(key: split_key(new_key).last)
57
57
  end)
58
58
  end
@@ -62,10 +62,10 @@ module I18n::Tasks::Data::Tree
62
62
  nodes do |node|
63
63
  next unless node.reference?
64
64
 
65
- old_target = [(node.root.key if root), node.value.to_s].compact.join('.')
65
+ old_target = [(node.root.key if root), node.value.to_s].compact.join(".")
66
66
  new_target = old_key_to_new_key[old_target]
67
67
  if new_target
68
- new_target = new_target.sub(/\A[^.]*\./, '') if root
68
+ new_target = new_target.sub(/\A[^.]*\./, "") if root
69
69
  node.value = new_target.to_sym
70
70
  end
71
71
  end
@@ -75,26 +75,26 @@ module I18n::Tasks::Data::Tree
75
75
  end
76
76
 
77
77
  def replace_node!(node, new_node)
78
- @list[@list.index(node)] = new_node
78
+ @list[@list.index(node)] = new_node
79
79
  key_to_node[new_node.key] = new_node
80
80
  end
81
81
 
82
82
  # @return [Node] by full key
83
83
  def get(full_key)
84
84
  first_key, rest = split_key(full_key.to_s, 2)
85
- node = key_to_node[first_key]
85
+ node = key_to_node[first_key]
86
86
  node = node.children.try(:get, rest) if rest && node
87
87
  node
88
88
  end
89
89
 
90
- alias [] get
90
+ alias_method :[], :get
91
91
 
92
92
  # add or replace node by full key
93
93
  def set(full_key, node)
94
- fail 'value should be a I18n::Tasks::Data::Tree::Node' unless node.is_a?(Node)
94
+ fail "value should be a I18n::Tasks::Data::Tree::Node" unless node.is_a?(Node)
95
95
 
96
96
  key_part, rest = split_key(full_key, 2)
97
- child = key_to_node[key_part]
97
+ child = key_to_node[key_part]
98
98
 
99
99
  if rest
100
100
  unless child
@@ -119,7 +119,7 @@ module I18n::Tasks::Data::Tree
119
119
  node
120
120
  end
121
121
 
122
- alias []= set
122
+ alias_method :[]=, :set
123
123
 
124
124
  # methods below change state
125
125
 
@@ -133,9 +133,9 @@ module I18n::Tasks::Data::Tree
133
133
  nodes = nodes.map do |node|
134
134
  fail "already has a child with key '#{node.key}'" if key_to_node.key?(node.key)
135
135
 
136
- key_to_node[node.key] = (node.parent == parent ? node : node.derive(parent: parent))
136
+ key_to_node[node.key] = ((node.parent == parent) ? node : node.derive(parent: parent))
137
137
  end
138
- super(nodes)
138
+ super
139
139
  self
140
140
  end
141
141
 
@@ -267,7 +267,7 @@ module I18n::Tasks::Data::Tree
267
267
  key_occurrences.each do |key_occurrence|
268
268
  forest[key_occurrence.key] = ::I18n::Tasks::Data::Tree::Node.new(
269
269
  key: split_key(key_occurrence.key).last,
270
- data: { occurrences: key_occurrence.occurrences }
270
+ data: {occurrences: key_occurrence.occurrences}
271
271
  )
272
272
  end
273
273
  end
@@ -276,9 +276,9 @@ module I18n::Tasks::Data::Tree
276
276
  def from_key_attr(key_attrs, opts = {}, &block)
277
277
  build_forest(opts) do |forest|
278
278
  key_attrs.each do |(full_key, attr)|
279
- fail "Invalid key #{full_key.inspect}" if full_key.end_with?('.')
279
+ fail "Invalid key #{full_key.inspect}" if full_key.end_with?(".")
280
280
 
281
- node = ::I18n::Tasks::Data::Tree::Node.new(**attr.merge(key: split_key(full_key).last))
281
+ node = ::I18n::Tasks::Data::Tree::Node.new(**attr, key: split_key(full_key).last)
282
282
  yield(full_key, node) if block
283
283
  forest[full_key] = node
284
284
  end
@@ -305,7 +305,7 @@ module I18n::Tasks::Data::Tree
305
305
  Siblings.new(opts)
306
306
  end
307
307
 
308
- alias [] from_nested_hash
308
+ alias_method :[], :from_nested_hash
309
309
 
310
310
  # build forest from [[Full Key, Value]]
311
311
  def from_flat_pairs(pairs)
@@ -323,7 +323,7 @@ module I18n::Tasks::Data::Tree
323
323
  opts[:parent] = ::I18n::Tasks::Data::Tree::Node.new(opts[:parent_attr]) if opts[:parent_attr]
324
324
  if opts[:parent_locale]
325
325
  opts[:parent] = ::I18n::Tasks::Data::Tree::Node.new(
326
- key: opts[:parent_locale], data: { locale: opts[:parent_locale] }
326
+ key: opts[:parent_locale], data: {locale: opts[:parent_locale]}
327
327
  )
328
328
  end
329
329
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
3
+ require "set"
4
4
 
5
5
  module I18n::Tasks
6
6
  module Data::Tree
7
7
  # Any Enumerable that yields nodes can mix in this module
8
8
  module Traversal # rubocop:disable Metrics/ModuleLength
9
- def nodes(&block)
10
- depth_first(&block)
9
+ def nodes(&)
10
+ depth_first(&)
11
11
  end
12
12
 
13
13
  def leaves(&visitor)
@@ -76,13 +76,13 @@ module I18n::Tasks
76
76
 
77
77
  def root_key_values(sort = false)
78
78
  result = keys(root: false).map { |key, node| [node.root.key, key, node.value] }
79
- result.sort! { |a, b| a[0] == b[0] ? a[1] <=> b[1] : a[0] <=> b[0] } if sort
79
+ result.sort! { |a, b| (a[0] == b[0]) ? a[1] <=> b[1] : a[0] <=> b[0] } if sort
80
80
  result
81
81
  end
82
82
 
83
83
  def root_key_value_data(sort = false)
84
84
  result = keys(root: false).map { |key, node| [node.root.key, key, node.value, node.data] }
85
- result.sort! { |a, b| a[0] == b[0] ? a[1] <=> b[1] : a[0] <=> b[0] } if sort
85
+ result.sort! { |a, b| (a[0] == b[0]) ? a[1] <=> b[1] : a[0] <=> b[0] } if sort
86
86
  result
87
87
  end
88
88
 
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'i18n/tasks/data/file_system'
3
+ require "i18n/tasks/data/file_system"
4
4
 
5
5
  module I18n::Tasks
6
6
  module Data
7
7
  DATA_DEFAULTS = {
8
- adapter: 'I18n::Tasks::Data::FileSystem'
8
+ adapter: "I18n::Tasks::Data::FileSystem"
9
9
  }.freeze
10
10
 
11
11
  # I18n data provider
@@ -17,7 +17,7 @@ module I18n::Tasks
17
17
  data_config[:locales] = config[:locales]
18
18
  adapter_class = data_config[:adapter].presence || data_config[:class].presence || DATA_DEFAULTS[:adapter]
19
19
  adapter_class = adapter_class.to_s
20
- adapter_class = 'I18n::Tasks::Data::FileSystem' if adapter_class == 'file_system'
20
+ adapter_class = "I18n::Tasks::Data::FileSystem" if adapter_class == "file_system"
21
21
  data_config.except!(:adapter, :class)
22
22
  ActiveSupport::Inflector.constantize(adapter_class).new data_config
23
23
  end
@@ -50,7 +50,7 @@ module I18n::Tasks
50
50
  end
51
51
 
52
52
  def t_proc(locale = base_locale)
53
- @t_proc ||= {}
53
+ @t_proc ||= {}
54
54
  @t_proc[locale] ||= proc { |key| t(key, locale) }
55
55
  end
56
56
 
@@ -2,8 +2,8 @@
2
2
 
3
3
  module I18n::Tasks
4
4
  module HtmlKeys
5
- HTML_KEY_PATTERN = /[.\-_]html\z/.freeze
6
- MAYBE_PLURAL_HTML_KEY_PATTERN = /[.\-_]html\.[^.]+\z/.freeze
5
+ HTML_KEY_PATTERN = /[.\-_]html\z/
6
+ MAYBE_PLURAL_HTML_KEY_PATTERN = /[.\-_]html\.[^.]+\z/
7
7
 
8
8
  def html_key?(full_key, locale)
9
9
  !!(full_key =~ HTML_KEY_PATTERN ||
@@ -12,19 +12,19 @@ module I18n::Tasks::IgnoreKeys
12
12
  # @param locale [String] only when type is :eq_base
13
13
  # @return [Regexp] a regexp that matches all the keys ignored for the type (and locale)
14
14
  def ignore_pattern(type, locale = nil)
15
- @ignore_patterns ||= HashWithIndifferentAccess.new
16
- @ignore_patterns[type] ||= {}
15
+ @ignore_patterns ||= HashWithIndifferentAccess.new
16
+ @ignore_patterns[type] ||= {}
17
17
  @ignore_patterns[type][locale] ||= begin
18
18
  global = ignore_config.presence || []
19
19
  type_ignore = ignore_config(type).presence || []
20
20
  patterns = case type_ignore
21
- when Array
22
- global + type_ignore
23
- when Hash
24
- # ignore per locale
25
- global + (type_ignore['all'] || []) +
26
- type_ignore.select { |k, _v| k.to_s =~ /\b#{locale}\b/ }.values.flatten(1).compact
27
- end
21
+ when Array
22
+ global + type_ignore
23
+ when Hash
24
+ # ignore per locale
25
+ global + (type_ignore["all"] || []) +
26
+ type_ignore.select { |k, _v| k.to_s =~ /\b#{locale}\b/ }.values.flatten(1).compact
27
+ end
28
28
  compile_patterns_re patterns
29
29
  end
30
30
  end
@@ -5,7 +5,7 @@ module I18n::Tasks
5
5
  class << self
6
6
  attr_accessor :variable_regex
7
7
  end
8
- @variable_regex = /(?<!%)%{[^}]+}/.freeze
8
+ @variable_regex = /(?<!%)%{[^}]+}/
9
9
 
10
10
  def inconsistent_interpolations(locales: nil, base_locale: nil) # rubocop:disable Metrics/AbcSize
11
11
  locales ||= self.locales
@@ -30,5 +30,25 @@ module I18n::Tasks
30
30
  result.each { |root| root.data[:type] = :inconsistent_interpolations }
31
31
  result
32
32
  end
33
+
34
+ def reserved_interpolations(locales: nil)
35
+ locales ||= self.locales
36
+ result = empty_forest
37
+
38
+ locales.each do |current_locale|
39
+ data[current_locale].key_values.each do |key, value|
40
+ next unless value.is_a?(String)
41
+
42
+ reserved_variables = value.scan(::I18n.reserved_keys_pattern).flatten
43
+ next if reserved_variables.empty?
44
+
45
+ result.merge!(data[current_locale].first.children[key].walk_to_root.reduce(nil) do |c, p|
46
+ [p.derive(children: c, value: reserved_variables)]
47
+ end)
48
+ end
49
+ end
50
+ result.each { |root| root.data[:type] = :inconsistent_interpolations }
51
+ result
52
+ end
33
53
  end
34
54
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'strscan'
3
+ require "strscan"
4
4
 
5
5
  module I18n::Tasks::KeyPatternMatching
6
6
  extend self # rubocop:disable Style/ModuleFunction
7
7
 
8
- MATCH_NOTHING = /\z\A/.freeze
8
+ MATCH_NOTHING = /\z\A/
9
9
 
10
10
  # one regex to match any
11
11
  def compile_patterns_re(key_patterns)
@@ -13,7 +13,7 @@ module I18n::Tasks::KeyPatternMatching
13
13
  # match nothing
14
14
  MATCH_NOTHING
15
15
  else
16
- /(?:#{key_patterns.map { |p| compile_key_pattern p } * '|'})/m
16
+ /(?:#{key_patterns.map { |p| compile_key_pattern p } * "|"})/m
17
17
  end
18
18
  end
19
19
 
@@ -31,10 +31,10 @@ module I18n::Tasks::KeyPatternMatching
31
31
 
32
32
  def key_pattern_re_body(key_pattern)
33
33
  key_pattern
34
- .gsub('.', '\.')
35
- .gsub('*:', '[^.]+?')
36
- .gsub('*', '.*')
37
- .gsub(':', '(?<=^|\.)[^.]+?(?=\.|$)')
38
- .gsub(/\{(.*?)}/) { "(#{Regexp.last_match(1).strip.gsub(/\s*,\s*/, '|')})" }
34
+ .gsub(".", '\.')
35
+ .gsub("*:", "[^.]+?")
36
+ .gsub("*", ".*")
37
+ .gsub(":", '(?<=^|\.)[^.]+?(?=\.|$)')
38
+ .gsub(/\{(.*?)}/) { "(#{Regexp.last_match(1).strip.gsub(/\s*,\s*/, "|")})" }
39
39
  end
40
40
  end
@@ -25,7 +25,8 @@ module I18n::Tasks::Logging
25
25
  def log_stderr(*args)
26
26
  # We don't want output from different threads to get intermixed.
27
27
  MUTEX.synchronize do
28
- $stderr.puts(*args)
28
+ # Use $stderr directly to avoid issues with JRuby and thread safety
29
+ $stderr.puts(*args) # rubocop:disable Style/StderrPuts
29
30
  end
30
31
  end
31
32