jekyll-chatgpt-translate 0.0.16 → 0.0.18

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 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