jekyll-chatgpt-translate 0.0.14 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/jekyll-chatgpt-translate.gemspec +1 -1
- data/lib/jekyll-chatgpt-translate/chatgpt.rb +9 -19
- data/lib/jekyll-chatgpt-translate/generator.rb +1 -1
- data/lib/jekyll-chatgpt-translate/plain.rb +17 -19
- data/lib/jekyll-chatgpt-translate/prompt.rb +59 -0
- data/lib/jekyll-chatgpt-translate/version.rb +1 -1
- data/test/test_plain.rb +12 -4
- data/test/test_prompt.rb +50 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3a1fa14a50caf08c780825e889b16c100e4afcd4042db089e49b33d01eece4a
|
4
|
+
data.tar.gz: 87cf1124880695e6efad136115b7dfd9e33a629e03f154adc5f7325f7f6e43e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb6b987ce7934d9294ba69f73d4d23bb3c74d6394cc5537ed956ee0b54b280f0ae79e5d12c9974ef75402f0586422700727d02304e44c4d7f5c74090c7b7c03c
|
7
|
+
data.tar.gz: 597a0397823b9d91a65ce1b8269f3117f37152b2ab9c8eafd501a9c4c0aaeec9ecd70022460616c2ae8c212605fb2fd1df347ab07b9accc5323e5931d7cfd037
|
@@ -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.16'
|
32
32
|
s.license = 'MIT'
|
33
33
|
s.summary = 'Translate Jekyll Pages Through ChatGPT'
|
34
34
|
s.description = [
|
@@ -25,6 +25,7 @@
|
|
25
25
|
require 'jekyll'
|
26
26
|
require 'openai'
|
27
27
|
require 'iso-639'
|
28
|
+
require_relative 'prompt'
|
28
29
|
|
29
30
|
# The module we are in.
|
30
31
|
module GptTranslate; end
|
@@ -51,7 +52,7 @@ class GptTranslate::ChatGPT
|
|
51
52
|
if par.length <= 32
|
52
53
|
Jekyll.logger.debug("Not translating this, b/c too short: \"#{par}\"")
|
53
54
|
par
|
54
|
-
elsif par !~ /^[
|
55
|
+
elsif par !~ /^[[:alpha:]]/
|
55
56
|
Jekyll.logger.debug("Not translating this, b/c it's not a plain text: \"#{par}\"")
|
56
57
|
par
|
57
58
|
elsif @key.empty?
|
@@ -66,38 +67,27 @@ class GptTranslate::ChatGPT
|
|
66
67
|
|
67
68
|
def translate_par(par)
|
68
69
|
start = Time.now
|
69
|
-
|
70
|
+
answer = nil
|
70
71
|
attempt = 0
|
71
72
|
begin
|
73
|
+
prompt = GptTranslate::Prompt.new(par, @source, @target).to_s
|
72
74
|
response = OpenAI::Client.new(access_token: @key).chat(
|
73
75
|
parameters: {
|
74
76
|
model: @model,
|
75
|
-
messages: [{
|
76
|
-
role: 'user',
|
77
|
-
content: "#{prompt}:\n\n#{par}"
|
78
|
-
}],
|
77
|
+
messages: [{ role: 'user', content: prompt }],
|
79
78
|
temperature: 0.7
|
80
79
|
}
|
81
80
|
)
|
82
|
-
|
81
|
+
answer = response.dig('choices', 0, 'message', 'content')
|
82
|
+
Jekyll.logger.debug("ChatGPT prompt: \"#{prompt}\", ChatGPT answer: \"#{answer}\"")
|
83
83
|
rescue StandardError => e
|
84
84
|
attempt += 1
|
85
85
|
retry if attempt < 4
|
86
86
|
raise e
|
87
87
|
end
|
88
88
|
Jekyll.logger.info("Translated #{par.split.count} #{@source.upcase} words \
|
89
|
-
to #{
|
89
|
+
to #{answer.split.count} #{@target.upcase} words \
|
90
90
|
through #{@model} in #{(Time.now - start).round(2)}s")
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
def prompt
|
95
|
-
[
|
96
|
-
'Please, translate this paragraph from',
|
97
|
-
ISO_639.find_by_code(@source),
|
98
|
-
'to',
|
99
|
-
ISO_639.find_by_code(@target),
|
100
|
-
', don\'t change proper nouns'
|
101
|
-
].join(' ')
|
91
|
+
answer
|
102
92
|
end
|
103
93
|
end
|
@@ -95,7 +95,7 @@ class GptTranslate::Generator < Jekyll::Generator
|
|
95
95
|
'',
|
96
96
|
translated,
|
97
97
|
'',
|
98
|
-
"#{marker}\n{: .jekyll-chatgpt-translate}"
|
98
|
+
"#{marker} on #{Time.now.strftime('%d/%m/%Y %H:%M')}\n{: .jekyll-chatgpt-translate}"
|
99
99
|
].join("\n")
|
100
100
|
)
|
101
101
|
site.pages << Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path))
|
@@ -40,36 +40,26 @@ class GptTranslate::Plain
|
|
40
40
|
def to_s
|
41
41
|
# To turn compact lists into proper lists
|
42
42
|
@markdown.gsub(/([^\n])\n(\s*\*)/, "\\1\n\n\\2").split(/\n{2,}/).compact.map do |par|
|
43
|
-
|
44
|
-
par.gsub!("\t", ' ')
|
45
|
-
par.gsub!(/ {2,}/, ' ')
|
43
|
+
par.strip!
|
46
44
|
# Liquid tags are removed, but this implementation is primitive
|
47
45
|
# Seehttps://stackoverflow.com/questions/
|
48
46
|
par.gsub!(/{{[^}]+}}/, '')
|
49
47
|
par.gsub!(/{%.+?%}/, '')
|
50
48
|
par.gsub!(/<!--.+?-->/m, '')
|
51
|
-
par.
|
52
|
-
|
49
|
+
par = Redcarpet::Markdown.new(Strip).render(par)
|
50
|
+
par.gsub!("\t", ' ')
|
51
|
+
par.gsub!(/\n+/, ' ') unless par.start_with?('```')
|
52
|
+
par.gsub!(/ {2,}/, ' ') unless par.start_with?('```')
|
53
|
+
par.strip
|
53
54
|
end.join("\n\n").gsub(/\n{2,}/, "\n\n").strip
|
54
55
|
end
|
55
56
|
|
56
|
-
# # To ignore/remove Liquid tags.
|
57
|
-
# class NullDrop < Liquid::Drop
|
58
|
-
# def method_missing(*)
|
59
|
-
# nil
|
60
|
-
# end
|
61
|
-
|
62
|
-
# def respond_to_missing?(*)
|
63
|
-
# true
|
64
|
-
# end
|
65
|
-
# end
|
66
|
-
|
67
57
|
# Markdown to pain text.
|
68
58
|
class Strip < Redcarpet::Render::Base
|
69
59
|
%i[
|
70
60
|
block_code block_quote
|
71
61
|
block_html
|
72
|
-
autolink
|
62
|
+
autolink double_emphasis
|
73
63
|
emphasis underline
|
74
64
|
triple_emphasis strikethrough
|
75
65
|
superscript highlight quote
|
@@ -81,8 +71,16 @@ class GptTranslate::Plain
|
|
81
71
|
end
|
82
72
|
end
|
83
73
|
|
84
|
-
def
|
85
|
-
|
74
|
+
def codespan(content)
|
75
|
+
if content.start_with?("\n")
|
76
|
+
"```#{content}```"
|
77
|
+
else
|
78
|
+
content
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def raw_html(content)
|
83
|
+
content
|
86
84
|
end
|
87
85
|
|
88
86
|
def list(content, _type)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# (The MIT License)
|
4
|
+
#
|
5
|
+
# Copyright (c) 2023 Yegor Bugayenko
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
15
|
+
# copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
# SOFTWARE.
|
24
|
+
|
25
|
+
require 'iso-639'
|
26
|
+
|
27
|
+
# The module we are in.
|
28
|
+
module GptTranslate; end
|
29
|
+
|
30
|
+
# Prompt for ChatGPT.
|
31
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
32
|
+
# Copyright:: Copyright (c) 2023 Yegor Bugayenko
|
33
|
+
# License:: MIT
|
34
|
+
class GptTranslate::Prompt
|
35
|
+
# Ctor.
|
36
|
+
# +par+ Text to translate
|
37
|
+
# +source+ The language to translate from
|
38
|
+
# +target+ The language to translate into
|
39
|
+
def initialize(par, source, target)
|
40
|
+
@par = par
|
41
|
+
@source = source
|
42
|
+
@target = target
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
from = ISO_639.find_by_code(@source)
|
47
|
+
raise "Unknown source language ISO-639 code: \"#{@source}\"" if from.nil?
|
48
|
+
to = ISO_639.find_by_code(@target)
|
49
|
+
raise "Unknown source language ISO-639 code: \"#{@target}\"" if to.nil?
|
50
|
+
head = [
|
51
|
+
'Please, translate the following paragraph from ',
|
52
|
+
from[3],
|
53
|
+
' to ',
|
54
|
+
to[3],
|
55
|
+
', don\'t change proper nouns'
|
56
|
+
].join
|
57
|
+
"#{head}:\n\n#{@par}"
|
58
|
+
end
|
59
|
+
end
|
data/test/test_plain.rb
CHANGED
@@ -34,6 +34,7 @@ class GptTranslate::PlainTest < Minitest::Test
|
|
34
34
|
assert_equal('Hello, world!', GptTranslate::Plain.new('Hello, **world**!').to_s)
|
35
35
|
assert_equal('Hello, Jeff!', GptTranslate::Plain.new('Hello, _Jeff_!').to_s)
|
36
36
|
assert_equal("Hi\n\nBye", GptTranslate::Plain.new(" Hi\n\nBye\n\n\n").to_s)
|
37
|
+
assert_equal('Hi, dude!', GptTranslate::Plain.new(" Hi,\ndude!\n").to_s)
|
37
38
|
end
|
38
39
|
|
39
40
|
def test_lists
|
@@ -77,17 +78,24 @@ class GptTranslate::PlainTest < Minitest::Test
|
|
77
78
|
|
78
79
|
def test_code_block
|
79
80
|
assert_equal(
|
80
|
-
"Hello:\n\nJava",
|
81
|
+
"Hello:\n\n```\nJava\n```",
|
81
82
|
GptTranslate::Plain.new("Hello:\n\n```\nJava\n```\n").to_s
|
82
83
|
)
|
83
84
|
end
|
84
85
|
|
85
86
|
def test_html
|
86
87
|
assert_equal(
|
87
|
-
'This is picture:
|
88
|
+
'This is picture: <img src="a"/>!',
|
88
89
|
GptTranslate::Plain.new('This is picture: <img src="a"/>!').to_s
|
89
90
|
)
|
90
|
-
assert_equal('
|
91
|
+
assert_equal('<img src="a"/>', GptTranslate::Plain.new('<img src="a"/>').to_s)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_big_code
|
95
|
+
assert_equal(
|
96
|
+
"```\nHello\n```",
|
97
|
+
GptTranslate::Plain.new("```\nHello\n```").to_s
|
98
|
+
)
|
91
99
|
end
|
92
100
|
|
93
101
|
def test_liquid_tags
|
@@ -96,7 +104,7 @@ class GptTranslate::PlainTest < Minitest::Test
|
|
96
104
|
GptTranslate::Plain.new('Hello, {{ Java }}!').to_s
|
97
105
|
)
|
98
106
|
assert_equal(
|
99
|
-
'Hello,
|
107
|
+
'Hello, dude !',
|
100
108
|
GptTranslate::Plain.new('Hello, {% if a %} dude {% endif %}!').to_s
|
101
109
|
)
|
102
110
|
assert_equal(
|
data/test/test_prompt.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# (The MIT License)
|
4
|
+
#
|
5
|
+
# Copyright (c) 2023 Yegor Bugayenko
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
15
|
+
# copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
# SOFTWARE.
|
24
|
+
|
25
|
+
require 'minitest/autorun'
|
26
|
+
require_relative '../lib/jekyll-chatgpt-translate/prompt'
|
27
|
+
|
28
|
+
# Prompt test.
|
29
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
30
|
+
# Copyright:: Copyright (c) 2023 Yegor Bugayenko
|
31
|
+
# License:: MIT
|
32
|
+
class GptTranslate::PromptTest < Minitest::Test
|
33
|
+
def par(body, source, target)
|
34
|
+
"Please, translate the following paragraph from #{source} to #{target}, don't change proper nouns:\n\n#{body}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_english_to_russian
|
38
|
+
assert_equal(
|
39
|
+
par('Hello, dude!', 'English', 'Russian'),
|
40
|
+
GptTranslate::Prompt.new('Hello, dude!', 'en', 'ru').to_s
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_english_to_chinese
|
45
|
+
assert_equal(
|
46
|
+
par('Hello, Jeff!', 'English', 'Chinese'),
|
47
|
+
GptTranslate::Prompt.new('Hello, Jeff!', 'en', 'zh').to_s
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- lib/jekyll-chatgpt-translate/permalink.rb
|
111
111
|
- lib/jekyll-chatgpt-translate/ping.rb
|
112
112
|
- lib/jekyll-chatgpt-translate/plain.rb
|
113
|
+
- lib/jekyll-chatgpt-translate/prompt.rb
|
113
114
|
- lib/jekyll-chatgpt-translate/version.rb
|
114
115
|
- logo.png
|
115
116
|
- renovate.json
|
@@ -119,6 +120,7 @@ files:
|
|
119
120
|
- test/test_permalink.rb
|
120
121
|
- test/test_ping.rb
|
121
122
|
- test/test_plain.rb
|
123
|
+
- test/test_prompt.rb
|
122
124
|
homepage: https://github.com/yegor256/jekyll-chatgpt-translate
|
123
125
|
licenses:
|
124
126
|
- MIT
|