jekyll-chatgpt-translate 0.0.6 → 0.0.7
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/Gemfile +1 -0
- data/features/cli.feature +6 -4
- data/features/step_definitions/steps.rb +1 -1
- data/jekyll-chatgpt-translate.gemspec +1 -1
- data/lib/jekyll-chatgpt-translate/chatgpt.rb +9 -14
- data/lib/jekyll-chatgpt-translate/generator.rb +7 -6
- data/lib/jekyll-chatgpt-translate/permalink.rb +5 -2
- data/lib/jekyll-chatgpt-translate/ping.rb +4 -3
- data/test/test_chatgpt.rb +8 -0
- data/test/test_permalink.rb +6 -6
- data/test/test_ping.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 951ecc1df96633e4552be477e42c43977b0187282978d9cb1c07bedc3913e8fa
|
4
|
+
data.tar.gz: 9fb20f291797eaee903f0ca20aaf0adcc7ed65fd77626dda6afbe3c6390418cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d94a2be708d58bb23427a707660276ce7d42dc90372924644b76558933c631834d3eeece4b1bb46c5b89221c3d758b62e71958137e355edb02451ef671fc9f03
|
7
|
+
data.tar.gz: 623f0e383d89805288678b37d9304d365b3adf6995d60c9bec4e7a9e6c85e2be44cb51e4a1001892cb5919a55799f99dfe9e174c5edf931f9f2e641d24ffed37
|
data/Gemfile
CHANGED
data/features/cli.feature
CHANGED
@@ -13,11 +13,11 @@ Feature: Simple site building
|
|
13
13
|
targets:
|
14
14
|
-
|
15
15
|
language: cn
|
16
|
-
permalink: :year-:month-:day-:
|
16
|
+
permalink: :year-:month-:day-:slug-chinese.html
|
17
17
|
layout: chinese-translated
|
18
18
|
-
|
19
19
|
language: fr
|
20
|
-
permalink: :year/:
|
20
|
+
permalink: :year/:slug-french.html
|
21
21
|
"""
|
22
22
|
And I have a "_layouts/default.html" file with content:
|
23
23
|
"""
|
@@ -30,13 +30,15 @@ Feature: Simple site building
|
|
30
30
|
And I have a "_posts/2023-01-01-hello.md" file with content:
|
31
31
|
"""
|
32
32
|
---
|
33
|
+
title: Hello, world!
|
33
34
|
layout: default
|
34
35
|
---
|
35
36
|
Hello, world!
|
36
37
|
"""
|
37
38
|
Then I build Jekyll site
|
39
|
+
Then File "_chatgpt-translated/cn/2023-01-01-hello-cn.md" exists
|
38
40
|
Then File "_site/2023/01/01/hello.html" exists
|
39
|
-
Then File "_site/2023-01-01-
|
40
|
-
Then File "_site/2023/
|
41
|
+
Then File "_site/2023-01-01-hello-chinese.html" exists
|
42
|
+
Then File "_site/2023/hello-french.html" exists
|
41
43
|
And Exit code is zero
|
42
44
|
|
@@ -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.
|
31
|
+
s.version = '0.0.7'
|
32
32
|
s.license = 'MIT'
|
33
33
|
s.summary = 'Translate Jekyll Pages Through ChatGPT'
|
34
34
|
s.description = [
|
@@ -65,7 +65,7 @@ class GptTranslate::ChatGPT
|
|
65
65
|
private
|
66
66
|
|
67
67
|
def translate_par(par)
|
68
|
-
Time.now
|
68
|
+
start = Time.now
|
69
69
|
t = nil
|
70
70
|
attempt = 0
|
71
71
|
begin
|
@@ -80,26 +80,21 @@ class GptTranslate::ChatGPT
|
|
80
80
|
}
|
81
81
|
)
|
82
82
|
t = response.dig('choices', 0, 'message', 'content')
|
83
|
-
rescue StandardError
|
83
|
+
rescue StandardError => e
|
84
84
|
attempt += 1
|
85
85
|
retry if attempt < 4
|
86
|
+
raise e
|
86
87
|
end
|
87
|
-
|
88
|
-
|
89
|
-
#{@
|
90
|
-
|
91
|
-
else
|
92
|
-
Jekyll.logger.debug("Translated #{par.split.count} #{@source.upcase} words
|
93
|
-
to #{t.split.count} #{@target.upcase} words
|
94
|
-
through #{@model} in #{(Time.now - pstart).round(2)}s")
|
95
|
-
t
|
96
|
-
end
|
88
|
+
Jekyll.logger.info("Translated #{par.split.count} #{@source.upcase} words \
|
89
|
+
to #{t.split.count} #{@target.upcase} words \
|
90
|
+
through #{@model} in #{(Time.now - start).round(2)}s")
|
91
|
+
t
|
97
92
|
end
|
98
93
|
|
99
94
|
def prompt
|
100
|
-
if (source == 'ru') && (target == 'en')
|
95
|
+
if (@source == 'ru') && (@target == 'en')
|
101
96
|
'Пожалуйста, переведи этот параграф на английский язык'
|
102
|
-
elsif (source == 'en') && (target == 'ru')
|
97
|
+
elsif (@source == 'en') && (@target == 'ru')
|
103
98
|
'Please, translate this paragraph to Russian'
|
104
99
|
else
|
105
100
|
[
|
@@ -43,11 +43,12 @@ class GptTranslate::Generator < Jekyll::Generator
|
|
43
43
|
# Main plugin action, called by Jekyll-core
|
44
44
|
def generate(site)
|
45
45
|
@site = site
|
46
|
+
home = '_chatgpt-translated'
|
46
47
|
key = ENV.fetch('OPENAI_API_KEY', nil)
|
47
48
|
if key.nil? && Jekyll.env == 'development'
|
48
|
-
Jekyll.logger.info(
|
49
|
-
we are in development mode, no actual translation will happen,
|
50
|
-
but pages will be generated
|
49
|
+
Jekyll.logger.info("OPENAI_API_KEY environment variable is not set and \
|
50
|
+
we are in development mode, no actual translation will happen, \
|
51
|
+
but pages will be generated")
|
51
52
|
key = ''
|
52
53
|
end
|
53
54
|
if key.nil?
|
@@ -61,7 +62,7 @@ but pages will be generated')
|
|
61
62
|
site.posts.docs.each do |doc|
|
62
63
|
plain = GptTranslate::Plain.new(doc.content).to_s
|
63
64
|
config['targets'].each do |target|
|
64
|
-
link = GptTranslate::Permalink.new(doc, target['permalink']).
|
65
|
+
link = GptTranslate::Permalink.new(doc, target['permalink']).to_path
|
65
66
|
next if GptTranslate::Ping.new(site, link).exists?
|
66
67
|
lang = target['language']
|
67
68
|
raise 'Language must be defined for each target' if target.nil?
|
@@ -77,7 +78,7 @@ but pages will be generated')
|
|
77
78
|
lang
|
78
79
|
)
|
79
80
|
translated = gpt.translate(plain)
|
80
|
-
path =
|
81
|
+
path = File.join(home, lang, doc.basename.gsub(/\.md$/, "-#{lang}.md"))
|
81
82
|
FileUtils.mkdir_p(File.dirname(path))
|
82
83
|
File.write(
|
83
84
|
path,
|
@@ -98,7 +99,7 @@ but pages will be generated')
|
|
98
99
|
end
|
99
100
|
break if total >= threshold
|
100
101
|
end
|
101
|
-
Jekyll.logger.info("#{total} pages
|
102
|
+
Jekyll.logger.info("#{total} pages translated in #{(Time.now - start).round(2)}s")
|
102
103
|
end
|
103
104
|
|
104
105
|
private
|
@@ -38,11 +38,14 @@ class GptTranslate::Permalink
|
|
38
38
|
@template = template
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
@template
|
41
|
+
def to_path
|
42
|
+
path = @template
|
43
43
|
.gsub(':year', format('%04d', @doc['date'].year))
|
44
44
|
.gsub(':month', format('%02d', @doc['date'].month))
|
45
45
|
.gsub(':day', format('%02d', @doc['date'].day))
|
46
46
|
.gsub(':title', CGI.escape(@doc['title']))
|
47
|
+
.gsub(':slug', CGI.escape(@doc['slug']))
|
48
|
+
path = "/#{path}" unless path.start_with?('/')
|
49
|
+
path
|
47
50
|
end
|
48
51
|
end
|
@@ -40,15 +40,16 @@ module GptTranslate; end
|
|
40
40
|
# License:: MIT
|
41
41
|
class GptTranslate::Ping
|
42
42
|
# Ctor.
|
43
|
-
def initialize(site,
|
43
|
+
def initialize(site, path)
|
44
44
|
@site = site
|
45
|
-
|
45
|
+
raise 'Permalink must start with a slash' unless path.start_with?('/')
|
46
|
+
@path = path
|
46
47
|
end
|
47
48
|
|
48
49
|
def exists?
|
49
50
|
home = @site.config['url']
|
50
51
|
return false if home.nil?
|
51
|
-
uri = Iri.new(home).path(@
|
52
|
+
uri = Iri.new(home).path(@path)
|
52
53
|
before = Net::HTTP.get_response(URI(uri.to_s))
|
53
54
|
if before.is_a?(Net::HTTPSuccess) && before.body.include?("/#{GptTranslate::VERSION}")
|
54
55
|
Jekyll.logger.info("No need to translate, page exists at #{uri} (#{before.body.split.count} words)")
|
data/test/test_chatgpt.rb
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
# SOFTWARE.
|
24
24
|
|
25
25
|
require 'minitest/autorun'
|
26
|
+
require 'webmock/minitest'
|
26
27
|
require_relative '../lib/jekyll-chatgpt-translate/chatgpt'
|
27
28
|
|
28
29
|
# ChatGPT test.
|
@@ -44,4 +45,11 @@ class GptTranslate::ChatGPTTest < Minitest::Test
|
|
44
45
|
chat = GptTranslate::ChatGPT.new('fake-key', 'gpt-3.5-turbo', 'en', 'ru')
|
45
46
|
assert_equal('<img src="a"/>', chat.translate('<img src="a"/>'))
|
46
47
|
end
|
48
|
+
|
49
|
+
def test_through_webmock
|
50
|
+
stub_request(:any, 'https://api.openai.com/v1/chat/completions')
|
51
|
+
.to_return(body: '{"choices":[{"message":{"content": "boom!"}}]}')
|
52
|
+
chat = GptTranslate::ChatGPT.new('fake-key', 'gpt-3.5-turbo', 'en', 'ru')
|
53
|
+
assert_equal('boom!', chat.translate('This is the text to send to OpenAI'))
|
54
|
+
end
|
47
55
|
end
|
data/test/test_permalink.rb
CHANGED
@@ -34,9 +34,9 @@ class GptTranslate::PermalinkTest < Minitest::Test
|
|
34
34
|
assert_equal(
|
35
35
|
'/2023.html',
|
36
36
|
GptTranslate::Permalink.new(
|
37
|
-
{ 'date' => Time.parse('2023-01-01'), 'title' => 'Hello' },
|
38
|
-
'
|
39
|
-
).
|
37
|
+
{ 'date' => Time.parse('2023-01-01'), 'title' => 'Hello', 'slug' => 'hello' },
|
38
|
+
':year.html'
|
39
|
+
).to_path
|
40
40
|
)
|
41
41
|
end
|
42
42
|
|
@@ -44,9 +44,9 @@ class GptTranslate::PermalinkTest < Minitest::Test
|
|
44
44
|
assert_equal(
|
45
45
|
'/2023-%23%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82.html',
|
46
46
|
GptTranslate::Permalink.new(
|
47
|
-
{ 'date' => Time.parse('2023-01-01'), 'title' => '#привет' },
|
48
|
-
'
|
49
|
-
).
|
47
|
+
{ 'date' => Time.parse('2023-01-01'), 'title' => '#привет', 'slug' => 'hello' },
|
48
|
+
':year-:title.html'
|
49
|
+
).to_path
|
50
50
|
)
|
51
51
|
end
|
52
52
|
end
|
data/test/test_ping.rb
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
# SOFTWARE.
|
24
24
|
|
25
25
|
require 'minitest/autorun'
|
26
|
+
require 'webmock/minitest'
|
26
27
|
require 'jekyll'
|
27
28
|
require_relative '../lib/jekyll-chatgpt-translate/ping'
|
28
29
|
|
@@ -40,8 +41,22 @@ class GptTranslate::PingTest < Minitest::Test
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def test_when_exists
|
44
|
+
stub_request(:any, 'https://www.yegor256.com/about-me.html')
|
43
45
|
site = FakeSite.new({ 'url' => 'https://www.yegor256.com/' })
|
44
46
|
ping = GptTranslate::Ping.new(site, '/about-me.html')
|
45
47
|
assert(!ping.exists?)
|
46
48
|
end
|
49
|
+
|
50
|
+
def test_when_not_exists
|
51
|
+
stub_request(:any, 'https://www.yegor256.com/absent.html').to_return(status: 404)
|
52
|
+
site = FakeSite.new({ 'url' => 'https://www.yegor256.com/' })
|
53
|
+
ping = GptTranslate::Ping.new(site, '/absent.html')
|
54
|
+
assert(!ping.exists?)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_relative_path
|
58
|
+
assert_raises do
|
59
|
+
GptTranslate::Ping.new({}, '404.html')
|
60
|
+
end
|
61
|
+
end
|
47
62
|
end
|
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.
|
4
|
+
version: 0.0.7
|
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-
|
11
|
+
date: 2023-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: iri
|