jekyll-chatgpt-translate 0.0.16 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -5
- data/features/cli.feature +5 -4
- data/features/gem_package.feature +1 -1
- data/features/step_definitions/steps.rb +10 -10
- data/jekyll-chatgpt-translate.gemspec +1 -1
- data/lib/jekyll-chatgpt-translate/generator.rb +13 -12
- data/lib/jekyll-chatgpt-translate/plain.rb +2 -0
- data/lib/jekyll-chatgpt-translate/version.rb +1 -1
- data/test/test_generator.rb +41 -14
- data/test/test_plain.rb +4 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f48ffb6334ada2deb878ede0622065326b23ed765946070aff20c9ce5888715d
|
4
|
+
data.tar.gz: df98890a24fb215b1ba1efc2794a6e4bf1cb0b9949efb94648b267c343052562
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 158df2fdeb7459cace117be39d3e94f9830c852034719ae049777b863bbbc61dc869c362534cc651f53e21af1fa37203865ec7b295254768274921a406cf858f
|
7
|
+
data.tar.gz: f8e34b0bd3432d598b9e7b2f5582ede75231f0c8c9e3dd71db348a5711ddf56106f7685adbf28c02a84f8ef84c7589f4a4909c650afb08d9a110751a317b8bbd
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ If you have a [Jekyll](https://jekyllrb.com/) static site, this plugin may help
|
|
7
7
|
translate its pages to another language, through [ChatGPT](https://chat.openai.com/). See how it
|
8
8
|
works for [my blog](https://github.com/yegor256/ru.yegor256.com),
|
9
9
|
for example [this page](https://ru.yegor256.com/2023-08-13-dictators.html) is translated to
|
10
|
-
[English]().
|
10
|
+
[English](https://ru.yegor256.com/english/2023-08-13-dictators.html).
|
11
11
|
|
12
12
|
Install it first:
|
13
13
|
|
@@ -18,13 +18,16 @@ gem install jekyll-chatgpt-translate
|
|
18
18
|
Then, add this to `_config.yml`:
|
19
19
|
|
20
20
|
```yaml
|
21
|
+
plugins:
|
22
|
+
- ... your other plugins here ...
|
23
|
+
- jekyll-chatgpt-translate
|
21
24
|
chatgpt-translate:
|
22
25
|
model: gpt-3.5-turbo
|
23
26
|
source: en
|
24
27
|
layout: translated
|
25
28
|
targets:
|
26
29
|
-
|
27
|
-
language:
|
30
|
+
language: zh
|
28
31
|
permalink: :year-:month-:day-:slug-chinese.html
|
29
32
|
layout: chinese-translated
|
30
33
|
-
|
@@ -32,7 +35,7 @@ chatgpt-translate:
|
|
32
35
|
permalink: :year-:month-:day-:title-french.html
|
33
36
|
```
|
34
37
|
|
35
|
-
Here, the source language is English (`en`), the target one is Chinese (`
|
38
|
+
Here, the source language is English (`en`), the target one is Chinese (`zh`),
|
36
39
|
the layout is `_layout/translated.html` (you must have this file).
|
37
40
|
|
38
41
|
OpenAI API KEY must be set in `OPENAI_API_KEY` environment variable, otherwise
|
@@ -40,9 +43,12 @@ the plugin will not do any translation and won't generate translated pages.
|
|
40
43
|
You can get your key [here](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key).
|
41
44
|
|
42
45
|
Inside the original page you can use `{{ page.translated-XX-url }}` in order to render the URL
|
43
|
-
of the translated page, where `XX` is the ISO-
|
46
|
+
of the translated page, where `XX` is the [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
|
47
|
+
code of the target language.
|
44
48
|
Inside the translated page you can use `{{ page.translated-original-url }}` in order
|
45
|
-
to get the URL of the page that was translated.
|
49
|
+
to get the URL of the page that was translated.
|
50
|
+
|
51
|
+
You can also use `{{ page.chatgpt-model }}`
|
46
52
|
inside both the original page and the translated one, to refer to the model of ChatGPT.
|
47
53
|
|
48
54
|
## Options
|
data/features/cli.feature
CHANGED
@@ -13,7 +13,7 @@ Feature: Simple site building
|
|
13
13
|
layout: translated
|
14
14
|
targets:
|
15
15
|
-
|
16
|
-
language:
|
16
|
+
language: zh
|
17
17
|
permalink: :year-:month-:day-:slug-chinese.html
|
18
18
|
layout: chinese-translated
|
19
19
|
-
|
@@ -22,7 +22,7 @@ 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-
|
25
|
+
The Chinese: {{ page.translated-zh-url }}
|
26
26
|
The French: {{ page.translated-fr-url }}
|
27
27
|
{{ content }}
|
28
28
|
"""
|
@@ -40,8 +40,9 @@ Feature: Simple site building
|
|
40
40
|
Hello, world!
|
41
41
|
"""
|
42
42
|
Then I build Jekyll site
|
43
|
-
And File "_chatgpt-translated/
|
44
|
-
And File "_chatgpt-translated/
|
43
|
+
And File "_chatgpt-translated/zh/2023-01-01-hello-zh.md" exists
|
44
|
+
And File "_chatgpt-translated/zh/2023-01-01-hello-zh.md" contains "/2023-01-01-hello-chinese.html"
|
45
|
+
And File "_chatgpt-translated/zh/2023-01-01-hello-zh.md" contains "translated-language: \"zh\""
|
45
46
|
And File "_site/2023/01/01/hello.html" exists
|
46
47
|
And File "_site/2023/01/01/hello.html" contains "The Chinese: /2023-01-01-hello-chinese.html"
|
47
48
|
And File "_site/2023-01-01-hello-chinese.html" exists
|
@@ -45,18 +45,18 @@ When(/^I build Jekyll site$/) do
|
|
45
45
|
@exitstatus = $CHILD_STATUS.exitstatus
|
46
46
|
end
|
47
47
|
|
48
|
-
Then(
|
49
|
-
raise "STDOUT doesn't contain '#{
|
48
|
+
Then('Stdout contains {string}') do |string|
|
49
|
+
raise "STDOUT doesn't contain '#{string}':\n#{@stdout}" unless @stdout.include?(string)
|
50
50
|
end
|
51
51
|
|
52
|
-
Then(
|
53
|
-
raise "The file \"#{
|
52
|
+
Then('File {string} exists') do |string|
|
53
|
+
raise "The file \"#{string}\" is absent:\n#{`tree -s`}" unless File.exist?(string)
|
54
54
|
end
|
55
55
|
|
56
|
-
Then(
|
57
|
-
raise "The file \"#{
|
58
|
-
content = File.read(
|
59
|
-
raise "The file \"#{
|
56
|
+
Then('File {string} contains {string}') do |string, string2|
|
57
|
+
raise "The file \"#{string}\" is absent" unless File.exist?(string)
|
58
|
+
content = File.read(string)
|
59
|
+
raise "The file \"#{string}\" doesn't contain \"#{string2}\":\n#{content}" unless content.include?(string2)
|
60
60
|
end
|
61
61
|
|
62
62
|
Then(/^Stdout is empty$/) do
|
@@ -85,10 +85,10 @@ When(/^I copy this gem into temp dir$/) do
|
|
85
85
|
FileUtils.copy_entry(@cwd, File.join(@dir, 'jekyll-chatgpt-translate'))
|
86
86
|
end
|
87
87
|
|
88
|
-
Given(
|
88
|
+
Given('It is Unix') do
|
89
89
|
pending if Gem.win_platform?
|
90
90
|
end
|
91
91
|
|
92
|
-
Given(
|
92
|
+
Given('It is Windows') do
|
93
93
|
pending unless Gem.win_platform?
|
94
94
|
end
|
@@ -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.17'
|
32
32
|
s.license = 'MIT'
|
33
33
|
s.summary = 'Translate Jekyll Pages Through ChatGPT'
|
34
34
|
s.description = [
|
@@ -54,7 +54,8 @@ class GptTranslate::Generator < Jekyll::Generator
|
|
54
54
|
version = config['version'] || GptTranslate::VERSION
|
55
55
|
threshold = config['threshold'] || 1024
|
56
56
|
start = Time.now
|
57
|
-
|
57
|
+
translated = 0
|
58
|
+
copied = 0
|
58
59
|
model = config['model'] || 'gpt-3.5-turbo'
|
59
60
|
marker = "Translated by ChatGPT #{model}/#{version}"
|
60
61
|
site.posts.docs.shuffle.each do |doc|
|
@@ -63,24 +64,24 @@ class GptTranslate::Generator < Jekyll::Generator
|
|
63
64
|
link = GptTranslate::Permalink.new(doc, target['permalink']).to_path
|
64
65
|
lang = target['language']
|
65
66
|
raise 'Language must be defined for each target' if target.nil?
|
66
|
-
if total >= threshold
|
67
|
-
Jekyll.logger.info("Already generated #{total} pages, that's enough for today")
|
68
|
-
break
|
69
|
-
end
|
70
67
|
path = File.join(home, lang, doc.basename.gsub(/\.md$/, "-#{lang}.md"))
|
71
68
|
FileUtils.mkdir_p(File.dirname(path))
|
72
69
|
File.write(path, '') # in order to surpress warnings in Page ctor
|
73
70
|
dest = Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path)).destination(site.dest)
|
74
71
|
doc.data["translated-#{lang}-url"] = link
|
75
72
|
doc.data['chatgpt-model'] = model
|
76
|
-
|
73
|
+
if GptTranslate::Ping.new(site, link).found?(dest, version)
|
74
|
+
copied += 1
|
75
|
+
next
|
76
|
+
end
|
77
|
+
next if translated >= threshold
|
77
78
|
gpt = GptTranslate::ChatGPT.new(
|
78
79
|
key,
|
79
80
|
model,
|
80
81
|
config['source'] || 'en',
|
81
82
|
lang
|
82
83
|
)
|
83
|
-
|
84
|
+
foreign = gpt.translate(plain)
|
84
85
|
File.write(
|
85
86
|
path,
|
86
87
|
[
|
@@ -90,21 +91,21 @@ class GptTranslate::Generator < Jekyll::Generator
|
|
90
91
|
"description: #{doc['description'].to_json}",
|
91
92
|
"permalink: #{link.to_json}",
|
92
93
|
"translated-original-url: #{doc.url.to_json}",
|
94
|
+
"translated-language: #{lang.to_json}",
|
93
95
|
"chatgpt-model: #{model.to_json}",
|
94
96
|
'---',
|
95
97
|
'',
|
96
|
-
|
98
|
+
foreign,
|
97
99
|
'',
|
98
100
|
"#{marker} on #{Time.now.strftime('%d/%m/%Y %H:%M')}\n{: .jekyll-chatgpt-translate}"
|
99
101
|
].join("\n")
|
100
102
|
)
|
101
103
|
site.pages << Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path))
|
102
|
-
|
103
|
-
Jekyll.logger.info("Translated via ChatGPT: #{path}")
|
104
|
+
translated += 1
|
105
|
+
Jekyll.logger.info("Translated via ChatGPT: #{path} (#{File.size(path)} bytes)")
|
104
106
|
end
|
105
|
-
break if total >= threshold
|
106
107
|
end
|
107
|
-
Jekyll.logger.info("#{
|
108
|
+
Jekyll.logger.info("#{translated} pages translated and #{copied} pages copied in #{(Time.now - start).round(2)}s")
|
108
109
|
end
|
109
110
|
|
110
111
|
private
|
data/test/test_generator.rb
CHANGED
@@ -33,19 +33,16 @@ require_relative '../lib/jekyll-chatgpt-translate/generator'
|
|
33
33
|
# License:: MIT
|
34
34
|
class GptTranslate::GeneratorTest < Minitest::Test
|
35
35
|
class FakeSite
|
36
|
-
attr_reader :config
|
36
|
+
attr_reader :config, :pages
|
37
37
|
|
38
|
-
def initialize(config,
|
38
|
+
def initialize(config, docs)
|
39
39
|
@config = config
|
40
|
-
@
|
40
|
+
@docs = docs
|
41
|
+
@pages = []
|
41
42
|
end
|
42
43
|
|
43
44
|
def posts
|
44
|
-
FakePosts.new(@
|
45
|
-
end
|
46
|
-
|
47
|
-
def pages
|
48
|
-
[]
|
45
|
+
FakePosts.new(@docs)
|
49
46
|
end
|
50
47
|
|
51
48
|
def permalink_style
|
@@ -65,7 +62,7 @@ class GptTranslate::GeneratorTest < Minitest::Test
|
|
65
62
|
end
|
66
63
|
|
67
64
|
def dest
|
68
|
-
|
65
|
+
File.dirname(@docs[0])
|
69
66
|
end
|
70
67
|
|
71
68
|
def in_theme_dir(base, _foo = nil, _bar = nil)
|
@@ -118,12 +115,12 @@ class GptTranslate::GeneratorTest < Minitest::Test
|
|
118
115
|
class FakePosts
|
119
116
|
attr_reader :config
|
120
117
|
|
121
|
-
def initialize(
|
122
|
-
@
|
118
|
+
def initialize(docs)
|
119
|
+
@docs = docs
|
123
120
|
end
|
124
121
|
|
125
122
|
def docs
|
126
|
-
|
123
|
+
@docs.map { |d| FakeDocument.new(d) }
|
127
124
|
end
|
128
125
|
end
|
129
126
|
|
@@ -137,18 +134,48 @@ class GptTranslate::GeneratorTest < Minitest::Test
|
|
137
134
|
'chatgpt-translate' => {
|
138
135
|
'targets' => [
|
139
136
|
{
|
140
|
-
'language' => '
|
137
|
+
'language' => 'zh',
|
141
138
|
'layout' => 'chinese',
|
142
139
|
'permalink' => ':slug.html'
|
143
140
|
}
|
144
141
|
]
|
145
142
|
}
|
146
143
|
},
|
147
|
-
|
144
|
+
[post]
|
145
|
+
)
|
146
|
+
gen = GptTranslate::Generator.new
|
147
|
+
stub_request(:get, 'https://www.yegor256.com/.html').to_return(body: '')
|
148
|
+
gen.generate(site)
|
149
|
+
assert_equal(1, site.pages.count)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_threshold_stops
|
154
|
+
Dir.mktmpdir do |home|
|
155
|
+
post = File.join(home, '2023-01-01-hello.md')
|
156
|
+
File.write(post, "---\ntitle: Hello\n---\n\nHello, world!")
|
157
|
+
site = FakeSite.new(
|
158
|
+
{
|
159
|
+
'chatgpt-translate' => {
|
160
|
+
'threshold' => 1,
|
161
|
+
'targets' => [
|
162
|
+
{
|
163
|
+
'language' => 'zh',
|
164
|
+
'permalink' => ':slug.html'
|
165
|
+
},
|
166
|
+
{
|
167
|
+
'language' => 'fr',
|
168
|
+
'permalink' => ':year/:slug.html'
|
169
|
+
}
|
170
|
+
]
|
171
|
+
}
|
172
|
+
},
|
173
|
+
[post, post]
|
148
174
|
)
|
149
175
|
gen = GptTranslate::Generator.new
|
150
176
|
stub_request(:get, 'https://www.yegor256.com/.html').to_return(body: '')
|
151
177
|
gen.generate(site)
|
178
|
+
assert_equal(1, site.pages.count)
|
152
179
|
end
|
153
180
|
end
|
154
181
|
end
|
data/test/test_plain.rb
CHANGED
@@ -96,6 +96,10 @@ class GptTranslate::PlainTest < Minitest::Test
|
|
96
96
|
"```\nHello\n```",
|
97
97
|
GptTranslate::Plain.new("```\nHello\n```").to_s
|
98
98
|
)
|
99
|
+
assert_equal(
|
100
|
+
"```\nprint('hi!')\n```",
|
101
|
+
GptTranslate::Plain.new("```java\nprint('hi!')\n```").to_s
|
102
|
+
)
|
99
103
|
end
|
100
104
|
|
101
105
|
def test_liquid_tags
|