auto-l18n 0.1.0 → 0.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: 9eaeb61d41c636035f955c83afb385e40a020e4806bcc2553b965032b3966c64
4
- data.tar.gz: c062d54dd2f5b7e639eeb9304e31993aa0a7485703a4236868d86a731ccc1518
3
+ metadata.gz: 3e1461d71a3c1c714d84295c11605c26cb984a8939c07f265c61489036a1cc58
4
+ data.tar.gz: c5805b85181f8e87a65a25371c651d4cf936084e9c67d1e2e1c49e09e8934a45
5
5
  SHA512:
6
- metadata.gz: ae940be66b33102dce802addaa5f2d3abacde2f3e934383b8ab0a663329a5672c3ef84065d5020332de26cca3f43831a2e214c61ffecfcae661b34d2e2249d0c
7
- data.tar.gz: 31b3b158aa2d1978828874f1d30e7815e5e8b868e599beb3e93fb1c7f68335476386c03d198e3506ce967208f66dec2e985c4e03570fa3ff860fcc434928da01
6
+ metadata.gz: d33f864be73e4bfa056f17d1897e581e2f8456d93a307871e7173ee19c995f7d09c426af5c8d82b5e77e22d6ede15286aa952525ad46ed2ff444896944e5f825
7
+ data.tar.gz: 3868fec113b95cd2d64f0e371630c58e27854792dc5b98462d2d1fbe13c55c40cdfd56880464b65b150b39f97c2c74a753cbd0ba0de75c2737bda64274200d2a
Binary file
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Auto
4
4
  module L18n
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
7
7
  end
data/lib/auto/l18n.rb CHANGED
@@ -64,7 +64,7 @@ module Auto
64
64
  record = lambda do |str, type:, source:, original_position: nil|
65
65
  return if str.nil?
66
66
 
67
- # Normalize whitespace
67
+ # Normalize whitespace
68
68
  s = str.gsub(/\s+/, " ").strip
69
69
  return if s.empty?
70
70
  return if s.length < opts[:min_length]
@@ -85,14 +85,63 @@ module Auto
85
85
  # Only skip if multiple curly braces (likely interpolation)
86
86
  return if s.scan(/\{/).size > 1 && s.scan(/\}/).size > 1
87
87
 
88
- # Skip file paths
88
+
89
+ # - Common field names that often appear as technical tokens rather than UI text
90
+ return if s.downcase == 'email'
91
+ # Skip file paths
89
92
  return if s =~ %r{\A\.?/?[\w\-]+(/[\w\-\.]+)+\z}
93
+
94
+ # Heuristics to avoid common non-translatable strings
95
+ # - CSS class/id-like tokens (snake/kebab case, possibly space-separated list)
96
+ if (s.include?('_') || s.include?('-')) && s =~ /\A[a-z0-9 _\-]+\z/
97
+ # e.g., "btn btn-secondary", "group_show", "event-link"
98
+ return
99
+ end
100
+
101
+ # - CamelCase or lowerCamelCase identifiers (likely variable/ID names)
102
+ return if s =~ /\A(?:[a-z]+(?:[A-Z][a-z0-9]+)+|[A-Z][a-z0-9]+(?:[A-Z][a-z0-9]+)+)\z/
103
+
104
+ # - Hex colors like #fff or #4CAF50
105
+ return if s =~ /\A#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b/
106
+
107
+ # - File names with extensions (e.g., JA_Logo.png, Chart.bundle)
108
+ return if s =~ /\A[\w\-]+(\.[A-Za-z0-9]{2,6})+\z/
109
+
110
+ # - Date/time format strings (strftime-style like %Y-%m-%d, %I:%M %p)
111
+ return if s =~ /%[a-zA-Z]/
112
+
113
+ # - Mostly numeric values (amounts, percentages)
114
+ return if s =~ /\A\d+(?:[.,]\d+)?(?:%|[a-zA-Z]*)?\z/
115
+
116
+ # - Rails-style parameter names with bracket notation used in form helpers
117
+ # e.g., "webhook_event_call[event][]", "permission_ids[]"
118
+ return if s =~ /\A[a-zA-Z0-9_]+(?:\[[^\]]*\])+(?:\[\])?\z/
119
+
120
+ # - Common developer token seen as IDs/variables but not visible copy
121
+ return if s == 'breadcrumb'
122
+
123
+ # - MIME types (single or comma-separated), e.g., "image/*", "image/png,image/jpeg"
124
+ return if s =~ /\A(?:[a-zA-Z0-9.+-]+\/[a-zA-Z0-9+*.-]+)(?:\s*,\s*[a-zA-Z0-9.+-]+\/[a-zA-Z0-9+*.-]+)*\z/
125
+
126
+ # - CSS inline style declarations (property: value; ...)
127
+ # Detect at least one property:value; pair
128
+ return if s =~ /\b[a-zA-Z\-]+\s*:\s*[^;]+;/
90
129
 
91
130
  # Skip code-like syntax
92
- return if s =~ /[;=>]{2,}/ || s =~ /function\s*\(/ || s =~ /\b(?:var|const|let)\s+\w+/
131
+ return if s =~ /[;=>]{2,}/ || s =~ /function\s*\(/ || s =~ /\b(?:var|const|let)\s+\w+/
132
+ # Method-call chains typical of JS (e.g., this.form.submit(); or foo.bar())
133
+ return if s =~ /[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+\s*\([^)]*\)\s*;?/
93
134
 
94
135
  # Normalize quotes for comparison
95
136
  normalized = s.tr('""''', %q{"''"})
137
+
138
+ # Skip common non-visible tokens that appear as ERB strings (helper options, class toggles)
139
+ if type == :erb_string
140
+ # Single lowercase identifier, typical for CSS classes or symbols used in helpers
141
+ return if s =~ /\A[a-z][a-z0-9_\-]*\z/
142
+ # Known nav/resource tokens commonly used as classes or symbols
143
+ return if %w[active members events partners permissions groups].include?(s)
144
+ end
96
145
 
97
146
  # Try to estimate line number
98
147
  line = estimate_line(original_position, line_map) if original_position
@@ -118,9 +167,40 @@ module Auto
118
167
 
119
168
  # Skip if it's an I18n call
120
169
  next if code =~ /\b(?:I18n\.)?(?:t|translate)\s*\(/
170
+
171
+ # Skip asset/helper calls where strings are not visible user text
172
+ # e.g., stylesheet_link_tag "webhooks", media: "all", "data-turbo-track": "reload"
173
+ next if code =~ /\b(?:stylesheet_link_tag|javascript_include_tag|javascript_pack_tag|stylesheet_pack_tag|asset_path|image_tag|image_pack_tag|font_path|font_url|asset_url)\b/
174
+
175
+ # Skip render calls where string literals represent partial/template names, not visible text
176
+ # e.g., render 'form', render partial: 'form'
177
+ next if code =~ /\brender\b/
178
+
179
+ # Skip check_box_tag and radio_button_tag: string args are names/values, not visible copy
180
+ next if code =~ /\b(?:check_box_tag|radio_button_tag)\b/
181
+
182
+ # Helper-aware extraction: only consider visible-content helpers
183
+ # Allow: link_to, button_to, submit_tag, label_tag, content_tag, form builder label/submit
184
+ helper_in_code = (
185
+ code =~ /\b(?:link_to|button_to|submit_tag|label_tag|content_tag)\b/ ||
186
+ code =~ /\b\w+\.(?:label|submit)\b/
187
+ )
188
+
189
+ # If no known content-bearing helper is present, skip extracting strings from this ERB block
190
+ unless helper_in_code
191
+ next
192
+ end
121
193
 
122
194
  # Extract double-quoted strings
123
195
  code.scan(/"((?:[^"\\]|\\.)*)"/m).each do |string_match|
196
+ # Skip inline conditional class injections like: "calculated" if condition
197
+ # or other inline if/unless patterns composed solely of a literal
198
+ next if code.strip =~ /\A["'][a-z0-9 _\-]+["']\s+(?:if|unless)\b/i
199
+ # Skip option-hash values like: class: "...", id: "...", data: "...", style: "..."
200
+ literal = string_match[0]
201
+ escaped_lit = Regexp.escape(literal)
202
+ next if code =~ /\b[a-zA-Z_]\w*\s*:\s*"#{escaped_lit}"/
203
+ next if code =~ /"#{escaped_lit}"\s*=>/
124
204
  unescaped = string_match[0].gsub(/\\(.)/, '\1')
125
205
  record.call(
126
206
  unescaped,
@@ -132,6 +212,13 @@ module Auto
132
212
 
133
213
  # Extract single-quoted strings
134
214
  code.scan(/'((?:[^'\\]|\\.)*)'/).each do |string_match|
215
+ # Skip inline conditional class injections like: 'calculated' if condition
216
+ next if code.strip =~ /\A["'][a-z0-9 _\-]+["']\s+(?:if|unless)\b/i
217
+ # Skip option-hash values like: class: '...'
218
+ literal = string_match[0]
219
+ escaped_lit = Regexp.escape(literal)
220
+ next if code =~ /\b[a-zA-Z_]\w*\s*:\s*'#{escaped_lit}'/
221
+ next if code =~ /'#{escaped_lit}'\s*=>/
135
222
  unescaped = string_match[0].gsub(/\\(.)/, '\1')
136
223
  record.call(
137
224
  unescaped,
@@ -229,10 +316,30 @@ module Auto
229
316
  fragment.css(selector).each do |el|
230
317
  all_attrs.each do |attr|
231
318
  next unless el[attr]
232
-
233
- # Skip empty values or single characters for 'value' attribute
234
- next if attr == 'value' && el[attr].length < 2
235
-
319
+
320
+ # Skip title attributes (tooltips) per project convention to reduce false positives
321
+ next if attr == 'title'
322
+
323
+ # Special handling for the 'value' attribute:
324
+ # - Only treat as visible text for input types that display their value as a label
325
+ # (submit, button, reset). For all other cases (hidden, text, radio, checkbox,
326
+ # option values, etc.) the value is not a visible label and should be ignored.
327
+ if attr == 'value'
328
+ tag = el.name.to_s.downcase
329
+ if tag == 'input'
330
+ input_type = (el['type'] || '').downcase
331
+ visible_button_types = %w[submit button reset]
332
+ # If not a visible button-like input, skip capturing the value
333
+ next unless visible_button_types.include?(input_type)
334
+ else
335
+ # For non-input tags (e.g., option, button), don't treat value as visible label
336
+ next
337
+ end
338
+ end
339
+
340
+ # Skip empty values or single characters (after applying 'value' rules above)
341
+ next if el[attr].strip.length < 2
342
+
236
343
  position = find_position_in_original(el[attr], raw)
237
344
  record.call(
238
345
  el[attr],
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: auto-l18n
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Reiner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-27 00:00:00.000000000 Z
11
+ date: 2025-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -44,6 +44,7 @@ files:
44
44
  - LICENSE.txt
45
45
  - README.md
46
46
  - Rakefile
47
+ - auto-l18n-0.1.0.gem
47
48
  - exe/auto-l18n
48
49
  - lib/auto/l18n.rb
49
50
  - lib/auto/l18n/version.rb
@@ -56,7 +57,7 @@ metadata:
56
57
  source_code_uri: https://github.com/NicolasReiner/auto-l18n
57
58
  changelog_uri: https://github.com/NicolasReiner/auto-l18n/blob/master/CHANGELOG.md
58
59
  bug_tracker_uri: https://github.com/NicolasReiner/auto-l18n/issues
59
- rubygems_mfa_required: 'true'
60
+ rubygems_mfa_required: 'false'
60
61
  post_install_message:
61
62
  rdoc_options: []
62
63
  require_paths: