extract_i18n 0.6.2 → 0.7.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/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: []
|