jekyll-chatgpt-translate 0.0.30 → 0.0.32

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: 5ac852a5c56511f0e7500c8d907058b3b90f94a976446603fe852ba57132253e
4
- data.tar.gz: da8e37f819dcbbf5d907bf343d8dee167b36aedeef3863f68ca18bacf0783f11
3
+ metadata.gz: a186798ad4c85bb986a0ad9aa25a8b3c659af36f4ee351ab4b8c0a6ddc407f90
4
+ data.tar.gz: b7fac01e0df9bc04f2d977f729b6bfc6aa28997775346610c76e3b2df0736c71
5
5
  SHA512:
6
- metadata.gz: 43e2cf29a74222c71c645323fbce0fe9ab5f2a27198c558bc0aa3bbab8620c48d794beb97f7e6a7e5898daf47227ab2ed0ea31b4bf92c098334a082d9c6d2f1f
7
- data.tar.gz: e14939086f28a2a1d865e3332e738f274f2ea18d673599509b5288558ad3856a5e1f67f9787ad4ac57d388854519adf9e125b2583bb5017e55304a2334480324
6
+ metadata.gz: 135850d3b8d6a9bbeabde741e5cb1c183ab4ead16a5caeb6612e99c0db7f4d9dd310d889b9144b6985af9a003a99f184261ab72eeaffba0aff1dec31e54a45ba
7
+ data.tar.gz: fad830cf25f4bf56ad35216da0132c4911c08ad85de7dd5a25e79f583e3a06781eccc5529588f5d511e800f300cd0d01520559d7758723558118561e3b4142c9
data/.gitignore CHANGED
@@ -3,4 +3,4 @@ Gemfile.lock
3
3
  .bundle/
4
4
  .DS_Store
5
5
  coverage/
6
- _chatgpt-translated/
6
+ _chatgpt-translate/
data/.rubocop.yml CHANGED
@@ -22,7 +22,7 @@ Metrics/BlockLength:
22
22
  Metrics/CyclomaticComplexity:
23
23
  Max: 30
24
24
  Metrics/PerceivedComplexity:
25
- Max: 30
25
+ Max: 40
26
26
  Layout/EmptyLineAfterGuardClause:
27
27
  Enabled: false
28
28
  Naming/FileName:
data/Gemfile CHANGED
@@ -29,7 +29,7 @@ gem 'cucumber', '8.0.0', require: false
29
29
  gem 'kramdown-parser-gfm', '1.1.0', require: false
30
30
  gem 'minitest', '5.19.0', require: false
31
31
  gem 'rake', '13.0.6', require: false
32
- gem 'rubocop', '1.56.0', require: false
32
+ gem 'rubocop', '1.56.2', require: false
33
33
  gem 'rubocop-rspec', '2.23.2', require: false
34
34
  gem 'simplecov', '0.22.0', require: false
35
35
  gem 'webmock', '3.18.1', require: false
data/README.md CHANGED
@@ -42,13 +42,13 @@ OpenAI API KEY must be set in `OPENAI_API_KEY` environment variable, otherwise
42
42
  the plugin will not do any translation and won't generate translated pages.
43
43
  You can get your key [here](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key).
44
44
 
