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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3231dd8c4f3772a49ed9db6862f6d34c9fabed656a1ba3588f2a0d24be158d50
4
- data.tar.gz: b5db9e81af6eff9256acf3b84bbbcea4a738c987f0485e04e127eba0236f01fd
3
+ metadata.gz: 71571170117cb1d0576fb7cbbe9ff289e5726a05366f1470b1fd3cc8442fd49c
4
+ data.tar.gz: a3d3f5468a9a93543538f6022e41bcaa4bafc65a52a84d6f425dfbbae25d6603
5
5
  SHA512:
6
- metadata.gz: 601af6d9cf98382b36cb7eebec4672c8918440f63899ef8e91cda798de0685971e331e352420c0f7d3c2f4a5dc056d80f7b0feb1b04e7aa847c69ebd273ff214
7
- data.tar.gz: d8e0c5b7bb14a6ab061f60360e869f293b2c608fde5a02f7ebb90495f8c0d0adec69d4665818bd96b2fe6f3c590434aa94ad35462e2777bd9925ac3150aa4273
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.0', group: :development
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.present?
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
- process_prism_results(path, Prism.parse_file(path))
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
- Results::KeyOccurrences.merge_keys collect_results.flatten(1)
22
- end
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module I18n
4
4
  module Tasks
5
- VERSION = "1.1.0"
5
+ VERSION = "1.1.1"
6
6
  end
7
7
  end
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.0
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-14 00:00:00.000000000 Z
11
+ date: 2025-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport