i18n-tasks 1.0.15 → 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.
- 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 +69 -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 +1 -1
- 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 +101 -61
- data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +169 -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 +225 -0
- data/lib/i18n/tasks/scanners/scanner.rb +2 -2
- data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +1 -1
- 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,27 +26,44 @@ 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)
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
+
def rails_view?
|
|
33
|
+
rails && file_path.present? && file_path.include?("app/views/")
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
+
def partial_view?
|
|
37
|
+
file_path.present? && File.basename(file_path).start_with?("_")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def support_relative_keys?
|
|
41
|
+
rails_view? && !partial_view?
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
def path
|
|
39
|
-
|
|
45
|
+
if rails_view?
|
|
46
|
+
folder_path = file_path.sub(%r{app/views/}, "").split("/")
|
|
47
|
+
name = folder_path.pop.split(".").first
|
|
48
|
+
|
|
49
|
+
[*folder_path, name]
|
|
50
|
+
else
|
|
51
|
+
[]
|
|
52
|
+
end
|
|
40
53
|
end
|
|
41
54
|
|
|
42
55
|
def process
|
|
43
56
|
(@translation_calls + @children.flat_map(&:process)).flatten
|
|
44
57
|
end
|
|
58
|
+
|
|
59
|
+
# Only supported for Rails controllers currently
|
|
60
|
+
def private_method
|
|
61
|
+
false
|
|
62
|
+
end
|
|
45
63
|
end
|
|
46
64
|
|
|
47
65
|
class TranslationCall
|
|
66
|
+
class ScopeError < StandardError; end
|
|
48
67
|
attr_reader(:node, :key, :receiver, :options, :parent)
|
|
49
68
|
|
|
50
69
|
def initialize(node:, key:, receiver:, options:, parent:)
|
|
@@ -56,7 +75,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
56
75
|
end
|
|
57
76
|
|
|
58
77
|
def relative_key?
|
|
59
|
-
@key&.start_with?(
|
|
78
|
+
@key&.start_with?(".") && @receiver.nil?
|
|
60
79
|
end
|
|
61
80
|
|
|
62
81
|
def with_parent(parent)
|
|
@@ -83,34 +102,47 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
83
102
|
occurrence(file_path)
|
|
84
103
|
end
|
|
85
104
|
|
|
105
|
+
# Returns either a single key string or an array of candidate key strings for this call.
|
|
86
106
|
def full_key
|
|
87
107
|
return nil if key.nil?
|
|
88
108
|
return nil unless key.is_a?(String)
|
|
89
109
|
return nil if relative_key? && !support_relative_keys?
|
|
90
110
|
|
|
91
|
-
|
|
111
|
+
base_parts = [scope].compact
|
|
92
112
|
|
|
93
113
|
if relative_key?
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
# For relative keys in controllers/methods, generate candidate keys by
|
|
115
|
+
# progressively stripping trailing path segments from the parent path.
|
|
116
|
+
# Example: parent.path = ["events", "create"], key = ".success"
|
|
117
|
+
# yields: ["events.create.success", "events.success"]
|
|
118
|
+
parent_path = parent&.path || []
|
|
119
|
+
rel_key = key[1..] # strip leading dot # rubocop:disable Performance/ArraySemiInfiniteRangeSlice
|
|
120
|
+
|
|
121
|
+
candidates = []
|
|
122
|
+
parent_path_length = parent_path.length
|
|
123
|
+
# Do not generate an unscoped bare key (keep_count = 0). Start from full parent path
|
|
124
|
+
parent_path_length.downto(1) do |keep_count|
|
|
125
|
+
parts = base_parts + parent_path.first(keep_count) + [rel_key]
|
|
126
|
+
candidates << parts.compact.join(".")
|
|
127
|
+
end
|
|
96
128
|
|
|
97
|
-
|
|
98
|
-
elsif key.start_with?(
|
|
99
|
-
|
|
129
|
+
candidates.map { |c| c.gsub("..", ".") }
|
|
130
|
+
elsif key.start_with?(".")
|
|
131
|
+
[base_parts + [key[1..]]].flatten.compact.join(".").gsub("..", ".") # rubocop:disable Performance/ArraySemiInfiniteRangeSlice,Performance/ChainArrayAllocation
|
|
100
132
|
else
|
|
101
|
-
|
|
133
|
+
[base_parts + [key]].flatten.compact.join(".").gsub("..", ".") # rubocop:disable Performance/ChainArrayAllocation
|
|
102
134
|
end
|
|
103
|
-
|
|
104
|
-
parts.compact.join('.').gsub('..', '.')
|
|
105
135
|
end
|
|
106
136
|
|
|
107
137
|
private
|
|
108
138
|
|
|
109
139
|
def scope
|
|
110
140
|
return nil if @options.nil?
|
|
111
|
-
return nil unless @options[
|
|
141
|
+
return nil unless @options["scope"]
|
|
112
142
|
|
|
113
|
-
Array(@options[
|
|
143
|
+
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) })
|
|
144
|
+
|
|
145
|
+
Array(@options["scope"]).join(".")
|
|
114
146
|
end
|
|
115
147
|
|
|
116
148
|
def occurrence(file_path)
|
|
@@ -118,38 +150,35 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
118
150
|
|
|
119
151
|
location = local_node.location
|
|
120
152
|
|
|
121
|
-
|
|
122
|
-
return nil if
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
options: {
|
|
142
|
-
**@context_options,
|
|
143
|
-
comment_for_node: @node
|
|
144
|
-
}
|
|
145
|
-
).occurrences(file_path)
|
|
153
|
+
final = full_key
|
|
154
|
+
return nil if final.nil?
|
|
155
|
+
|
|
156
|
+
occurrence = ::I18n::Tasks::Scanners::Results::Occurrence.new(
|
|
157
|
+
path: file_path,
|
|
158
|
+
line: local_node.respond_to?(:slice) ? local_node.slice : local_node.location.slice,
|
|
159
|
+
pos: location.start_offset,
|
|
160
|
+
line_pos: location.start_column,
|
|
161
|
+
line_num: location.start_line,
|
|
162
|
+
raw_key: key
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# full_key may be a single String or an Array of candidate strings
|
|
166
|
+
if final.is_a?(Array)
|
|
167
|
+
# record candidate keys on the occurrence (first candidate is the primary)
|
|
168
|
+
occurrence.instance_variable_set(:@candidate_keys, final)
|
|
169
|
+
[final.first, occurrence]
|
|
170
|
+
else
|
|
171
|
+
occurrence.instance_variable_set(:@candidate_keys, [final])
|
|
172
|
+
[final, occurrence]
|
|
146
173
|
end
|
|
174
|
+
rescue ScopeError
|
|
175
|
+
nil
|
|
147
176
|
end
|
|
148
177
|
|
|
149
178
|
# Only public methods are added to the context path
|
|
150
179
|
# Only some classes supports relative keys
|
|
151
180
|
def support_relative_keys?
|
|
152
|
-
parent.is_a?(ParsedMethod) && parent.support_relative_keys?
|
|
181
|
+
(parent.is_a?(ParsedMethod) || parent.is_a?(Root)) && parent.support_relative_keys?
|
|
153
182
|
end
|
|
154
183
|
end
|
|
155
184
|
|
|
@@ -158,10 +187,6 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
158
187
|
false
|
|
159
188
|
end
|
|
160
189
|
|
|
161
|
-
def private_method
|
|
162
|
-
false
|
|
163
|
-
end
|
|
164
|
-
|
|
165
190
|
def path
|
|
166
191
|
(@parent&.path || []) + [path_name]
|
|
167
192
|
end
|
|
@@ -179,9 +204,8 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
179
204
|
@methods = []
|
|
180
205
|
@private_methods = []
|
|
181
206
|
@before_actions = []
|
|
182
|
-
@rails = rails
|
|
183
207
|
|
|
184
|
-
super
|
|
208
|
+
super
|
|
185
209
|
end
|
|
186
210
|
|
|
187
211
|
def add_child(node)
|
|
@@ -200,8 +224,14 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
200
224
|
end
|
|
201
225
|
|
|
202
226
|
def process # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
203
|
-
|
|
227
|
+
if controller?
|
|
228
|
+
process_controller
|
|
229
|
+
else
|
|
230
|
+
super
|
|
231
|
+
end
|
|
232
|
+
end
|
|
204
233
|
|
|
234
|
+
def process_controller
|
|
205
235
|
methods_by_name = @methods.group_by(&:name)
|
|
206
236
|
private_methods_by_name = @private_methods.group_by(&:name)
|
|
207
237
|
|
|
@@ -221,7 +251,9 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
221
251
|
next unless before_action.applies_to?(method.name)
|
|
222
252
|
|
|
223
253
|
method.add_translation_call(
|
|
224
|
-
translation_calls.map
|
|
254
|
+
translation_calls.map do |call|
|
|
255
|
+
call.with_parent(method)
|
|
256
|
+
end
|
|
225
257
|
)
|
|
226
258
|
end
|
|
227
259
|
end
|
|
@@ -240,7 +272,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
240
272
|
nested_calls[method.name] << other_method.name
|
|
241
273
|
|
|
242
274
|
if nested_calls[call.name]&.include?(method.name)
|
|
243
|
-
|
|
275
|
+
next
|
|
244
276
|
end
|
|
245
277
|
|
|
246
278
|
other_method.translation_calls.each do |translation_call|
|
|
@@ -249,7 +281,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
249
281
|
end
|
|
250
282
|
end
|
|
251
283
|
|
|
252
|
-
@children.flat_map(&:process) + new_translation_calls
|
|
284
|
+
@translation_calls + @children.flat_map(&:process) + new_translation_calls
|
|
253
285
|
end
|
|
254
286
|
|
|
255
287
|
def private_methods!
|
|
@@ -257,7 +289,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
257
289
|
end
|
|
258
290
|
|
|
259
291
|
def support_relative_keys?
|
|
260
|
-
|
|
292
|
+
controller? || mailer?
|
|
261
293
|
end
|
|
262
294
|
|
|
263
295
|
def path
|
|
@@ -265,12 +297,16 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
265
297
|
end
|
|
266
298
|
|
|
267
299
|
def controller?
|
|
268
|
-
@node.name.to_s.end_with?(
|
|
300
|
+
@rails && @node.name.to_s.end_with?("Controller")
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def mailer?
|
|
304
|
+
@rails && @node.name.to_s.end_with?("Mailer")
|
|
269
305
|
end
|
|
270
306
|
|
|
271
307
|
def path_name
|
|
272
308
|
path = @node.constant_path.full_name_parts.map { |s| s.to_s.underscore }
|
|
273
|
-
path.last.
|
|
309
|
+
path.last.delete_suffix!("_controller") if controller?
|
|
274
310
|
|
|
275
311
|
path
|
|
276
312
|
end
|
|
@@ -301,7 +337,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
301
337
|
end
|
|
302
338
|
|
|
303
339
|
class ParsedBeforeAction < Root
|
|
304
|
-
|
|
340
|
+
attr_accessor(:name, :only, :except)
|
|
305
341
|
|
|
306
342
|
def initialize(node:, parent:, name: nil, only: nil, except: nil)
|
|
307
343
|
@name = name
|
|
@@ -312,7 +348,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
312
348
|
end
|
|
313
349
|
|
|
314
350
|
def support_relative_keys?
|
|
315
|
-
|
|
351
|
+
false
|
|
316
352
|
end
|
|
317
353
|
|
|
318
354
|
def applies_to?(method_name)
|
|
@@ -330,5 +366,9 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
330
366
|
def path
|
|
331
367
|
@parent&.path || []
|
|
332
368
|
end
|
|
369
|
+
|
|
370
|
+
def process
|
|
371
|
+
@translation_calls.filter { |call| !call.relative_key? }
|
|
372
|
+
end
|
|
333
373
|
end
|
|
334
374
|
end
|