45
- Inside the original page you can use `{{ page.translated-XX-url }}` in order to render the URL
45
+ Inside the original page you can use `{{ page.chatgpt-translate.urls[XX] }}` in order to render the URL
46
46
  of the translated page, where `XX` is the [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
47
47
  code of the target language.
48
- Inside the translated page you can use `{{ page.translated-original-url }}` in order
48
+ Inside the translated page you can use `{{ page.chatgpt-translate.original-url }}` in order
49
49
  to get the URL of the page that was translated.
50
50
 
51
- You can also use `{{ page.chatgpt-model }}`
51
+ You can also use `{{ page.chatgpt-translate.model }}`
52
52
  inside both the original page and the translated one, to refer to the model of ChatGPT.
53
53
  The presence of this attribute in the `{{ page }}` means that the
54
54
  page was translated or the translated HTML was downloaded and placed into the `_site` directory.
@@ -80,7 +80,8 @@ Full list of options available to specify in `_config.yml`:
80
80
  number big enough, to avoid silly translations. The default is 128.
81
81
 
82
82
  * `layout` (optional) — is name of the file in `_layouts` directory, without the extension.
83
- This layout will be specified for the pages generated by this plugin.
83
+ This layout will be specified for the pages generated by this plugin.
84
+ The default value is `translated` (expecting you to have `_layouts/translated.html` file available).
84
85
 
85
86
  * `targets` (mandatory) — an array of target languages, each of which has the following attributes
86
87
 
@@ -100,6 +101,9 @@ This layout will be specified for the pages generated by this plugin.
100
101
  when the `version` is changed on another hand. By default, the version of
101
102
  this plugin will be used, unless you set your own value.
102
103
 
104
+ * `tmpdir` (optional) — the name of the directory where to keep temporary files,
105
+ `_chatgpt-translate` is the default value.
106
+
103
107
  ## How to Contribute
104
108
 
105
109
  Make a fork and then test it locally like this:
data/features/cli.feature CHANGED
@@ -22,19 +22,19 @@ Feature: Simple site building
22
22
  """
23
23
  And I have a "_layouts/default.html" file with content:
24
24
  """
25
- The Chinese: {{ page.translated-zh-url }}
26
- The French: {{ page.translated-fr-url }}
25
+ The Chinese: {{ page.chatgpt-translate.urls['zh'] }}
26
+ The French: {{ page.chatgpt-translate.urls['fr'] }}
27
27
  {{ content }}
28
28
  """
29
29
  And I have a "_layouts/chinese-translated.html" file with content:
30
30
  """
31
31
  Chinese: {{ content }}
32
- The original: {{ page.translated-original-url }}
32
+ The original: {{ page.chatgpt-translate.original-url }}
33
33
  """
34
34
  And I have a "_layouts/translated.html" file with content:
35
35
  """
36
36
  French: {{ content }}
37
- The original: {{ page.translated-original-url }}
37
+ The original: {{ page.chatgpt-translate.original-url }}
38
38
  """
39
39
  And I have a "_posts/2023-01-01-hello.md" file with content:
40
40
  """
@@ -46,9 +46,9 @@ Feature: Simple site building
46
46
  """
47
47
  Then I build Jekyll site
48
48
  And Exit code is zero
49
- And File "_chatgpt-translated/zh/2023-01-01-hello-zh.md" exists
50
- And File "_chatgpt-translated/zh/2023-01-01-hello-zh.md" contains "/2023-01-01-hello-chinese.html"
51
- And File "_chatgpt-translated/zh/2023-01-01-hello-zh.md" contains "translated-language: \"zh\""
49
+ And File "_chatgpt-translate/zh/2023-01-01-hello-zh.md" exists
50
+ And File "_chatgpt-translate/zh/2023-01-01-hello-zh.md" contains "/2023-01-01-hello-chinese.html"
51
+ And File "_chatgpt-translate/zh/2023-01-01-hello-zh.md" contains "language: \"zh\""
52
52
  And File "_site/2023/01/01/hello.html" exists
53
53
  And File "_site/2023/01/01/hello.html" contains "The Chinese: /2023-01-01-hello-chinese.html"
54
54
  And File "_site/2023-01-01-hello-chinese.html" exists
@@ -69,7 +69,7 @@ Feature: Simple site building
69
69
  layout: should-not-be-used
70
70
  targets:
71
71
  -
72
- language: en
72
+ language: ru
73
73
  permalink: about-me.html
74
74
  """
75
75
  And I have a "_posts/2023-01-01-hello.md" file with content:
@@ -77,12 +77,13 @@ Feature: Simple site building
77
77
  ---
78
78
  title: foo
79
79
  ---
80
- foo
80
+ see translated page: {{ page.chatgpt-translate.urls['ru'] }}
81
81
  """
82
82
  Then I build Jekyll site
83
83
  And Exit code is zero
84
- And Stdout contains "No need to translate, the page exists"
84
+ And Stdout contains "Re-translation not required, since version is empty"
85
85
  And File "_site/2023/01/01/hello.html" exists
86
+ And File "_site/2023/01/01/hello.html" contains "see translated page: /about-me.html"
86
87
  And File "_site/about-me.html" exists
87
88
  And File "_site/about-me.html" contains "Yegor Bugayenko"
88
89
 
@@ -125,3 +126,83 @@ Feature: Simple site building
125
126
  And File "_site/about-me.html" exists
126
127
  And File "_site/about-me.html" contains "foo-file-foo"
127
128
  And File "_site/boom.html" exists
129
+
130
+ Scenario: Simple translation with links to other pages
131
+ Given I have a "_config.yml" file with content:
132
+ """
133
+ url: https://www.yegor256.com
134
+ markdown: kramdown
135
+ plugins:
136
+ - jekyll-chatgpt-translate
137
+ chatgpt-translate:
138
+ source: en
139
+ api_key: "it-is-not-used, because EN to EN translation"
140
+ layout: default
141
+ targets:
142
+ -
143
+ language: en
144
+ permalink: :slug.html
145
+ """
146
+ And I have a "_layouts/default.html" file with content:
147
+ """
148
+ {{ content }}
149
+ """
150
+ And I have a "_posts/2023-01-01-hello.md" file with content:
151
+ """
152
+ ---
153
+ title: foo
154
+ ---
155
+ See {% post_url 2023-02-02-bye %}
156
+ """
157
+ And I have a "_posts/2023-02-02-bye.md" file with content:
158
+ """
159
+ ---
160
+ title: foo
161
+ ---
162
+ See {% post_url 2023-01-01-hello %}
163
+ """
164
+ Then I build Jekyll site
165
+ And Exit code is zero
166
+ And Stdout contains "The page is absent, need to translate"
167
+ And File "_site/2023/01/01/hello.html" exists
168
+ And File "_site/2023/01/01/hello.html" contains "/bye.html"
169
+ And File "_site/2023/02/02/bye.html" exists
170
+ And File "_site/2023/02/02/bye.html" contains "/hello.html"
171
+
172
+ Scenario: No translation at all
173
+ Given I have a "_config.yml" file with content:
174
+ """
175
+ url: https://www.yegor256.com
176
+ markdown: kramdown
177
+ plugins:
178
+ - jekyll-chatgpt-translate
179
+ chatgpt-translate:
180
+ source: en
181
+ threshold: 0
182
+ api_key: "it-is-not-used, because EN to EN translation"
183
+ layout: default
184
+ targets:
185
+ -
186
+ language: en
187
+ permalink: :slug.html
188
+ """
189
+ And I have a "_layouts/default.html" file with content:
190
+ """
191
+ {{ content }}
192
+ """
193
+ And I have a "_posts/2023-01-01-hello.md" file with content:
194
+ """
195
+ ---
196
+ title: foo
197
+ ---
198
+ {% if page.chatgpt-translate.model %}
199
+ TRANSLATED :(
200
+ {% else %}
201
+ NO TRANSLATION! :)
202
+ {% endif %}
203
+ """
204
+ Then I build Jekyll site
205
+ And Exit code is zero
206
+ And Stdout contains "The page is absent, need to translate"
207
+ And File "_site/2023/01/01/hello.html" exists
208
+ And File "_site/2023/01/01/hello.html" contains "NO TRANSLATION!"
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
29
29
  s.required_ruby_version = '>= 2.6'
30
30
  s.name = 'jekyll-chatgpt-translate'
31
- s.version = '0.0.30'
31
+ s.version = '0.0.32'
32
32
  s.license = 'MIT'
33
33
  s.summary = 'Translate Jekyll Pages Through ChatGPT'
34
34
  s.description = [
@@ -23,6 +23,7 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  require 'jekyll'
26
+ require 'fileutils'
26
27
  require 'json'
27
28
  require_relative 'chatgpt'
28
29
  require_relative 'permalink'
@@ -44,7 +45,7 @@ class GptTranslate::Generator < Jekyll::Generator
44
45
  # Main plugin action, called by Jekyll-core
45
46
  def generate(site)
46
47
  config ||= site.config['chatgpt-translate'] || {}
47
- home = '_chatgpt-translated'
48
+ home = config['tmpdir'] || '_chatgpt-translate'
48
49
  key = api_key(config)
49
50
  if key.nil?
50
51
  Jekyll.logger.info('jekyll-chatgpt-translate requires OPENAI_API_KEY environment variable')
@@ -59,7 +60,7 @@ class GptTranslate::Generator < Jekyll::Generator
59
60
  copied = 0
60
61
  model = config['model'] || 'gpt-3.5-turbo'
61
62
  marker = "Translated by ChatGPT #{model}#{version.empty? ? '' : "/#{version}"}"
62
- site.posts.docs.shuffle.each do |doc|
63
+ site.posts.docs.shuffle.each_with_index do |doc, pos|
63
64
  plain = GptTranslate::Plain.new(doc.content).to_s
64
65
  config['targets'].each do |target|
65
66
  start = Time.now
@@ -76,18 +77,36 @@ class GptTranslate::Generator < Jekyll::Generator
76
77
  "title: #{doc['title'].to_json}",
77
78
  "description: #{doc['description'].to_json}",
78
79
  "permalink: #{link.to_json}",
79
- "translated-original-url: #{doc.url.to_json}",
80
- "translated-language: #{lang.to_json}",
81
- "chatgpt-model: #{model.to_json}",
80
+ 'chatgpt-translate:',
81
+ " original-url: #{doc.url.to_json}",
82
+ " language: #{lang.to_json}",
83
+ " model: #{model.to_json}",
82
84
  '---'
83
85
  ].join("\n")
84
86
  )
85
- ping = GptTranslate::Ping.new(site, link)
86
- if config['no_download'].nil? && ping.found?(version.empty? ? '' : marker)
87
- copied += 1
88
- elsif translated >= threshold
89
- next
87
+ html = config['no_download'].nil? ? GptTranslate::Ping.new(site, link).download : nil
88
+ needed = false
89
+ added = false
90
+ if html.nil?
91
+ Jekyll.logger.info("The page is absent, need to translate #{link.inspect}")
92
+ needed = true
90
93
  else
94
+ copied += 1
95
+ site.static_files << DownloadedFile.new(site, link, html)
96
+ added = true
97
+ if version.empty?
98
+ Jekyll.logger.info("Re-translation not required, since version is empty: #{link.inspect}")
99
+ elsif html.include?(marker)
100
+ Jekyll.logger.info("No need to translate, the page exists at \
101
+ #{link.inspect} (#{html.split.count} words)")
102
+ else
103
+ Jekyll.logger.info("Re-translation required for #{link.inspect}")
104
+ needed = true
105
+ end
106
+ end
107
+ if translated >= threshold
108
+ Jekyll.logger.info("Page ##{pos} is ignored, we are over the threshold of #{threshold}: #{link}")
109
+ elsif needed
91
110
  gpt = GptTranslate::ChatGPT.new(
92
111
  key,
93
112
  model,
@@ -106,19 +125,41 @@ class GptTranslate::Generator < Jekyll::Generator
106
125
  mode: 'a+'
107
126
  )
108
127
  site.pages << Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path))
109
- site.static_files.delete_if { |f| f.is_a?(GptTranslate::Ping::DownloadedFile) && f.link == link }
128
+ site.static_files.delete_if { |f| f.is_a?(DownloadedFile) && f.link == link }
129
+ added = true
110
130
  translated += 1
111
131
  Jekyll.logger.info("Translated via ChatGPT \
112
132
  in #{(Time.now - start).round(2)}s: #{path} (#{File.size(path)} bytes)")
113
133
  end
114
- doc.data["translated-#{lang}-url"] = link
115
- doc.data['chatgpt-model'] = model
134
+ next unless added
135
+ doc.data['chatgpt-translate'] ||= {}
136
+ doc.data['chatgpt-translate']['model'] ||= model
137
+ doc.data['chatgpt-translate']['urls'] ||= {}
138
+ doc.data['chatgpt-translate']['urls'][lang] = link
116
139
  end
117
140
  end
118
141
  Jekyll.logger.info("jekyll-chatgpt-translate #{GptTranslate::VERSION}: \
119
142
  #{translated} pages translated and #{copied} pages copied in #{(Time.now - start).round(2)}s")
120
143
  end
121
144
 
145
+ # The file we just downloaded.
146
+ class DownloadedFile < Jekyll::StaticFile
147
+ attr_reader :link
148
+
149
+ def initialize(site, link, html)
150
+ super(site, site.dest, '', link)
151
+ @html = html
152
+ @link = link
153
+ end
154
+
155
+ def write(_dest)
156
+ FileUtils.mkdir_p(File.dirname(path))
157
+ File.write(path, @html)
158
+ Jekyll.logger.info("Saved #{@html.split.count} words to #{path.inspect}")
159
+ true
160
+ end
161
+ end
162
+
122
163
  private
123
164
 
124
165
  # Try to find the KEY, either in the environment, a file, etc.
@@ -25,8 +25,6 @@
25
25
  require 'iri'
26
26
  require 'net/http'
27
27
  require 'uri'
28
- require 'jekyll'
29
- require 'fileutils'
30
28
  require_relative 'version'
31
29
 
32
30
  # see https://stackoverflow.com/a/6048451/187141
@@ -48,47 +46,20 @@ class GptTranslate::Ping
48
46
  @path = path
49
47
  end
50
48
 
51
- def found?(marker)
49
+ # Downloads the page from the Internet and returns HTML or NIL, if the page is absent
50
+ def download
52
51
  home = @site.config['url']
53
- return false if home.nil?
52
+ return nil if home.nil?
54
53
  uri = Iri.new(home).path(@path).to_s
54
+ html = nil
55
55
  begin
56
- before = Net::HTTP.get_response(URI(uri))
57
- if before.is_a?(Net::HTTPSuccess)
58
- html = before.body
59
- @site.static_files << DownloadedFile.new(@site, @path, html)
60
- if html.include?(marker)
61
- Jekyll.logger.info("No need to translate, the page exists at \
62
- #{uri.inspect} (#{html.split.count} words)")
63
- return true
64
- end
65
- Jekyll.logger.info("Re-translation required for #{uri.inspect}")
66
- else
67
- Jekyll.logger.info("The page is absent, will translate #{uri.inspect} (#{before.code})")
68
- end
69
- Jekyll.logger.debug("GET #{uri.inspect}: #{before.code}")
56
+ response = Net::HTTP.get_response(URI(uri))
57
+ html = response.body if response.is_a?(Net::HTTPSuccess)
58
+ Jekyll.logger.debug("GET #{uri.inspect}: #{response.code}")
70
59
  rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL => e
71
60
  Jekyll.logger.debug("Failed to ping #{uri.inspect}: #{e.message}")
72
61
  Jekyll.logger.info("The page is absent (#{e.class.name}): #{uri.inspect}")
73
62
  end
74
- false
75
- end
76
-
77
- # The file we just downloaded.
78
- class DownloadedFile < Jekyll::StaticFile
79
- attr_reader :link
80
-
81
- def initialize(site, link, html)
82
- super(site, site.dest, '', link)
83
- @html = html
84
- @link = link
85
- end
86
-
87
- def write(_dest)
88
- FileUtils.mkdir_p(File.dirname(path))
89
- File.write(path, @html)
90
- Jekyll.logger.info("Saved #{@html.split.count} words to #{path.inspect}")
91
- true
92
- end
63
+ html
93
64
  end
94
65
  end
@@ -121,7 +121,11 @@ class GptTranslate::Plain
121
121
  end
122
122
 
123
123
  def link(link, _title, content)
124
- "[#{content}](#{link})"
124
+ if !link.nil? && link.start_with?('/', 'https://', 'http://')
125
+ "[#{content}](#{link})"
126
+ else
127
+ content
128
+ end
125
129
  end
126
130
  end
127
131
  end
@@ -23,5 +23,5 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  module GptTranslate
26
- VERSION = '0.0.30'
26
+ VERSION = '0.0.32'
27
27
  end
data/test/test_ping.rb CHANGED
@@ -38,24 +38,21 @@ class GptTranslate::PingTest < Minitest::Test
38
38
  stub_request(:any, 'https://www.yegor256.com/about-me.html').to_return(body: 'Hello!')
39
39
  site = GptTranslate::FakeSite.new({ 'url' => 'https://www.yegor256.com/' })
40
40
  ping = GptTranslate::Ping.new(site, '/about-me.html')
41
- assert(ping.found?(''))
42
- assert_equal(1, site.static_files.size)
41
+ assert(!ping.download.nil?)
43
42
  end
44
43
 
45
44
  def test_when_not_exists
46
45
  stub_request(:any, 'https://www.yegor256.com/absent.html').to_return(status: 404)
47
46
  site = GptTranslate::FakeSite.new({ 'url' => 'https://www.yegor256.com/' })
48
47
  ping = GptTranslate::Ping.new(site, '/absent.html')
49
- assert(!ping.found?(''))
50
- assert_equal(0, site.static_files.size)
48
+ assert(ping.download.nil?)
51
49
  end
52
50
 
53
51
  def test_wrong_address
54
52
  WebMock.allow_net_connect!
55
53
  site = GptTranslate::FakeSite.new({ 'url' => 'https://localhost:1/' })
56
54
  ping = GptTranslate::Ping.new(site, '/boom.html')
57
- assert(!ping.found?(''))
58
- assert_equal(0, site.static_files.size)
55
+ assert(ping.download.nil?)
59
56
  end
60
57
 
61
58
  def test_relative_path
data/test/test_plain.rb CHANGED
@@ -75,6 +75,14 @@ class GptTranslate::PlainTest < Minitest::Test
75
75
  'Hello, [dude](/a.html)!',
76
76
  GptTranslate::Plain.new('Hello, [dude](/a.html)!').to_s
77
77
  )
78
+ assert_equal(
79
+ 'Hello, dude!',
80
+ GptTranslate::Plain.new('Hello, [dude]()!').to_s
81
+ )
82
+ assert_equal(
83
+ 'Hello, dude!',
84
+ GptTranslate::Plain.new('Hello, [dude]({% post_url 2023-01-01-hello %})!').to_s
85
+ )
78
86
  end
79
87
 
80
88
  def test_code
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-chatgpt-translate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.30
4
+ version: 0.0.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-25 00:00:00.000000000 Z
11
+ date: 2023-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: iri