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,83 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'file_scanner'
|
|
4
|
-
require_relative 'ruby_ast_scanner'
|
|
5
|
-
|
|
6
|
-
module I18n::Tasks::Scanners
|
|
7
|
-
class PrismScanner < FileScanner
|
|
8
|
-
MAGIC_COMMENT_SKIP_PRISM = 'i18n-tasks-skip-prism'
|
|
9
|
-
|
|
10
|
-
def initialize(**args)
|
|
11
|
-
unless VISITOR
|
|
12
|
-
warn(
|
|
13
|
-
'Please make sure `prism` is available to use this feature. Fallback to Ruby AST Scanner.'
|
|
14
|
-
)
|
|
15
|
-
end
|
|
16
|
-
super
|
|
17
|
-
|
|
18
|
-
@fallback = RubyAstScanner.new(**args)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
protected
|
|
22
|
-
|
|
23
|
-
# Extract all occurrences of translate calls from the file at the given path.
|
|
24
|
-
#
|
|
25
|
-
# @return [Array<[key, Results::KeyOccurrence]>] each occurrence found in the file
|
|
26
|
-
def scan_file(path)
|
|
27
|
-
return @fallback.send(:scan_file, path) if VISITOR.nil?
|
|
28
|
-
|
|
29
|
-
process_results(path, PARSER.parse_file(path))
|
|
30
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
31
|
-
raise(
|
|
32
|
-
::I18n::Tasks::CommandError.new(
|
|
33
|
-
e,
|
|
34
|
-
"Error scanning #{path}: #{e.message}"
|
|
35
|
-
)
|
|
36
|
-
)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Need to have method that can be overridden to be able to test it
|
|
40
|
-
def process_results(path, parse_results)
|
|
41
|
-
parsed = parse_results.value
|
|
42
|
-
comments = parse_results.comments
|
|
43
|
-
|
|
44
|
-
return @fallback.send(:scan_file, path) if skip_prism_comment?(comments)
|
|
45
|
-
|
|
46
|
-
rails = if config[:prism_visitor].blank?
|
|
47
|
-
true
|
|
48
|
-
else
|
|
49
|
-
config[:prism_visitor] != 'ruby'
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
visitor = VISITOR.new(comments: comments, rails: rails)
|
|
53
|
-
parsed.accept(visitor)
|
|
54
|
-
|
|
55
|
-
occurrences = []
|
|
56
|
-
visitor.process.each do |translation_call|
|
|
57
|
-
result = translation_call.occurrences(path)
|
|
58
|
-
occurrences << result if result
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
occurrences
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def skip_prism_comment?(comments)
|
|
65
|
-
comments.any? do |comment|
|
|
66
|
-
content =
|
|
67
|
-
comment.respond_to?(:slice) ? comment.slice : comment.location.slice
|
|
68
|
-
content.include?(MAGIC_COMMENT_SKIP_PRISM)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# This block handles adding a fallback if the `prism` gem is not available.
|
|
73
|
-
begin
|
|
74
|
-
require 'prism'
|
|
75
|
-
require_relative 'prism_scanners/visitor'
|
|
76
|
-
PARSER = Prism
|
|
77
|
-
VISITOR = I18n::Tasks::Scanners::PrismScanners::Visitor
|
|
78
|
-
rescue LoadError
|
|
79
|
-
PARSER = nil
|
|
80
|
-
VISITOR = nil
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'i18n/tasks/scanners/file_scanner'
|
|
4
|
-
require 'i18n/tasks/scanners/relative_keys'
|
|
5
|
-
require 'i18n/tasks/scanners/ruby_ast_call_finder'
|
|
6
|
-
require 'i18n/tasks/scanners/ruby_parser_factory'
|
|
7
|
-
require 'i18n/tasks/scanners/ast_matchers/default_i18n_subject_matcher'
|
|
8
|
-
require 'i18n/tasks/scanners/ast_matchers/message_receivers_matcher'
|
|
9
|
-
require 'i18n/tasks/scanners/ast_matchers/rails_model_matcher'
|
|
10
|
-
|
|
11
|
-
module I18n::Tasks::Scanners
|
|
12
|
-
# Scan for I18n.translate calls using whitequark/parser
|
|
13
|
-
class RubyAstScanner < FileScanner
|
|
14
|
-
include RelativeKeys
|
|
15
|
-
include AST::Sexp
|
|
16
|
-
|
|
17
|
-
MAGIC_COMMENT_PREFIX = /\A.\s*i18n-tasks-use\s+/.freeze
|
|
18
|
-
|
|
19
|
-
def initialize(**args)
|
|
20
|
-
super(**args)
|
|
21
|
-
@parser = RubyParserFactory.create_parser
|
|
22
|
-
@magic_comment_parser = RubyParserFactory.create_parser
|
|
23
|
-
@matchers = setup_matchers
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
protected
|
|
27
|
-
|
|
28
|
-
# Extract all occurrences of translate calls from the file at the given path.
|
|
29
|
-
#
|
|
30
|
-
# @return [Array<[key, Results::KeyOccurrence]>] each occurrence found in the file
|
|
31
|
-
def scan_file(path)
|
|
32
|
-
ast, comments = path_to_ast_and_comments(path)
|
|
33
|
-
|
|
34
|
-
ast_to_occurences(ast) + comments_to_occurences(path, ast, comments)
|
|
35
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
36
|
-
raise ::I18n::Tasks::CommandError.new(e, "Error scanning #{path}: #{e.message}")
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Parse file on path and returns AST and comments.
|
|
40
|
-
#
|
|
41
|
-
# @param path Path to file to parse
|
|
42
|
-
# @return [{Parser::AST::Node}, [Parser::Source::Comment]]
|
|
43
|
-
def path_to_ast_and_comments(path)
|
|
44
|
-
@parser.reset
|
|
45
|
-
@parser.parse_with_comments(make_buffer(path))
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def keys_relative_to_calling_method?(path)
|
|
49
|
-
/controllers|mailers/.match(path)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Create an {Parser::Source::Buffer} with the given contents.
|
|
53
|
-
# The contents are assigned a {Parser::Source::Buffer#raw_source}.
|
|
54
|
-
#
|
|
55
|
-
# @param path [String] Path to assign as the buffer name.
|
|
56
|
-
# @param contents [String]
|
|
57
|
-
# @return [Parser::Source::Buffer] file contents
|
|
58
|
-
def make_buffer(path, contents = read_file(path))
|
|
59
|
-
Parser::Source::Buffer.new(path).tap do |buffer|
|
|
60
|
-
buffer.raw_source = contents
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Convert an array of {Parser::Source::Comment} to occurrences.
|
|
65
|
-
#
|
|
66
|
-
# @param path Path to file
|
|
67
|
-
# @param ast Parser::AST::Node
|
|
68
|
-
# @param comments [Parser::Source::Comment]
|
|
69
|
-
# @return [nil, [key, Occurrence]] full absolute key name and the occurrence.
|
|
70
|
-
def comments_to_occurences(path, ast, comments)
|
|
71
|
-
magic_comments = comments.select { |comment| comment.text =~ MAGIC_COMMENT_PREFIX }
|
|
72
|
-
comment_to_node = Parser::Source::Comment.associate_locations(ast, magic_comments).tap do |h|
|
|
73
|
-
h.transform_values!(&:first)
|
|
74
|
-
end.invert
|
|
75
|
-
|
|
76
|
-
magic_comments.flat_map do |comment|
|
|
77
|
-
@parser.reset
|
|
78
|
-
associated_node = comment_to_node[comment]
|
|
79
|
-
ast = @parser.parse(make_buffer(path, comment.text.sub(MAGIC_COMMENT_PREFIX, '').split(/\s+(?=t)/).join('; ')))
|
|
80
|
-
calls = RubyAstCallFinder.new.collect_calls(ast)
|
|
81
|
-
results = []
|
|
82
|
-
|
|
83
|
-
# method_name is not available at this stage
|
|
84
|
-
calls.each do |(send_node, _method_name)|
|
|
85
|
-
@matchers.each do |matcher|
|
|
86
|
-
result = matcher.convert_to_key_occurrences(
|
|
87
|
-
send_node,
|
|
88
|
-
nil,
|
|
89
|
-
location: associated_node || comment.location
|
|
90
|
-
)
|
|
91
|
-
results << result if result
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
results
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Convert {Parser::AST::Node} to occurrences.
|
|
100
|
-
#
|
|
101
|
-
# @param ast {Parser::Source::Comment}
|
|
102
|
-
# @return [nil, [key, Occurrence]] full absolute key name and the occurrence.
|
|
103
|
-
def ast_to_occurences(ast)
|
|
104
|
-
calls = RubyAstCallFinder.new.collect_calls(ast)
|
|
105
|
-
results = []
|
|
106
|
-
calls.each do |send_node, method_name|
|
|
107
|
-
@matchers.each do |matcher|
|
|
108
|
-
result = matcher.convert_to_key_occurrences(send_node, method_name)
|
|
109
|
-
results << result if result
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
results
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def setup_matchers
|
|
117
|
-
if config[:receiver_messages]
|
|
118
|
-
config[:receiver_messages].map do |receiver, message|
|
|
119
|
-
AstMatchers::MessageReceiversMatcher.new(
|
|
120
|
-
receivers: [receiver],
|
|
121
|
-
message: message,
|
|
122
|
-
scanner: self
|
|
123
|
-
)
|
|
124
|
-
end
|
|
125
|
-
else
|
|
126
|
-
matchers = %i[t t! translate translate!].map do |message|
|
|
127
|
-
AstMatchers::MessageReceiversMatcher.new(
|
|
128
|
-
receivers: [
|
|
129
|
-
AST::Node.new(:const, [nil, :I18n]),
|
|
130
|
-
nil
|
|
131
|
-
],
|
|
132
|
-
message: message,
|
|
133
|
-
scanner: self
|
|
134
|
-
)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
Array(config[:ast_matchers]).each do |class_name|
|
|
138
|
-
matchers << ActiveSupport::Inflector.constantize(class_name).new(scanner: self)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
matchers
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|