i18n-tasks 1.1.0 → 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 +1 -1
- data/lib/i18n/tasks/scanners/erb_ast_scanner.rb +4 -3
- data/lib/i18n/tasks/scanners/occurrence_from_position.rb +3 -2
- data/lib/i18n/tasks/scanners/prism_scanners/nodes.rb +27 -6
- data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +18 -1
- data/lib/i18n/tasks/scanners/ruby_scanner.rb +2 -1
- data/lib/i18n/tasks/scanners/scanner_multiplexer.rb +2 -23
- data/lib/i18n/tasks/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 71571170117cb1d0576fb7cbbe9ff289e5726a05366f1470b1fd3cc8442fd49c
|
|
4
|
+
data.tar.gz: a3d3f5468a9a93543538f6022e41bcaa4bafc65a52a84d6f425dfbbae25d6603
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40583950dc98e2ef6f7cb3d7af909984d97b68e036b1a6ebcbab51f6dcb627e9d7cf7a42a6feff8be1417b0e4663984311e3b3156c16091747f7857f0e68c8b4
|
|
7
|
+
data.tar.gz: 025ada2029c316a7ce2a9286ae7dd28a971e628a6adbca6f22aa119579104710cd705ab2b303964997cabfe538b142fb62342a2e18807670f56b02841d792582
|
data/README.md
CHANGED
|
@@ -24,7 +24,7 @@ i18n-tasks can be used with any project using the ruby [i18n gem][i18n-gem] (def
|
|
|
24
24
|
Add i18n-tasks to the Gemfile:
|
|
25
25
|
|
|
26
26
|
```ruby
|
|
27
|
-
gem 'i18n-tasks', '~> 1.1.
|
|
27
|
+
gem 'i18n-tasks', '~> 1.1.1', group: :development
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Copy the default [configuration file](#configuration):
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "i18n/tasks/scanners/ruby_scanner"
|
|
4
4
|
require "i18n/tasks/scanners/local_ruby_parser"
|
|
5
5
|
require "i18n/tasks/scanners/occurrence_from_position"
|
|
6
|
-
require "prism"
|
|
7
6
|
|
|
8
7
|
module I18n::Tasks::Scanners
|
|
9
8
|
# Scan for I18n.translate calls in ERB-file using regexp and Parser/Prism
|
|
@@ -111,7 +110,8 @@ module I18n::Tasks::Scanners
|
|
|
111
110
|
path,
|
|
112
111
|
content,
|
|
113
112
|
start + occurrence.pos,
|
|
114
|
-
raw_key: occurrence.raw_key
|
|
113
|
+
raw_key: occurrence.raw_key,
|
|
114
|
+
candidate_keys: occurrence.candidate_keys
|
|
115
115
|
)
|
|
116
116
|
]
|
|
117
117
|
end
|
|
@@ -128,7 +128,8 @@ module I18n::Tasks::Scanners
|
|
|
128
128
|
path,
|
|
129
129
|
content,
|
|
130
130
|
start + (code.index(key) || occurrence.pos),
|
|
131
|
-
raw_key: occurrence.raw_key
|
|
131
|
+
raw_key: occurrence.raw_key,
|
|
132
|
+
candidate_keys: occurrence.candidate_keys
|
|
132
133
|
)
|
|
133
134
|
]
|
|
134
135
|
end
|
|
@@ -11,7 +11,7 @@ module I18n
|
|
|
11
11
|
# @param contents [String] contents of the file at the path.
|
|
12
12
|
# @param position [Integer] position just before the beginning of the match.
|
|
13
13
|
# @return [Results::Occurrence]
|
|
14
|
-
def occurrence_from_position(path, contents, position, raw_key: nil)
|
|
14
|
+
def occurrence_from_position(path, contents, position, raw_key: nil, candidate_keys: nil)
|
|
15
15
|
line_begin = contents.rindex(/^/, position - 1)
|
|
16
16
|
line_end = contents.index(/.(?=\r?\n|$)/, position)
|
|
17
17
|
Results::Occurrence.new(
|
|
@@ -20,7 +20,8 @@ module I18n
|
|
|
20
20
|
line_num: contents[0..position].count("\n") + 1,
|
|
21
21
|
line_pos: position - line_begin + 1,
|
|
22
22
|
line: contents[line_begin..line_end],
|
|
23
|
-
raw_key: raw_key
|
|
23
|
+
raw_key: raw_key,
|
|
24
|
+
candidate_keys: candidate_keys
|
|
24
25
|
)
|
|
25
26
|
end
|
|
26
27
|
end
|
|
@@ -41,6 +41,10 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
41
41
|
rails_view? && !partial_view?
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
def support_candidate_keys?
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
44
48
|
def path
|
|
45
49
|
if rails_view?
|
|
46
50
|
folder_path = file_path.sub(%r{app/views/}, "").split("/")
|
|
@@ -66,12 +70,13 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
66
70
|
class ScopeError < StandardError; end
|
|
67
71
|
attr_reader(:node, :key, :receiver, :options, :parent)
|
|
68
72
|
|
|
69
|
-
def initialize(node:, key:, receiver:, options:, parent:)
|
|
73
|
+
def initialize(node:, key:, receiver:, options:, parent:, candidate_keys: nil)
|
|
70
74
|
@node = node
|
|
71
75
|
@key = key
|
|
72
76
|
@receiver = receiver
|
|
73
77
|
@options = options
|
|
74
78
|
@parent = parent
|
|
79
|
+
@candidate_keys = candidate_keys || []
|
|
75
80
|
end
|
|
76
81
|
|
|
77
82
|
def relative_key?
|
|
@@ -110,7 +115,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
110
115
|
|
|
111
116
|
base_parts = [scope].compact
|
|
112
117
|
|
|
113
|
-
if relative_key?
|
|
118
|
+
if relative_key? && support_candidate_keys?
|
|
114
119
|
# For relative keys in controllers/methods, generate candidate keys by
|
|
115
120
|
# progressively stripping trailing path segments from the parent path.
|
|
116
121
|
# Example: parent.path = ["events", "create"], key = ".success"
|
|
@@ -127,8 +132,15 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
127
132
|
end
|
|
128
133
|
|
|
129
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
|
|
130
138
|
elsif key.start_with?(".")
|
|
131
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
|
|
132
144
|
else
|
|
133
145
|
[base_parts + [key]].flatten.compact.join(".").gsub("..", ".") # rubocop:disable Performance/ChainArrayAllocation
|
|
134
146
|
end
|
|
@@ -159,16 +171,14 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
159
171
|
pos: location.start_offset,
|
|
160
172
|
line_pos: location.start_column,
|
|
161
173
|
line_num: location.start_line,
|
|
162
|
-
raw_key: key
|
|
174
|
+
raw_key: key,
|
|
175
|
+
candidate_keys: Array(final)
|
|
163
176
|
)
|
|
164
177
|
|
|
165
178
|
# full_key may be a single String or an Array of candidate strings
|
|
166
179
|
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
180
|
[final.first, occurrence]
|
|
170
181
|
else
|
|
171
|
-
occurrence.instance_variable_set(:@candidate_keys, [final])
|
|
172
182
|
[final, occurrence]
|
|
173
183
|
end
|
|
174
184
|
rescue ScopeError
|
|
@@ -180,6 +190,11 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
180
190
|
def support_relative_keys?
|
|
181
191
|
(parent.is_a?(ParsedMethod) || parent.is_a?(Root)) && parent.support_relative_keys?
|
|
182
192
|
end
|
|
193
|
+
|
|
194
|
+
# Not supported for Rails views
|
|
195
|
+
def support_candidate_keys?
|
|
196
|
+
support_relative_keys? && parent.support_candidate_keys?
|
|
197
|
+
end
|
|
183
198
|
end
|
|
184
199
|
|
|
185
200
|
class ParsedModule < Root
|
|
@@ -292,6 +307,10 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
292
307
|
controller? || mailer?
|
|
293
308
|
end
|
|
294
309
|
|
|
310
|
+
def support_candidate_keys?
|
|
311
|
+
controller?
|
|
312
|
+
end
|
|
313
|
+
|
|
295
314
|
def path
|
|
296
315
|
(@parent&.path || []) + [path_name]
|
|
297
316
|
end
|
|
@@ -323,6 +342,8 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
323
342
|
!@private_method && @parent&.support_relative_keys?
|
|
324
343
|
end
|
|
325
344
|
|
|
345
|
+
delegate(:support_candidate_keys?, to: :parent)
|
|
346
|
+
|
|
326
347
|
def path
|
|
327
348
|
(@parent&.path || []) + [@node.name]
|
|
328
349
|
end
|
|
@@ -95,6 +95,8 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
95
95
|
@current_class&.private_methods!
|
|
96
96
|
when :t, :t!, :translate, :translate!
|
|
97
97
|
args, kwargs = process_arguments(node)
|
|
98
|
+
# Do not process other receivers than I18n, e.g. Service.translate(:key)
|
|
99
|
+
return if node.receiver.present? && !i18n_receiver?(node.receiver)
|
|
98
100
|
parent.add_translation_call(
|
|
99
101
|
TranslationCall.new(
|
|
100
102
|
node: node,
|
|
@@ -160,6 +162,17 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
160
162
|
end
|
|
161
163
|
end
|
|
162
164
|
|
|
165
|
+
def i18n_receiver?(receiver)
|
|
166
|
+
case receiver.type
|
|
167
|
+
when :constant_read_node
|
|
168
|
+
receiver.name == :I18n
|
|
169
|
+
when :constant_path_node
|
|
170
|
+
receiver.parent.nil? && receiver.child&.name == :I18n
|
|
171
|
+
else
|
|
172
|
+
false
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
163
176
|
# ---- Rails specific methods ----
|
|
164
177
|
# Returns true if the node was handled
|
|
165
178
|
def handle_rails_call_node(node, &)
|
|
@@ -268,7 +281,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
268
281
|
# We need to check for `node.receiver` since node is the `human` call
|
|
269
282
|
model_name = if current_class.present? && rails_model_method_called_on_current_class?(node.receiver)
|
|
270
283
|
current_class.path.flatten.map!(&:underscore).join(".")
|
|
271
|
-
elsif node.receiver
|
|
284
|
+
elsif node.receiver&.receiver&.type == :constant_read_node
|
|
272
285
|
node.receiver&.receiver&.name&.to_s&.underscore
|
|
273
286
|
end
|
|
274
287
|
|
|
@@ -289,6 +302,9 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
289
302
|
node: node,
|
|
290
303
|
receiver: nil,
|
|
291
304
|
key: [:activerecord, :models, model_name, count_key].join("."),
|
|
305
|
+
candidate_keys: [
|
|
306
|
+
[:activerecord, :models, model_name].join(".")
|
|
307
|
+
],
|
|
292
308
|
parent: parent,
|
|
293
309
|
options: kwargs
|
|
294
310
|
)
|
|
@@ -327,6 +343,7 @@ module I18n::Tasks::Scanners::PrismScanners
|
|
|
327
343
|
TranslationCall.new(
|
|
328
344
|
node: node,
|
|
329
345
|
key: key,
|
|
346
|
+
candidate_keys: Array([:attributes, array_args.first].join(".")),
|
|
330
347
|
receiver: nil,
|
|
331
348
|
parent: parent,
|
|
332
349
|
options: {}
|
|
@@ -167,7 +167,8 @@ module I18n::Tasks::Scanners
|
|
|
167
167
|
# Extract all occurrences of translate calls from the file at the given path.
|
|
168
168
|
# @return [Array<[key, Results::KeyOccurrence]>] each occurrence found in the file
|
|
169
169
|
def prism_parse_file(path)
|
|
170
|
-
|
|
170
|
+
# Need File.expand_path for JRuby
|
|
171
|
+
process_prism_results(path, Prism.parse_file(File.expand_path(path)))
|
|
171
172
|
end
|
|
172
173
|
|
|
173
174
|
# This method handles only parsing to be able to test it properly.
|
|
@@ -4,8 +4,6 @@ require "i18n/tasks/scanners/scanner"
|
|
|
4
4
|
|
|
5
5
|
module I18n::Tasks::Scanners
|
|
6
6
|
# Run multiple {Scanner Scanners} and merge their results.
|
|
7
|
-
# @note The scanners are run concurrently. A thread is spawned per each scanner.
|
|
8
|
-
# @since 0.9.0
|
|
9
7
|
class ScannerMultiplexer < Scanner
|
|
10
8
|
# @param scanners [Array<Scanner>]
|
|
11
9
|
def initialize(scanners:)
|
|
@@ -15,29 +13,10 @@ module I18n::Tasks::Scanners
|
|
|
15
13
|
|
|
16
14
|
# Collect the results of all the scanners. Occurrences of a key from multiple scanners are merged.
|
|
17
15
|
#
|
|
18
|
-
# @note The scanners are run concurrently. A thread is spawned per each scanner.
|
|
19
16
|
# @return (see Scanner#keys)
|
|
20
17
|
def keys
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
# @return [Array<Array<Results::KeyOccurrences>>]
|
|
27
|
-
def collect_results
|
|
28
|
-
return [@scanners[0].keys] if @scanners.length == 1
|
|
29
|
-
|
|
30
|
-
Array.new(@scanners.length).tap do |results|
|
|
31
|
-
results_mutex = Mutex.new
|
|
32
|
-
@scanners.map.with_index do |scanner, i|
|
|
33
|
-
Thread.start do
|
|
34
|
-
scanner_results = scanner.keys
|
|
35
|
-
results_mutex.synchronize do
|
|
36
|
-
results[i] = scanner_results
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end.each(&:join)
|
|
40
|
-
end
|
|
18
|
+
results = @scanners.map(&:keys)
|
|
19
|
+
Results::KeyOccurrences.merge_keys results.flatten(1)
|
|
41
20
|
end
|
|
42
21
|
end
|
|
43
22
|
end
|
data/lib/i18n/tasks/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: i18n-tasks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- glebm
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-11-
|
|
11
|
+
date: 2025-11-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|