i18n-tasks 1.0.15 → 1.1.1
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.
- checksums.yaml +4 -4
- data/README.md +85 -13
- data/Rakefile +4 -4
- data/bin/i18n-tasks +3 -3
- data/config/locales/en.yml +6 -0
- data/config/locales/ru.yml +7 -0
- data/i18n-tasks.gemspec +28 -41
- data/lib/i18n/tasks/base_task.rb +19 -19
- data/lib/i18n/tasks/cli.rb +37 -30
- data/lib/i18n/tasks/command/collection.rb +4 -4
- data/lib/i18n/tasks/command/commander.rb +5 -5
- data/lib/i18n/tasks/command/commands/check_prism.rb +126 -0
- data/lib/i18n/tasks/command/commands/data.rb +33 -33
- data/lib/i18n/tasks/command/commands/eq_base.rb +3 -3
- data/lib/i18n/tasks/command/commands/health.rb +6 -5
- data/lib/i18n/tasks/command/commands/interpolations.rb +14 -3
- data/lib/i18n/tasks/command/commands/meta.rb +6 -6
- data/lib/i18n/tasks/command/commands/missing.rb +25 -25
- data/lib/i18n/tasks/command/commands/tree.rb +33 -33
- data/lib/i18n/tasks/command/commands/usages.rb +24 -24
- data/lib/i18n/tasks/command/dsl.rb +1 -1
- data/lib/i18n/tasks/command/option_parsers/enum.rb +5 -5
- data/lib/i18n/tasks/command/option_parsers/locale.rb +4 -4
- data/lib/i18n/tasks/command/options/common.rb +16 -16
- data/lib/i18n/tasks/command/options/data.rb +18 -18
- data/lib/i18n/tasks/command/options/locales.rb +32 -32
- data/lib/i18n/tasks/commands.rb +14 -12
- data/lib/i18n/tasks/concurrent/cache.rb +1 -1
- data/lib/i18n/tasks/concurrent/cached_value.rb +1 -1
- data/lib/i18n/tasks/configuration.rb +22 -21
- data/lib/i18n/tasks/console_context.rb +11 -11
- data/lib/i18n/tasks/data/adapter/json_adapter.rb +1 -1
- data/lib/i18n/tasks/data/adapter/yaml_adapter.rb +5 -5
- data/lib/i18n/tasks/data/file_formats.rb +3 -3
- data/lib/i18n/tasks/data/file_system.rb +5 -5
- data/lib/i18n/tasks/data/file_system_base.rb +26 -26
- data/lib/i18n/tasks/data/language_names.rb +202 -0
- data/lib/i18n/tasks/data/router/conservative_router.rb +3 -3
- data/lib/i18n/tasks/data/router/isolating_router.rb +19 -19
- data/lib/i18n/tasks/data/router/pattern_router.rb +5 -5
- data/lib/i18n/tasks/data/tree/node.rb +27 -27
- data/lib/i18n/tasks/data/tree/nodes.rb +10 -10
- data/lib/i18n/tasks/data/tree/siblings.rb +20 -20
- data/lib/i18n/tasks/data/tree/traversal.rb +5 -5
- data/lib/i18n/tasks/data.rb +4 -4
- data/lib/i18n/tasks/html_keys.rb +2 -2
- data/lib/i18n/tasks/ignore_keys.rb +9 -9
- data/lib/i18n/tasks/interpolations.rb +21 -1
- data/lib/i18n/tasks/key_pattern_matching.rb +8 -8
- data/lib/i18n/tasks/logging.rb +2 -1
- data/lib/i18n/tasks/missing_keys.rb +24 -8
- data/lib/i18n/tasks/plural_keys.rb +6 -4
- data/lib/i18n/tasks/references.rb +4 -4
- data/lib/i18n/tasks/reports/base.rb +18 -14
- data/lib/i18n/tasks/reports/terminal.rb +64 -47
- data/lib/i18n/tasks/scanners/ast_matchers/base_matcher.rb +3 -3
- data/lib/i18n/tasks/scanners/ast_matchers/default_i18n_subject_matcher.rb +3 -3
- data/lib/i18n/tasks/scanners/ast_matchers/message_receivers_matcher.rb +10 -10
- data/lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb +1 -1
- data/lib/i18n/tasks/scanners/erb_ast_scanner.rb +70 -10
- data/lib/i18n/tasks/scanners/file_scanner.rb +5 -5
- data/lib/i18n/tasks/scanners/files/caching_file_finder.rb +3 -3
- data/lib/i18n/tasks/scanners/files/caching_file_finder_provider.rb +3 -3
- data/lib/i18n/tasks/scanners/files/caching_file_reader.rb +2 -2
- data/lib/i18n/tasks/scanners/files/file_finder.rb +8 -8
- data/lib/i18n/tasks/scanners/files/file_reader.rb +1 -1
- data/lib/i18n/tasks/scanners/local_ruby_parser.rb +8 -8
- data/lib/i18n/tasks/scanners/occurrence_from_position.rb +4 -3
- data/lib/i18n/tasks/scanners/pattern_mapper.rb +7 -7
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +20 -20
- data/lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb +8 -8
- data/lib/i18n/tasks/scanners/prism_scanners/arguments_visitor.rb +8 -1
- data/lib/i18n/tasks/scanners/prism_scanners/nodes.rb +123 -62
- data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +186 -105
- data/lib/i18n/tasks/scanners/relative_keys.rb +8 -8
- data/lib/i18n/tasks/scanners/results/key_occurrences.rb +3 -3
- data/lib/i18n/tasks/scanners/results/occurrence.rb +14 -10
- data/lib/i18n/tasks/scanners/ruby_ast_call_finder.rb +1 -1
- data/lib/i18n/tasks/scanners/ruby_key_literals.rb +6 -6
- data/lib/i18n/tasks/scanners/ruby_parser_factory.rb +1 -1
- data/lib/i18n/tasks/scanners/ruby_scanner.rb +226 -0
- data/lib/i18n/tasks/scanners/scanner.rb +2 -2
- data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +3 -24
- data/lib/i18n/tasks/split_key.rb +4 -4
- data/lib/i18n/tasks/stats.rb +3 -3
- data/lib/i18n/tasks/translation.rb +5 -5
- data/lib/i18n/tasks/translators/base_translator.rb +40 -14
- data/lib/i18n/tasks/translators/deepl_translator.rb +17 -14
- data/lib/i18n/tasks/translators/google_translator.rb +169 -25
- data/lib/i18n/tasks/translators/openai_translator.rb +34 -23
- data/lib/i18n/tasks/translators/watsonx_translator.rb +16 -16
- data/lib/i18n/tasks/translators/yandex_translator.rb +8 -8
- data/lib/i18n/tasks/unused_keys.rb +1 -1
- data/lib/i18n/tasks/used_keys.rb +32 -33
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/i18n/tasks.rb +17 -17
- data/templates/config/i18n-tasks.yml +12 -0
- data/templates/minitest/i18n_test.rb +3 -3
- data/templates/rspec/i18n_spec.rb +7 -7
- metadata +25 -185
- data/lib/i18n/tasks/scanners/prism_scanner.rb +0 -83
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +0 -145
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
3
|
+
require "i18n/tasks/scanners/file_scanner"
|
|
4
|
+
require "i18n/tasks/scanners/relative_keys"
|
|
5
|
+
require "i18n/tasks/scanners/occurrence_from_position"
|
|
6
|
+
require "i18n/tasks/scanners/ruby_key_literals"
|
|
7
7
|
|
|
8
8
|
module I18n::Tasks::Scanners
|
|
9
9
|
# Maps the provided patterns to keys.
|
|
@@ -34,14 +34,14 @@ module I18n::Tasks::Scanners
|
|
|
34
34
|
@patterns.flat_map do |pattern, key|
|
|
35
35
|
result = []
|
|
36
36
|
text.scan(pattern) do |_|
|
|
37
|
-
match
|
|
38
|
-
matches
|
|
37
|
+
match = Regexp.last_match
|
|
38
|
+
matches = match.names.map(&:to_sym).zip(match.captures).to_h
|
|
39
39
|
if matches.key?(:key)
|
|
40
40
|
matches[:key] = strip_literal(matches[:key])
|
|
41
41
|
next unless valid_key?(matches[:key])
|
|
42
42
|
end
|
|
43
43
|
result << [absolute_key(format(key, matches), path),
|
|
44
|
-
|
|
44
|
+
occurrence_from_position(path, text, match.offset(0).first)]
|
|
45
45
|
end
|
|
46
46
|
result
|
|
47
47
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
3
|
+
require "i18n/tasks/scanners/file_scanner"
|
|
4
|
+
require "i18n/tasks/scanners/relative_keys"
|
|
5
|
+
require "i18n/tasks/scanners/occurrence_from_position"
|
|
6
|
+
require "i18n/tasks/scanners/ruby_key_literals"
|
|
7
7
|
|
|
8
8
|
module I18n::Tasks::Scanners
|
|
9
9
|
# Scan for I18n.t usages using a simple regular expression.
|
|
@@ -12,15 +12,15 @@ module I18n::Tasks::Scanners
|
|
|
12
12
|
include OccurrenceFromPosition
|
|
13
13
|
include RubyKeyLiterals
|
|
14
14
|
|
|
15
|
-
TRANSLATE_CALL_RE = /(?<=^|[^\p{L}_'\-.]|[^\p{L}'-]I18n\.|I18n\.)t(?:!|ranslate!?)
|
|
15
|
+
TRANSLATE_CALL_RE = /(?<=^|[^\p{L}_'\-.]|[^\p{L}'-]I18n\.|I18n\.)t(?:!|ranslate!?)?/
|
|
16
16
|
IGNORE_LINES = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
"coffee" => /^\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)}
|
|
24
24
|
}.freeze
|
|
25
25
|
|
|
26
26
|
def initialize(**args)
|
|
@@ -47,13 +47,13 @@ module I18n::Tasks::Scanners
|
|
|
47
47
|
key = match_to_key(match, path, location)
|
|
48
48
|
next unless key
|
|
49
49
|
|
|
50
|
-
key +=
|
|
50
|
+
key += ":" if key.end_with?(".")
|
|
51
51
|
next unless valid_key?(key)
|
|
52
52
|
|
|
53
53
|
keys << [key, location]
|
|
54
54
|
end
|
|
55
55
|
keys
|
|
56
|
-
rescue
|
|
56
|
+
rescue => e
|
|
57
57
|
raise ::I18n::Tasks::CommandError.new(e, "Error scanning #{path}: #{e.message}")
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -62,7 +62,7 @@ module I18n::Tasks::Scanners
|
|
|
62
62
|
# @return [String] full absolute key name
|
|
63
63
|
def match_to_key(match, path, location)
|
|
64
64
|
absolute_key(strip_literal(match[0]), path,
|
|
65
|
-
|
|
65
|
+
calling_method: -> { closest_method(location) if key_relative_to_method?(path) })
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def exclude_line?(line, path)
|
|
@@ -70,11 +70,11 @@ module I18n::Tasks::Scanners
|
|
|
70
70
|
re && re =~ line
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
VALID_KEY_RE_DYNAMIC = /^(#{VALID_KEY_CHARS}|[:\#{@}\[\]])
|
|
73
|
+
VALID_KEY_RE_DYNAMIC = /^(#{VALID_KEY_CHARS}|[:\#{@}\[\]])+$/
|
|
74
74
|
|
|
75
75
|
def valid_key?(key)
|
|
76
76
|
if @config[:strict]
|
|
77
|
-
super
|
|
77
|
+
super
|
|
78
78
|
else
|
|
79
79
|
key =~ VALID_KEY_RE_DYNAMIC
|
|
80
80
|
end
|
|
@@ -85,9 +85,9 @@ module I18n::Tasks::Scanners
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def closest_method(occurrence)
|
|
88
|
-
method = File.readlines(occurrence.path, encoding:
|
|
89
|
-
|
|
90
|
-
method && method.strip.sub(/^def\s*/,
|
|
88
|
+
method = File.readlines(occurrence.path, encoding: "UTF-8")
|
|
89
|
+
.first(occurrence.line_num - 1).reverse_each.find { |x| x =~ /\bdef\b/ }
|
|
90
|
+
method && method.strip.sub(/^def\s*/, "").sub(/[(\s;].*$/, "")
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
# This method only exists for backwards compatibility with monkey-patches and plugins
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "i18n/tasks/scanners/pattern_scanner"
|
|
4
4
|
|
|
5
5
|
module I18n::Tasks::Scanners
|
|
6
6
|
# Scans for I18n.t(key, scope: ...) usages
|
|
@@ -21,13 +21,13 @@ module I18n::Tasks::Scanners
|
|
|
21
21
|
# @param [String] path
|
|
22
22
|
# @return [String] full absolute key name with scope resolved if any
|
|
23
23
|
def match_to_key(match, path, location)
|
|
24
|
-
key
|
|
24
|
+
key = super
|
|
25
25
|
scope = match[1]
|
|
26
26
|
if scope
|
|
27
27
|
scope_parts = extract_literal_or_array_of_literals(scope)
|
|
28
28
|
return nil if scope_parts.nil? || scope_parts.empty?
|
|
29
29
|
|
|
30
|
-
"#{scope_parts.join(
|
|
30
|
+
"#{scope_parts.join(".")}.#{key}"
|
|
31
31
|
else
|
|
32
32
|
key unless match[0] =~ /\A\w/
|
|
33
33
|
end
|
|
@@ -43,7 +43,7 @@ module I18n::Tasks::Scanners
|
|
|
43
43
|
if val =~ /\A[\w@]/
|
|
44
44
|
"\#{#{val}}"
|
|
45
45
|
else
|
|
46
|
-
super
|
|
46
|
+
super
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -77,18 +77,18 @@ module I18n::Tasks::Scanners
|
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
s.each_char.with_index do |c, i|
|
|
80
|
-
if c ==
|
|
80
|
+
if c == "["
|
|
81
81
|
return nil unless braces_stack.empty?
|
|
82
82
|
|
|
83
83
|
braces_stack.push(i)
|
|
84
|
-
elsif c ==
|
|
84
|
+
elsif c == "]"
|
|
85
85
|
break
|
|
86
|
-
elsif c ==
|
|
86
|
+
elsif c == ","
|
|
87
87
|
consume_literal.call
|
|
88
88
|
break if braces_stack.empty?
|
|
89
89
|
elsif c =~ VALID_KEY_CHARS || /['":]/ =~ c
|
|
90
90
|
acc << c
|
|
91
|
-
elsif c !=
|
|
91
|
+
elsif c != " "
|
|
92
92
|
return nil
|
|
93
93
|
end
|
|
94
94
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "prism/visitor"
|
|
4
4
|
|
|
5
5
|
# This class is used to parse the arguments to e.g. a Prism::CallNode and return the values we need
|
|
6
6
|
# for turning them into translations and occurrences.
|
|
@@ -9,11 +9,18 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
9
9
|
class ArgumentsVisitor < Prism::Visitor
|
|
10
10
|
def visit_keyword_hash_node(node)
|
|
11
11
|
node.child_nodes.each_with_object({}) do |child, hash|
|
|
12
|
+
next if child.type == :assoc_splat_node
|
|
13
|
+
|
|
12
14
|
hash[visit(child.key)] = visit(child.value)
|
|
13
15
|
hash
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# Cannot handle arguments that are calls
|
|
20
|
+
def visit_call_node(_node)
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
|
|
17
24
|
def visit_symbol_node(node)
|
|
18
25
|
node.value
|
|
19
26
|
end
|
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
# Used in the PrismScanners::Visitor class.
|
|
5
5
|
module I18n::Tasks::Scanners::PrismScanners
|
|
6
6
|
class Root
|
|
7
|
-
attr_reader(:calls, :translation_calls, :children, :node, :parent)
|
|
7
|
+
attr_reader(:calls, :translation_calls, :children, :node, :parent, :rails, :file_path)
|
|
8
8
|
|
|
9
|
-
def initialize(node: nil, parent: nil)
|
|
9
|
+
def initialize(node: nil, parent: nil, file_path: nil, rails: false)
|
|
10
10
|
@calls = []
|
|
11
11
|
@translation_calls = []
|
|
12
12
|
@children = []
|
|
13
13
|
@node = node
|
|
14
14
|
@parent = parent
|
|
15
|
+
@rails = rails
|
|
16
|
+
@file_path = file_path
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def add_child(node)
|
|
@@ -24,39 +26,61 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def add_translation_call(translation_call)
|
|
27
|
-
@translation_calls
|
|
29
|
+
@translation_calls += Array(translation_call)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def rails_view?
|
|
33
|
+
rails && file_path.present? && file_path.include?("app/views/")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def partial_view?
|
|
37
|
+
file_path.present? && File.basename(file_path).start_with?("_")
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
def support_relative_keys?
|
|
31
|
-
|
|
41
|
+
rails_view? && !partial_view?
|
|
32
42
|
end
|
|
33
43
|
|
|
34
|
-
def
|
|
44
|
+
def support_candidate_keys?
|
|
35
45
|
false
|
|
36
46
|
end
|
|
37
47
|
|
|
38
48
|
def path
|
|
39
|
-
|
|
49
|
+
if rails_view?
|
|
50
|
+
folder_path = file_path.sub(%r{app/views/}, "").split("/")
|
|
51
|
+
name = folder_path.pop.split(".").first
|
|
52
|
+
|
|
53
|
+
[*folder_path, name]
|
|
54
|
+
else
|
|
55
|
+
[]
|
|
56
|
+
end
|
|
40
57
|
end
|
|
41
58
|
|
|
42
59
|
def process
|
|
43
60
|
(@translation_calls + @children.flat_map(&:process)).flatten
|
|
44
61
|
end
|
|
62
|
+
|
|
63
|
+
# Only supported for Rails controllers currently
|
|
64
|
+
def private_method
|
|
65
|
+
false
|
|
66
|
+
end
|
|
45
67
|
end
|
|
46
68
|
|
|
47
69
|
class TranslationCall
|
|
70
|
+
class ScopeError < StandardError; end
|
|
48
71
|
attr_reader(:node, :key, :receiver, :options, :parent)
|
|
49
72
|
|
|
50
|
-
def initialize(node:, key:, receiver:, options:, parent:)
|
|
73
|
+
def initialize(node:, key:, receiver:, options:, parent:, candidate_keys: nil)
|
|
51
74
|
@node = node
|
|
52
75
|
@key = key
|
|
53
76
|
@receiver = receiver
|
|
54
77
|
@options = options
|
|
55
78
|
@parent = parent
|
|
79
|
+
@candidate_keys = candidate_keys || []
|
|
56
80
|
end
|
|
57
81
|
|
|
58
82
|
def relative_key?
|
|
59
|
-
@key&.start_with?(
|
|
83
|
+
@key&.start_with?(".") && @receiver.nil?
|
|
60
84
|
end
|
|
61
85
|
|
|
62
86
|
def with_parent(parent)
|
|
@@ -83,34 +107,54 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
83
107
|
occurrence(file_path)
|
|
84
108
|
end
|
|
85
109
|
|
|
110
|
+
# Returns either a single key string or an array of candidate key strings for this call.
|
|
86
111
|
def full_key
|
|
87
112
|
return nil if key.nil?
|
|
88
113
|
return nil unless key.is_a?(String)
|
|
89
114
|
return nil if relative_key? && !support_relative_keys?
|
|
90
115
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if relative_key?
|
|
94
|
-
|
|
95
|
-
|
|
116
|
+
base_parts = [scope].compact
|
|
117
|
+
|
|
118
|
+
if relative_key? && support_candidate_keys?
|
|
119
|
+
# For relative keys in controllers/methods, generate candidate keys by
|
|
120
|
+
# progressively stripping trailing path segments from the parent path.
|
|
121
|
+
# Example: parent.path = ["events", "create"], key = ".success"
|
|
122
|
+
# yields: ["events.create.success", "events.success"]
|
|
123
|
+
parent_path = parent&.path || []
|
|
124
|
+
rel_key = key[1..] # strip leading dot # rubocop:disable Performance/ArraySemiInfiniteRangeSlice
|
|
125
|
+
|
|
126
|
+
candidates = []
|
|
127
|
+
parent_path_length = parent_path.length
|
|
128
|
+
# Do not generate an unscoped bare key (keep_count = 0). Start from full parent path
|
|
129
|
+
parent_path_length.downto(1) do |keep_count|
|
|
130
|
+
parts = base_parts + parent_path.first(keep_count) + [rel_key]
|
|
131
|
+
candidates << parts.compact.join(".")
|
|
132
|
+
end
|
|
96
133
|
|
|
97
|
-
|
|
98
|
-
elsif
|
|
99
|
-
|
|
134
|
+
candidates.map { |c| c.gsub("..", ".") }
|
|
135
|
+
elsif relative_key?
|
|
136
|
+
# For relative keys in views, just append to the full path
|
|
137
|
+
[base_parts + parent.path + [key[1..]]].flatten.compact.join(".").gsub("..", ".") # rubocop:disable Performance/ChainArrayAllocation
|
|
138
|
+
elsif key.start_with?(".")
|
|
139
|
+
[base_parts + [key[1..]]].flatten.compact.join(".").gsub("..", ".") # rubocop:disable Performance/ArraySemiInfiniteRangeSlice,Performance/ChainArrayAllocation
|
|
140
|
+
elsif @candidate_keys.present?
|
|
141
|
+
([key] + @candidate_keys).map do |c|
|
|
142
|
+
[base_parts + [c]].flatten.compact.join(".").gsub("..", ".") # rubocop:disable Performance/ChainArrayAllocation
|
|
143
|
+
end
|
|
100
144
|
else
|
|
101
|
-
|
|
145
|
+
[base_parts + [key]].flatten.compact.join(".").gsub("..", ".") # rubocop:disable Performance/ChainArrayAllocation
|
|
102
146
|
end
|
|
103
|
-
|
|
104
|
-
parts.compact.join('.').gsub('..', '.')
|
|
105
147
|
end
|
|
106
148
|
|
|
107
149
|
private
|
|
108
150
|
|
|
109
151
|
def scope
|
|
110
152
|
return nil if @options.nil?
|
|
111
|
-
return nil unless @options[
|
|
153
|
+
return nil unless @options["scope"]
|
|
154
|
+
|
|
155
|
+
fail(ScopeError, "Could not process scope") if @options.key?("scope") && (Array(@options["scope"]).empty? || !Array(@options["scope"]).all? { |s| s.is_a?(String) || s.is_a?(Symbol) })
|
|
112
156
|
|
|
113
|
-
Array(@options[
|
|
157
|
+
Array(@options["scope"]).join(".")
|
|
114
158
|
end
|
|
115
159
|
|
|
116
160
|
def occurrence(file_path)
|
|
@@ -118,38 +162,38 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
118
162
|
|
|
119
163
|
location = local_node.location
|
|
120
164
|
|
|
121
|
-
|
|
122
|
-
return nil if
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
child_node.with_context(
|
|
140
|
-
path: @path,
|
|
141
|
-
options: {
|
|
142
|
-
**@context_options,
|
|
143
|
-
comment_for_node: @node
|
|
144
|
-
}
|
|
145
|
-
).occurrences(file_path)
|
|
165
|
+
final = full_key
|
|
166
|
+
return nil if final.nil?
|
|
167
|
+
|
|
168
|
+
occurrence = ::I18n::Tasks::Scanners::Results::Occurrence.new(
|
|
169
|
+
path: file_path,
|
|
170
|
+
line: local_node.respond_to?(:slice) ? local_node.slice : local_node.location.slice,
|
|
171
|
+
pos: location.start_offset,
|
|
172
|
+
line_pos: location.start_column,
|
|
173
|
+
line_num: location.start_line,
|
|
174
|
+
raw_key: key,
|
|
175
|
+
candidate_keys: Array(final)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# full_key may be a single String or an Array of candidate strings
|
|
179
|
+
if final.is_a?(Array)
|
|
180
|
+
[final.first, occurrence]
|
|
181
|
+
else
|
|
182
|
+
[final, occurrence]
|
|
146
183
|
end
|
|
184
|
+
rescue ScopeError
|
|
185
|
+
nil
|
|
147
186
|
end
|
|
148
187
|
|
|
149
188
|
# Only public methods are added to the context path
|
|
150
189
|
# Only some classes supports relative keys
|
|
151
190
|
def support_relative_keys?
|
|
152
|
-
parent.is_a?(ParsedMethod) && parent.support_relative_keys?
|
|
191
|
+
(parent.is_a?(ParsedMethod) || parent.is_a?(Root)) && parent.support_relative_keys?
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Not supported for Rails views
|
|
195
|
+
def support_candidate_keys?
|
|
196
|
+
support_relative_keys? && parent.support_candidate_keys?
|
|
153
197
|
end
|
|
154
198
|
end
|
|
155
199
|
|
|
@@ -158,10 +202,6 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
158
202
|
false
|
|
159
203
|
end
|
|
160
204
|
|
|
161
|
-
def private_method
|
|
162
|
-
false
|
|
163
|
-
end
|
|
164
|
-
|
|
165
205
|
def path
|
|
166
206
|
(@parent&.path || []) + [path_name]
|
|
167
207
|
end
|
|
@@ -179,9 +219,8 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
179
219
|
@methods = []
|
|
180
220
|
@private_methods = []
|
|
181
221
|
@before_actions = []
|
|
182
|
-
@rails = rails
|
|
183
222
|
|
|
184
|
-
super
|
|
223
|
+
super
|
|
185
224
|
end
|
|
186
225
|
|
|
187
226
|
def add_child(node)
|
|
@@ -200,8 +239,14 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
200
239
|
end
|
|
201
240
|
|
|
202
241
|
def process # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
203
|
-
|
|
242
|
+
if controller?
|
|
243
|
+
process_controller
|
|
244
|
+
else
|
|
245
|
+
super
|
|
246
|
+
end
|
|
247
|
+
end
|
|
204
248
|
|
|
249
|
+
def process_controller
|
|
205
250
|
methods_by_name = @methods.group_by(&:name)
|
|
206
251
|
private_methods_by_name = @private_methods.group_by(&:name)
|
|
207
252
|
|
|
@@ -221,7 +266,9 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
221
266
|
next unless before_action.applies_to?(method.name)
|
|
222
267
|
|
|
223
268
|
method.add_translation_call(
|
|
224
|
-
translation_calls.map
|
|
269
|
+
translation_calls.map do |call|
|
|
270
|
+
call.with_parent(method)
|
|
271
|
+
end
|
|
225
272
|
)
|
|
226
273
|
end
|
|
227
274
|
end
|
|
@@ -240,7 +287,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
240
287
|
nested_calls[method.name] << other_method.name
|
|
241
288
|
|
|
242
289
|
if nested_calls[call.name]&.include?(method.name)
|
|
243
|
-
|
|
290
|
+
next
|
|
244
291
|
end
|
|
245
292
|
|
|
246
293
|
other_method.translation_calls.each do |translation_call|
|
|
@@ -249,7 +296,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
249
296
|
end
|
|
250
297
|
end
|
|
251
298
|
|
|
252
|
-
@children.flat_map(&:process) + new_translation_calls
|
|
299
|
+
@translation_calls + @children.flat_map(&:process) + new_translation_calls
|
|
253
300
|
end
|
|
254
301
|
|
|
255
302
|
def private_methods!
|
|
@@ -257,7 +304,11 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
257
304
|
end
|
|
258
305
|
|
|
259
306
|
def support_relative_keys?
|
|
260
|
-
|
|
307
|
+
controller? || mailer?
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def support_candidate_keys?
|
|
311
|
+
controller?
|
|
261
312
|
end
|
|
262
313
|
|
|
263
314
|
def path
|
|
@@ -265,12 +316,16 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
265
316
|
end
|
|
266
317
|
|
|
267
318
|
def controller?
|
|
268
|
-
@node.name.to_s.end_with?(
|
|
319
|
+
@rails && @node.name.to_s.end_with?("Controller")
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def mailer?
|
|
323
|
+
@rails && @node.name.to_s.end_with?("Mailer")
|
|
269
324
|
end
|
|
270
325
|
|
|
271
326
|
def path_name
|
|
272
327
|
path = @node.constant_path.full_name_parts.map { |s| s.to_s.underscore }
|
|
273
|
-
path.last.
|
|
328
|
+
path.last.delete_suffix!("_controller") if controller?
|
|
274
329
|
|
|
275
330
|
path
|
|
276
331
|
end
|
|
@@ -287,6 +342,8 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
287
342
|
!@private_method && @parent&.support_relative_keys?
|
|
288
343
|
end
|
|
289
344
|
|
|
345
|
+
delegate(:support_candidate_keys?, to: :parent)
|
|
346
|
+
|
|
290
347
|
def path
|
|
291
348
|
(@parent&.path || []) + [@node.name]
|
|
292
349
|
end
|
|
@@ -301,7 +358,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
301
358
|
end
|
|
302
359
|
|
|
303
360
|
class ParsedBeforeAction < Root
|
|
304
|
-
|
|
361
|
+
attr_accessor(:name, :only, :except)
|
|
305
362
|
|
|
306
363
|
def initialize(node:, parent:, name: nil, only: nil, except: nil)
|
|
307
364
|
@name = name
|
|
@@ -312,7 +369,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
312
369
|
end
|
|
313
370
|
|
|
314
371
|
def support_relative_keys?
|
|
315
|
-
|
|
372
|
+
false
|
|
316
373
|
end
|
|
317
374
|
|
|
318
375
|
def applies_to?(method_name)
|
|
@@ -330,5 +387,9 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
330
387
|
def path
|
|
331
388
|
@parent&.path || []
|
|
332
389
|
end
|
|
390
|
+
|
|
391
|
+
def process
|
|
392
|
+
@translation_calls.filter { |call| !call.relative_key? }
|
|
393
|
+
end
|
|
333
394
|
end
|
|
334
395
|
end
|