extract_i18n 0.6.2 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +14 -0
- data/README.md +12 -0
- data/extract_i18n.gemspec +6 -3
- data/js/.gitignore +1 -0
- data/js/.tool-versions +1 -0
- data/js/find_string_tokens.js +106 -0
- data/lib/extract_i18n/adapters/adapter.rb +1 -0
- data/lib/extract_i18n/adapters/js_adapter.rb +79 -0
- data/lib/extract_i18n/adapters/vue_adapter.rb +18 -1
- data/lib/extract_i18n/adapters/vue_pug_adapter.rb +24 -0
- data/lib/extract_i18n/version.rb +1 -1
- data/lib/extract_i18n.rb +14 -4
- metadata +14 -8
@@ -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
|
data/lib/extract_i18n/version.rb
CHANGED
data/lib/extract_i18n.rb
CHANGED
@@ -22,10 +22,19 @@ module ExtractI18n
|
|
22
22
|
self.ignorelist = [
|
23
23
|
'_',
|
24
24
|
'::',
|
25
|
-
|
25
|
+
'v-else',
|
26
|
+
'v-else-if',
|
27
|
+
' ',
|
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(/\.
|
40
|
-
gsub(/([A-Z\d]+)([A-Z][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.
|
4
|
+
version: 0.7.1
|
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-
|
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
|
-
|
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:
|
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.
|
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: []
|