extract_i18n 0.3.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d5459b5c55363dfd1893ecafd4d3eb0bea7db570a54da421782a36750dfc50c
4
- data.tar.gz: a9a335109c2ad690a9234f95e7bfe5a24fff8f5a5eb7d642031d3a402773a223
3
+ metadata.gz: 1d6886c7a13cbbe6698326a72004018aaea5f57e753a9fab278f4025181bdd01
4
+ data.tar.gz: 10172e9a05996706d92f314291137c13bb8c957a0a974e9d9852cd295cb45ceb
5
5
  SHA512:
6
- metadata.gz: 756c430da5da81bf5e1a1211c43b1039cd17863e46f64b239d5eaf42c57fe45bc98dfc5f63a4974310a7ed77fa26efb1441d5aa2f036df655ea836589626d598
7
- data.tar.gz: 98b61188778616f1cb643310e3dfd1d76f7cfc6e4b7d5b4b41b10296d26aa438034639133d4cedc5c43f2faacae69bed46fa94e33692519bbd5c22f720c06463
6
+ metadata.gz: dcc33ec2bfab6ec9970508beebb22f57266679c20df3d97ee1eeaea2650c97f052a037ee4ce46de109bc09984b96df7df6a3097edfcb66772e6156c330a466e6
7
+ data.tar.gz: 7d75f557e0a6e1074e504931fa4ea6985153589dd0307fdd08d51faf3d6173ae6a29a0c15c146fee2bbc490d4a201628c067ba5de12257d7999e6a1b13c0bc86
@@ -22,8 +22,8 @@ jobs:
22
22
  run: bundle exec rspec
23
23
  - name: Build and publish gem
24
24
  uses: jstastny/publish-gem-to-github@master
25
+ continue-on-error: true
25
26
  with:
26
- continue-on-error: true
27
27
  token: ${{ secrets.GITHUB_TOKEN }}
28
28
  owner: pludoni
29
29
 
data/README.md CHANGED
@@ -12,15 +12,17 @@ This Gem **supports** the following source files:
12
12
 
13
13
  - Ruby files (controllers, models etc.) via Ruby-Parser, e.g. walking all Ruby Strings
