jekyll-chatgpt-translate 0.0.16 → 0.0.18

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: f3a1fa14a50caf08c780825e889b16c100e4afcd4042db089e49b33d01eece4a
4
- data.tar.gz: 87cf1124880695e6efad136115b7dfd9e33a629e03f154adc5f7325f7f6e43e9
3
+ metadata.gz: 1f593e5927f89dc9fe25dcbe28cc61ed938b3fdc42aee742d9ec31ebc86417fe
4
+ data.tar.gz: c5486a9adaf6e838bd016ea01175169786dbdd1807601cf922b0acc065c0a337
5
5
  SHA512:
6
- metadata.gz: fb6b987ce7934d9294ba69f73d4d23bb3c74d6394cc5537ed956ee0b54b280f0ae79e5d12c9974ef75402f0586422700727d02304e44c4d7f5c74090c7b7c03c
7
- data.tar.gz: 597a0397823b9d91a65ce1b8269f3117f37152b2ab9c8eafd501a9c4c0aaeec9ecd70022460616c2ae8c212605fb2fd1df347ab07b9accc5323e5931d7cfd037
6
+ metadata.gz: 794664182ebba232506ab97b026d02a7060ee369fb16756308d43362df24aed862703836fb0af142c07240fd1eeeeeb9305e43804ed5b34300ce2dff1c30a03c
7
+ data.tar.gz: f1db90b466e6ea441519ee35351a2bf62b9182ed772ab020b5cd4692fc8932a22115fb994089b1cb70724c92a4344865810312b7a58fe1fe666ba0e5c308aaeb
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: cn
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 (`cn`),
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,10 +43,15 @@ 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-839-1 code of the target language..
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. You can also use `{{ page.chatgpt-model }}`
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.
53
+ The presence of this attribute in the `{{ page }}` means that the
54
+ page was translated or the translated HTML was downloaded and placed into the `_site` directory.
47
55
 
48
56
  ## Options
49
57
 
@@ -56,6 +64,11 @@ Full list of options available to specify in `_config.yml`:
56
64
 
57
65
  * `source` (optional) — is the ISO-839-1 code of the source language.
58
66
 
67
+ * `no_download` (optional) — if this attribute is present, the plugin won't try
68
+ to find HTML versions of translated pages in the Internet and won't try to
69
+ download them and place into the `_site` directory. Thus, your entire site
70
+ will have to be re-translated on every build (might be very ineffective if the site is big!)
71
+
59
72
  * `layout` (optional) — is name of the file in `_layouts` directory, without the extension.
60
73
  This layout will be specified for the pages generated by this plugin.
61
74
 
data/features/cli.feature CHANGED
@@ -13,7 +13,7 @@ Feature: Simple site building
13
13
  layout: translated
14
14
  targets:
15
15
  -
16
- language: cn
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-cn-url }}
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/cn/2023-01-01-hello-cn.md" exists
44
- And File "_chatgpt-translated/cn/2023-01-01-hello-cn.md" contains "/2023-01-01-hello-chinese.html"
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
@@ -3,7 +3,7 @@ Feature: Gem Package
3
3
  package the Gem into .gem file
4
4
 
5
5
  Scenario: Gem can be packaged
6
- When it is Unix
6
+ When It is Unix
7
7
  Given I have a "execs.rb" file with content:
8
8
  """
9
9
  #!/usr/bin/env ruby
@@ -40,39 +40,35 @@ Given(/^I have a "([^"]*)" file with content:$/) do |file, text|
40
40
  File.write(file, text.gsub('\\xFF', 0xFF.chr))
41
41
  end
42
42
 
43
- When(/^I build Jekyll site$/) do
43
+ When('I build Jekyll site') do
44
44
  @stdout = `jekyll build`
45
45
  @exitstatus = $CHILD_STATUS.exitstatus
46
46
  end
47
47
 
48
- Then(/^Stdout contains "([^"]*)"$/) do |txt|
49
- raise "STDOUT doesn't contain '#{txt}':\n#{@stdout}" unless @stdout.include?(txt)
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(/^File "([^"]*)" exists$/) do |name|
53
- raise "The file \"#{name}\" is absent:\n#{`tree -s`}" unless File.exist?(name)
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(/^File "([^"]*)" contains "([^"]*)"$/) do |name, text|
57
- raise "The file \"#{name}\" is absent" unless File.exist?(name)
58
- content = File.read(name)
59
- raise "The file \"#{name}\" doesn't contain \"#{text}\":\n#{content}" unless content.include?(text)
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
- Then(/^Stdout is empty$/) do
63
- raise "STDOUT is not empty:\n#{@stdout}" unless @stdout == ''
64
- end
65
-
66
- Then(/^Exit code is zero$/) do
62
+ Then('Exit code is zero') do
67
63
  raise "Non-zero exit #{@exitstatus}:\n#{@stdout}" unless @exitstatus.zero?
68
64
  end
69
65
 
70
- Then(/^Exit code is not zero$/) do
66
+ Then('Exit code is not zero') do
71
67
  raise 'Zero exit code' if @exitstatus.zero?
72
68
  end
73
69
 
74
- When(/^I run bash with "([^"]*)"$/) do |text|
75
- @stdout = `#{text}`
70
+ When('I run bash with {string}') do |string|
71
+ @stdout = `#{string}`
76
72
  @exitstatus = $CHILD_STATUS.exitstatus
77
73
  end
78
74
 
@@ -81,14 +77,14 @@ When(/^I run bash with:$/) do |text|
81
77
  @exitstatus = $CHILD_STATUS.exitstatus
82
78
  end
83
79
 
84
- When(/^I copy this gem into temp dir$/) do
80
+ When('I copy this gem into temp dir') do
85
81
  FileUtils.copy_entry(@cwd, File.join(@dir, 'jekyll-chatgpt-translate'))
86
82
  end
87
83
 
88
- Given(/^It is Unix$/) do
84
+ Given('It is Unix') do
89
85
  pending if Gem.win_platform?
90
86
  end
91
87
 
92
- Given(/^It is Windows$/) do
88
+ Given('It is Windows') do
93
89
  pending unless Gem.win_platform?
94
90
  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.16'
31
+ s.version = '0.0.18'
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
- total = 0
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,48 +64,49 @@ 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)
71
+ if config['no_download'].nil? && GptTranslate::Ping.new(site, link).found?(dest, version)
72
+ copied += 1
73
+ elsif translated >= threshold
74
+ next
75
+ else
76
+ gpt = GptTranslate::ChatGPT.new(
77
+ key,
78
+ model,
79
+ config['source'] || 'en',
80
+ lang
81
+ )
82
+ foreign = gpt.translate(plain)
83
+ File.write(
84
+ path,
85
+ [
86
+ '---',
87
+ "layout: #{target['layout'] || layout}",
88
+ "title: #{doc['title'].to_json}",
89
+ "description: #{doc['description'].to_json}",
90
+ "permalink: #{link.to_json}",
91
+ "translated-original-url: #{doc.url.to_json}",
92
+ "translated-language: #{lang.to_json}",
93
+ "chatgpt-model: #{model.to_json}",
94
+ '---',
95
+ '',
96
+ foreign,
97
+ '',
98
+ "#{marker} on #{Time.now.strftime('%d/%m/%Y %H:%M')}\n{: .jekyll-chatgpt-translate}"
99
+ ].join("\n")
100
+ )
101
+ site.pages << Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path))
102
+ translated += 1
103
+ Jekyll.logger.info("Translated via ChatGPT: #{path} (#{File.size(path)} bytes)")
104
+ end
74
105
  doc.data["translated-#{lang}-url"] = link
75
106
  doc.data['chatgpt-model'] = model
76
- next if GptTranslate::Ping.new(site, link).found?(dest, version)
77
- gpt = GptTranslate::ChatGPT.new(
78
- key,
79
- model,
80
- config['source'] || 'en',
81
- lang
82
- )
83
- translated = gpt.translate(plain)
84
- File.write(
85
- path,
86
- [
87
- '---',
88
- "layout: #{target['layout'] || layout}",
89
- "title: #{doc['title'].to_json}",
90
- "description: #{doc['description'].to_json}",
91
- "permalink: #{link.to_json}",
92
- "translated-original-url: #{doc.url.to_json}",
93
- "chatgpt-model: #{model.to_json}",
94
- '---',
95
- '',
96
- translated,
97
- '',
98
- "#{marker} on #{Time.now.strftime('%d/%m/%Y %H:%M')}\n{: .jekyll-chatgpt-translate}"
99
- ].join("\n")
100
- )
101
- site.pages << Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path))
102
- total += 1
103
- Jekyll.logger.info("Translated via ChatGPT: #{path}")
104
107
  end
105
- break if total >= threshold
106
108
  end
107
- Jekyll.logger.info("#{total} pages translated in #{(Time.now - start).round(2)}s")
109
+ Jekyll.logger.info("#{translated} pages translated and #{copied} pages copied in #{(Time.now - start).round(2)}s")
108
110
  end
109
111
 
110
112
  private
@@ -74,6 +74,8 @@ class GptTranslate::Plain
74
74
  def codespan(content)
75
75
  if content.start_with?("\n")
76
76
  "```#{content}```"
77
+ elsif content.end_with?("\n")
78
+ "```\n#{content.split("\n", 2)[1]}```"
77
79
  else
78
80
  content
79
81
  end
@@ -23,5 +23,5 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  module GptTranslate
26
- VERSION = '0.0.16'
26
+ VERSION = '0.0.18'
27
27
  end
@@ -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, doc)
38
+ def initialize(config, docs)
39
39
  @config = config
40
- @doc = doc
40
+ @docs = docs
41
+ @pages = []
41
42
  end
42
43
 
43
44
  def posts
44
- FakePosts.new(@doc)
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)
@@ -78,28 +75,23 @@ class GptTranslate::GeneratorTest < Minitest::Test
78
75
  end
79
76
 
80
77
  class FakeDocument
78
+ attr_reader :data
79
+
81
80
  def initialize(path)
82
81
  @path = path
82
+ @data = { 'date' => Time.now, 'title' => 'Hello!' }
83
83
  end
84
84
 
85
85
  def content
86
86
  'Hello, world!'
87
87
  end
88
88
 
89
- def data
90
- {}
89
+ def []=(key, value)
90
+ @data[key] = value
91
91
  end
92
92
 
93
- def []=(key, value); end
94
-
95
93
  def [](key)
96
- if key == 'date'
97
- Time.now
98
- elsif key == 'title'
99
- 'Hello!'
100
- else
101
- ''
102
- end
94
+ @data[key] || ''
103
95
  end
104
96
 
105
97
  def relative_path
@@ -118,12 +110,12 @@ class GptTranslate::GeneratorTest < Minitest::Test
118
110
  class FakePosts
119
111
  attr_reader :config
120
112
 
121
- def initialize(doc)
122
- @doc = doc
113
+ def initialize(docs)
114
+ @docs = docs
123
115
  end
124
116
 
125
117
  def docs
126
- [FakeDocument.new(@doc)]
118
+ @docs.map { |d| FakeDocument.new(d) }
127
119
  end
128
120
  end
129
121
 
@@ -137,18 +129,48 @@ class GptTranslate::GeneratorTest < Minitest::Test
137
129
  'chatgpt-translate' => {
138
130
  'targets' => [
139
131
  {
140
- 'language' => 'cn',
132
+ 'language' => 'zh',
141
133
  'layout' => 'chinese',
142
134
  'permalink' => ':slug.html'
143
135
  }
144
136
  ]
145
137
  }
146
138
  },
147
- FakeDocument.new({})
139
+ [post]
140
+ )
141
+ gen = GptTranslate::Generator.new
142
+ stub_request(:get, 'https://www.yegor256.com/.html').to_return(body: '')
143
+ gen.generate(site)
144
+ assert_equal(1, site.pages.count)
145
+ end
146
+ end
147
+
148
+ def test_threshold_stops
149
+ Dir.mktmpdir do |home|
150
+ post = File.join(home, '2023-01-01-hello.md')
151
+ File.write(post, "---\ntitle: Hello\n---\n\nHello, world!")
152
+ site = FakeSite.new(
153
+ {
154
+ 'chatgpt-translate' => {
155
+ 'threshold' => 1,
156
+ 'targets' => [
157
+ {
158
+ 'language' => 'zh',
159
+ 'permalink' => ':slug.html'
160
+ },
161
+ {
162
+ 'language' => 'fr',
163
+ 'permalink' => ':year/:slug.html'
164
+ }
165
+ ]
166
+ }
167
+ },
168
+ [post, post]
148
169
  )
149
170
  gen = GptTranslate::Generator.new
150
171
  stub_request(:get, 'https://www.yegor256.com/.html').to_return(body: '')
151
172
  gen.generate(site)
173
+ assert_equal(1, site.pages.count)
152
174
  end
153
175
  end
154
176
  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
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.16
4
+ version: 0.0.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko