extract_i18n 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,7 @@ module ExtractI18n::Adapters
7
7
  when /\.rb$/ then RubyAdapter
8
8
  when /\.erb$/ then ErbAdapter
9
9
  when /\.slim$/ then SlimAdapter
10
+ when /\.ts$/, /\.js$/, /\.mjs/, /\.jsx$/ then JsAdapter
10
11
  when /\.vue$/
11
12
  if File.read(file_path)[/lang=.pug./]
12
13
  VuePugAdapter
@@ -0,0 +1,79 @@
1
+ require 'open3'
2
+ require 'json'
3
+
4
+ module ExtractI18n::Adapters
5
+ class JsAdapter < Adapter
6
+ IGNORE_AFTER = /(key: *|import .*|require\(.*)/
7
+
8
+ def run(original_context)
9
+ path = '../../../js/find_string_tokens.js'
10
+ abs_path = File.expand_path(path, __dir__)
11
+ stdout, stderr, status = Open3.capture3('node', abs_path, stdin_data: original_context)
12
+ unless status.success?
13
+ warn stderr
14
+ warn "Failed to run js/find_string_tokens.js: #{status}"
15
+ return original_context
16
+ end
17
+ @current_buffer = original_context.dup
18
+ @original_context = original_context.dup.freeze
19
+
20
+ @t_template = %[t('%s'%s)]
21
+ if vue2?(original_context)
22
+ @t_template = %[this.$t('%s'%s)]
23
+ end
24
+
25
+ @byte_offset = 0
26
+
27
+ results = ::JSON.parse(stdout, symbolize_names: true)
28
+ results.each do |item|
29
+ if item[:type] == 'literal'
30
+ replace_string(item)
31
+ else
32
+ warn "Template String literals not implemented yet: #{JSON.pretty_generate(item)}"
33
+ end
34
+ end
35
+ @current_buffer
36
+ end
37
+
38
+ def replace_string(item)
39
+ content = item[:value]
40
+ start = [item.dig(:loc, :start, :line) - 1, item.dig(:loc, :start, :column)]
41
+ finish = [item.dig(:loc, :end, :line) - 1, item.dig(:loc, :end, :column)]
42
+
43
+ source_line = @original_context.lines[start[0]]
44
+ change = ExtractI18n::SourceChange.new(
45
+ i18n_key: "#{@file_key}.#{ExtractI18n.key(content)}",
46
+ i18n_string: content,
47
+ source_line: source_line,
48
+ remove: item[:raw],
49
+ t_template: @t_template,
50
+ interpolation_type: :vue,
51
+ interpolate_arguments: {},
52
+ )
53
+
54
+ if ExtractI18n.ignore?(change.i18n_string)
55
+ return
56
+ end
57
+ token = Regexp.escape(item[:raw])
58
+ regexp = Regexp.new(IGNORE_AFTER.to_s + token)
59
+ return if source_line.match?(regexp)
60
+ return if change.i18n_string[file_key]
61
+
62
+ byte_from = @original_context.lines[0...start[0]].join.length + start[1]
63
+ byte_to = @original_context.lines[0...finish[0]].join.length + finish[1]
64
+
65
+ if @on_ask.call(change)
66
+ byte_from += @byte_offset
67
+ byte_to += @byte_offset
68
+
69
+ @current_buffer = @current_buffer[0...byte_from] + change.i18n_t + @current_buffer[byte_to..]
70
+
71
+ @byte_offset += change.i18n_t.length - item[:raw].length
72
+ end
73
+ end
74
+
75
+ def vue2?(content)
76
+ content['data()'] || content['computed:'] || content['this.$i18n'] || content['this.$t'] || content['this.$store']
77
+ end
78
+ end
79
+ end
@@ -37,7 +37,9 @@ module ExtractI18n::Adapters
37
37
  all_match
38
38
  end
39
39
  end
40
- out
40
+ @new_content = out.split("\n")
41
+ check_javascript_for_strings!
42
+ @new_content.join("\n")
41
43
  end
42
44
 
43
45
  # nokogir::html can parse @click.prevent stuff, but transforms all CamelCase Attributes to kebab-case by default
@@ -134,5 +136,20 @@ module ExtractI18n::Adapters
134
136
  end
135
137
  [args, translation]
136
138
  end
139
+
140
+ def check_javascript_for_strings!
141
+ lines = @new_content
142
+ # drop lines until <script
143
+ start = lines.find_index { |line| line[/^<script/] }
144
+ finish = lines.find_index { |line| line[/^<\/script/] }
145
+ return if start.nil? || finish.nil?
146
+
147
+ js_content = lines[(start + 1)...finish].join("\n")
148
+
149
+ js_adapter = ExtractI18n::Adapters::JsAdapter.new(file_key: @file_key, on_ask: @on_ask, options: @options)
150
+ js_replaced = js_adapter.run(js_content)
151
+
152
+ @new_content = lines[0..start] + js_replaced.split("\n") + lines[(finish)..-1]
153
+ end
137
154
  end
138
155
  end
@@ -2,6 +2,27 @@
2
2
 
3
3
  module ExtractI18n::Adapters
4
4
  class VuePugAdapter < SlimAdapter
5
+ def run(original_content)
6
+ super
7
+ check_javascript_for_strings!
8
+ @new_content.join("\n")
9
+ end
10
+
11
+ def check_javascript_for_strings!
12
+ lines = @new_content
13
+ # drop lines until <script
14
+ start = lines.find_index { |line| line[/^<script/] }
15
+ finish = lines.find_index { |line| line[/^<\/script/] }
16
+ return if start.nil? || finish.nil?
17
+
18
+ js_content = lines[(start + 1)...finish].join("\n")
19
+
20
+ js_adapter = ExtractI18n::Adapters::JsAdapter.new(file_key: @file_key, on_ask: @on_ask, options: @options)
21
+ js_replaced = js_adapter.run(js_content)
22
+
23
+ @new_content = lines[0..start] + js_replaced.split("\n") + lines[(finish)..-1]
24
+ end
25
+
5
26
  def process_line(old_line)
6
27
  @mode ||= :template
7
28
  if old_line[/^<template/]
@@ -19,6 +40,9 @@ module ExtractI18n::Adapters
19
40
  if change.nil? # nothing to do
20
41
  return old_line
21
42
  end
43
+ if ExtractI18n.ignore?(change.i18n_string) || change.i18n_string.empty?
44
+ return old_line
45
+ end
22
46
 
23
47
  if @on_ask.call(change)
24
48
  change.i18n_t
@@ -1,3 +1,3 @@
1
1
  module ExtractI18n
2
- VERSION = "0.6.2"
2
+ VERSION = "0.7.0"
3
3
  end
data/lib/extract_i18n.rb CHANGED
@@ -22,10 +22,19 @@ module ExtractI18n
22
22
  self.ignorelist = [
23
23
  '_',
24
24
  '::',
25
- %r{^/}
25
+ 'v-else',
26
+ 'v-else-if',
27
+ '&nbsp;',
28
+ %r{^#[^ ]+$},
29
+ %r{^/},
30
+ %r{^(mdi|fa|fas|far|icon)-},
26
31
  ]
27
32
  self.html_fields_with_plaintext = %w[title placeholder alt label aria-label modal-title]
28
33
 
34
+ def self.ignore?(string)
35
+ ExtractI18n.ignorelist.any? { |item| string.to_s[item] }
36
+ end
37
+
29
38
  def self.key(string, length: 25)
30
39
  string.strip.
31
40
  unicode_normalize(:nfkd).gsub(/(\p{Letter})\p{Mark}+/, '\\1').
@@ -35,9 +44,10 @@ module ExtractI18n
35
44
 
36
45
  def self.file_key(path)
37
46
  path.gsub(strip_path, '').
38
- gsub(%r{^/|/$}, '').
39
- gsub(/\.(vue|rb|html\.slim|\.slim)$/, '').
40
- gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').
47
+ gsub(%r{^/|/$}, ''). # remove leading and trailing slashes
48
+ gsub(/\.[a-z]+$/, ''). # remove file extension
49
+ gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'). # convert camelcase to underscore
50
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
41
51
  gsub('/_', '.').
42
52
  gsub('/', '.').
43
53
  tr("-", "_").downcase
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extract_i18n
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Wienert
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-29 00:00:00.000000000 Z
11
+ date: 2024-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -121,6 +121,7 @@ files:
121
121
  - ".gitignore"
122
122
  - ".rspec"
123
123
  - ".rubocop.yml"
124
+ - CONTRIBUTING.md
124
125
  - Gemfile
125
126
  - LICENSE.txt
126
127
  - README.md
@@ -129,9 +130,13 @@ files:
129
130
  - bin/setup
130
131
  - exe/extract-i18n
131
132
  - extract_i18n.gemspec
133
+ - js/.gitignore
134
+ - js/.tool-versions
135
+ - js/find_string_tokens.js
132
136
  - lib/extract_i18n.rb
133
137
  - lib/extract_i18n/adapters/adapter.rb
134
138
  - lib/extract_i18n/adapters/erb_adapter.rb
139
+ - lib/extract_i18n/adapters/js_adapter.rb
135
140
  - lib/extract_i18n/adapters/ruby_adapter.rb
136
141
  - lib/extract_i18n/adapters/slim_adapter.rb
137
142
  - lib/extract_i18n/adapters/slim_adapter_wip.rb
@@ -157,8 +162,9 @@ files:
157
162
  homepage: https://github.com/pludoni/extract_i18n
158
163
  licenses:
159
164
  - MIT
160
- metadata: {}
161
- post_install_message:
165
+ metadata:
166
+ rubygems_mfa_required: 'true'
167
+ post_install_message:
162
168
  rdoc_options: []
163
169
  require_paths:
164
170
  - lib
@@ -166,15 +172,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
166
172
  requirements:
167
173
  - - ">="
168
174
  - !ruby/object:Gem::Version
169
- version: '0'
175
+ version: 2.7.0
170
176
  required_rubygems_version: !ruby/object:Gem::Requirement
171
177
  requirements:
172
178
  - - ">="
173
179
  - !ruby/object:Gem::Version
174
180
  version: '0'
175
181
  requirements: []
176
- rubygems_version: 3.2.33
177
- signing_key:
182
+ rubygems_version: 3.5.11
183
+ signing_key:
178
184
  specification_version: 4
179
185
  summary: Extact i18n from Ruby files using Ruby parser and slim files using regex
180
186
  test_files: []