14
14
  - Slim Views (via Regexp parser by [SlimKeyfy](https://github.com/phrase/slimkeyfy) (MIT License))
15
+ - Vue templates
16
+ - will scan all texts and common string-attributes such as title, alt etc. for static strings and replace with vue-i18n's $t
17
+ - Caveats: because of limitations of the HTML/XML parser it will slightly transform the HTML, for example, self closing tags are expanded (e.g. ``<Component />`` will become ``<Component></Component>``). Also multi-line arrangements of attributes, tags etc. might produce unexpected results, so make sure to use Git and diff the result.
15
18
  - Vue Pug views
16
- - Pug is very similar to slim and thus relatively good extractable via Regexp.
19
+ - Pug is very similar to slim and thus relatively well extractable via Regexp.
17
20
  - ERB views
18
21
  - by vendoring/extending https://github.com/ProGM/i18n-html_extractor (MIT License)
19
22
 
20
23
  CURRENTLY THERE IS **NO SUPPORT** FOR:
21
24
 
22
25
  - haml ( integrating https://github.com/shaiguitar/haml-i18n-extractor)
23
- - vue html templates ([Check out my vue pug converting script](https://gist.github.com/zealot128/6c41df1d33a810856a557971a04989f6))
24
26
 
25
27
  But I am open to integrating PRs for those!
26
28
 
@@ -6,7 +6,12 @@ module ExtractI18n::Adapters
6
6
  case file_path
7
7
  when /\.rb$/ then RubyAdapter
8
8
  when /\.slim$/ then SlimAdapter
9
- when /\.vue$/ then VueAdapter
9
+ when /\.vue$/
10
+ if File.read(file_path)[/lang=.pug./]
11
+ VuePugAdapter
12
+ else
13
+ VueAdapter
14
+ end
10
15
  end
11
16
  end
12
17
 
@@ -1,31 +1,138 @@
1
- # frozen_string_literal: true
2
-
1
+ require 'nokogiri'
3
2
  module ExtractI18n::Adapters
4
- class VueAdapter < SlimAdapter
5
- def process_line(old_line)
6
- @mode ||= :template
7
- if old_line[/^<template/]
8
- @mode = :template
9
- elsif old_line[/^<script/]
10
- @mode = :script
11
- elsif old_line[/^<style/]
12
- @mode = :style
13
- end
14
- if @mode != :template
15
- return old_line
16
- end
17
- word = ExtractI18n::Slimkeyfy::Word.for('.vue').new(old_line)
18
- ExtractI18n::Slimkeyfy::VueTransformer.new(word, @file_key).transform do |change|
19
- if change.nil? # nothing to do
20
- return old_line
3
+ class VueAdapter < Adapter
4
+ ATTRIBUTES_WITH_TEXT = %w[
5
+ title
6
+ alt
7
+ placeholder
8
+ label
9
+ description
10
+ ]
11
+ def run(original_content)
12
+ content2, replace_pairs = replace_camelcase_attributes(original_content)
13
+
14
+ doc = Nokogiri::HTML5.fragment(content2)
15
+ template = doc.at('template')
16
+ return original_content unless template
17
+ return original_content if template['lang']
18
+
19
+ nodes = template.xpath(".//text() | text()").select { |i| i.to_s.strip.length > 0 }
20
+
21
+ nodes.each do |node|
22
+ process_change(node)
23
+ end
24
+
25
+ ATTRIBUTES_WITH_TEXT.each do |attr|
26
+ template.search("*[#{attr}]").each do |node|
27
+ process_attribute_change(node, attr)
28
+ end
29
+ end
30
+
31
+ out = unreplace_camelcase_attributes(doc.to_s, replace_pairs)
32
+ out.gsub!(/ (\w+)=""/) do |all_match|
33
+ m = $1
34
+ if original_content[/ #{m}[^=]/]
35
+ " #{m}"
36
+ else
37
+ all_match
21
38
  end
39
+ end
40
+ out
41
+ end
22
42
 
23
- if @on_ask.call(change)
24
- change.i18n_t
43
+ # nokogir::html can parse @click.prevent stuff, but transforms all CamelCase Attributes to kebab-case by default
44
+ # To keep the original case, we replace it with a placeholder and unreplace it back
45
+ def replace_camelcase_attributes(text)
46
+ cc = 1
47
+ pairs = []
48
+ replaced = text.gsub(/ ([a-zA-Z:]+=)/) { |pp|
49
+ match = Regexp.last_match
50
+ cc += 1
51
+ # kebab-case
52
+ if pp[/[A-Z]/]
53
+ kebab = match[1].gsub(/([A-Z])/) { |i| "-#{i.downcase}" }
54
+ hack = " :camelcase-nokogiri-hack-#{cc}-#{kebab}"
55
+ pairs << [hack, " " + match[1]]
56
+ hack
25
57
  else
26
- old_line
58
+ pp
27
59
  end
60
+ }
61
+ # tags
62
+ replaced.scan(/<([a-zA-Z0-9]+)/).flatten.each do |pp|
63
+ next pp unless pp[/[A-Z]/]
64
+
65
+ cc += 1
66
+
67
+ kebab = pp.gsub(/([A-Z])/) { |i| "-#{i.downcase}" }.sub(/^-/, '')
68
+ hack = "camelcase-nokogiri-hack-#{cc}-#{kebab}"
69
+ replaced.gsub!(/<#{pp}/, "<#{hack}")
70
+ replaced.gsub!(/<\/ *#{pp}/, "</#{hack}")
71
+ pairs << ["<#{hack}", "<#{pp}"]
72
+ pairs << ["</#{hack}", "</#{pp}"]
73
+ end
74
+ [replaced, pairs]
75
+ end
76
+
77
+ def unreplace_camelcase_attributes(text, pairs)
78
+ txt = text.dup
79
+ pairs.each do |hack, camel|
80
+ txt.gsub!(hack, camel)
81
+ end
82
+ txt
83
+ end
84
+
85
+ def process_attribute_change(node, attr)
86
+ _, ws_before, content, ws_after = node[attr].to_s.match(/(\s*)(.*?)(\s*)$/m).to_a
87
+
88
+ interpolate_arguments, content = extract_arguments(content)
89
+
90
+ change = ExtractI18n::SourceChange.new(
91
+ i18n_key: "#{@file_key}.#{ExtractI18n.key(content)}",
92
+ i18n_string: content,
93
+ source_line: node.to_s,
94
+ remove: content,
95
+ t_template: %[$t('%s'%s)],
96
+ interpolation_type: :vue,
97
+ interpolate_arguments: interpolate_arguments,
98
+ )
99
+ if @on_ask.call(change)
100
+ node.remove_attribute "title"
101
+ node[":" + attr] = "#{ws_before}#{change.i18n_t}#{ws_after}"
102
+ end
103
+ end
104
+
105
+ def process_change(node)
106
+ _, ws_before, content, ws_after = node.to_s.match(/(\s*)(.*?)(\s*)$/m).to_a
107
+
108
+ return if content.include?("{{")
109
+
110
+ interpolate_arguments, content = extract_arguments(content)
111
+
112
+ change = ExtractI18n::SourceChange.new(
113
+ i18n_key: "#{@file_key}.#{ExtractI18n.key(content)}",
114
+ i18n_string: content,
115
+ source_line: node.parent.to_s,
116
+ remove: content,
117
+ t_template: %[{{ $t('%s'%s) }}],
118
+ interpolation_type: :vue,
119
+ interpolate_arguments: interpolate_arguments,
120
+ )
121
+ if @on_ask.call(change)
122
+ node.replace("#{ws_before}#{change.i18n_t}#{ws_after}")
123
+ end
124
+ end
125
+
126
+ def extract_arguments(translation)
127
+ args = {}
128
+ translation.scan(/\{\{([^}]*)\}\}/).each_with_index do |stripped_arg, index|
129
+ arg = Regexp.last_match[0]
130
+ key = ExtractI18n.key(arg)
131
+ key = key + index.to_s if index > 0
132
+ translation = translation.gsub(arg, "{#{key}}")
133
+ args[key] = stripped_arg[0]
28
134
  end
135
+ [args, translation]
29
136
  end
30
137
  end
31
138
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtractI18n::Adapters
4
+ class VuePugAdapter < SlimAdapter
5
+ def process_line(old_line)
6
+ @mode ||= :template
7
+ if old_line[/^<template/]
8
+ @mode = :template
9
+ elsif old_line[/^<script/]
10
+ @mode = :script
11
+ elsif old_line[/^<style/]
12
+ @mode = :style
13
+ end
14
+ if @mode != :template
15
+ return old_line
16
+ end
17
+ word = ExtractI18n::Slimkeyfy::Word.for('.vue').new(old_line)
18
+ ExtractI18n::Slimkeyfy::VueTransformer.new(word, @file_key).transform do |change|
19
+ if change.nil? # nothing to do
20
+ return old_line
21
+ end
22
+
23
+ if @on_ask.call(change)
24
+ change.i18n_t
25
+ else
26
+ old_line
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -27,7 +27,7 @@ module ExtractI18n
27
27
  end
28
28
 
29
29
  opts.on('-nNAMESPACE', '--namespace=NAMESPACE', 'Locale base key to wrap locations in') do |f|
30
- @options[:base_key] = f
30
+ @options[:namespace] = f
31
31
  end
32
32
 
33
33
  opts.on('-r', '--slim-relative', 'When activated, will use relative keys like t(".title")') do |f|
@@ -23,8 +23,8 @@ module ExtractI18n
23
23
  append: /(?<html_tag>append:\s*)/,
24
24
  label: /(?<html_tag>[a-z]*_?label:\s*)/,
25
25
  optionals: /(?<html_tag>(default|include_blank|alt):\s*)/,
26
- input: /(?<html_tag>[a-z]*\.?input:?\s*)/,
27
- button: /(?<html_tag>[a-z]*\.?button:?\s*(\:[a-z]+\s*,\s*)?)/,
26
+ input: /(?<html_tag>[a-z]*\.?input:\s*)/,
27
+ button: /(?<html_tag>[a-z]*\.?button:\s*(\:[a-z]+\s*,\s*)?)/,
28
28
  tag: /(?<html_tag>(submit|content)_tag[\:\(]?\s*)/,
29
29
  data_naive: /(?<html_tag>data:\s*\{\s*(confirm|content):\s*)/
30
30
  }.freeze
@@ -47,8 +47,12 @@ module ExtractI18n
47
47
  unless @source_line.include?("\n")
48
48
  s += "\n"
49
49
  end
50
- s += PASTEL.cyan("with: ") + PASTEL.blue(@source_line).
51
- gsub(@remove, PASTEL.green(i18n_t))
50
+ if @source_line[@remove]
51
+ s += PASTEL.cyan("with: ") + PASTEL.blue(@source_line).
52
+ gsub(@remove, PASTEL.green(i18n_t))
53
+ else
54
+ s += PASTEL.cyan("with: ") + PASTEL.green(i18n_t)
55
+ end
52
56
  unless @source_line.include?("\n")
53
57
  s += "\n"
54
58
  end
@@ -66,7 +70,7 @@ module ExtractI18n
66
70
  end
67
71
 
68
72
  def increment_key!
69
- if @key[-1][/(.*)([0-9]+)$/]
73
+ if @key[/(.+)([0-9]+)$/]
70
74
  rest = $1
71
75
  number = $2.to_i
72
76
  else
@@ -1,3 +1,3 @@
1
1
  module ExtractI18n
2
- VERSION = "0.3.0"
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/extract_i18n.rb CHANGED
@@ -21,7 +21,8 @@ module ExtractI18n
21
21
  self.ignore_functions = %w[where order group select sql]
22
22
  self.ignorelist = [
23
23
  '_',
24
- '::'
24
+ '::',
25
+ %r{^/}
25
26
  ]
26
27
  self.html_fields_with_plaintext = %w[title placeholder alt label aria-label modal-title]
27
28
 
@@ -37,6 +38,7 @@ module ExtractI18n
37
38
  gsub(%r{^/|/$}, '').
38
39
  gsub(/\.(vue|rb|html\.slim|\.slim)$/, '').
39
40
  gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').
41
+ gsub('/_', '.').
40
42
  gsub('/', '.').
41
43
  tr("-", "_").downcase
42
44
  end
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.3.0
4
+ version: 0.6.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: 2020-10-15 00:00:00.000000000 Z
11
+ date: 2022-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -136,6 +136,7 @@ files:
136
136
  - lib/extract_i18n/adapters/slim_adapter.rb
137
137
  - lib/extract_i18n/adapters/slim_adapter_wip.rb
138
138
  - lib/extract_i18n/adapters/vue_adapter.rb
139
+ - lib/extract_i18n/adapters/vue_pug_adapter.rb
139
140
  - lib/extract_i18n/cli.rb
140
141
  - lib/extract_i18n/file_processor.rb
141
142
  - lib/extract_i18n/html_extractor/erb_document.rb
@@ -157,7 +158,7 @@ homepage: https://github.com/pludoni/extract_i18n
157
158
  licenses:
158
159
  - MIT
159
160
  metadata: {}
160
- post_install_message:
161
+ post_install_message:
161
162
  rdoc_options: []
162
163
  require_paths:
163
164
  - lib
@@ -172,9 +173,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
173
  - !ruby/object:Gem::Version
173
174
  version: '0'
174
175
  requirements: []
175
- rubyforge_project:
176
- rubygems_version: 2.7.6
177
- signing_key:
176
+ rubygems_version: 3.1.6
177
+ signing_key:
178
178
  specification_version: 4
179
179
  summary: Extact i18n from Ruby files using Ruby parser and slim files using regex
180
180
  test_files: []