jekyll-chatgpt-translate 0.0.12 → 0.0.14

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: ed6617e3b8bf46db264f484e1286ea1f91295f4fb47512cb983a6fc59c7922df
4
- data.tar.gz: a0f6c744cd4bf2b2558d7b5568f1ca961a3f854e5264b9d64bb8b24249f03e3a
3
+ metadata.gz: 4b8a34c60ab2378b8dc26ab4b189ff3fa168cbb864c80db92a854d68b58def59
4
+ data.tar.gz: 3654fe32f201e8a155cd5f154129c24350333b6f2459cd86bb511c0acc22c0c1
5
5
  SHA512:
6
- metadata.gz: 2d58f41b76840e524e9f1d72c2a4816b33d9e57ad6b207f54baa226abf68ae6cca848cb8cce80cf1f4502a3ad9b52871db7f7708463bf4101fdbf8c21e5f99bb
7
- data.tar.gz: f85b41ad75176c9989a8a3715fd84485b3a2fcbe03e6cca879b7a201ac4b5745dda0aa16695f26cbaad1ec901b84c6837f983f7a04cee94aa26752d13ef41593
6
+ metadata.gz: ea02f9faba8ffedaa3e25bd428bde6aaba7a7a484bda745cc270a9f97f1a40211b776cff7d5a8c5d297854f107ba311b10dd5885bb6eff8de353f4a8aa9778d6
7
+ data.tar.gz: 6f3cc93df6fff8290f3a6d45bc980a9c5b273154a98ab7c18ddff1571c55dd8c2796e9ef965c3a0df72ff175c42ed54b72f4b4ecb5d2d8bb21864ae3b4aa0c7c
data/.gitignore CHANGED
@@ -2,4 +2,5 @@ Gemfile.lock
2
2
  *.gem
3
3
  .bundle/
4
4
  .DS_Store
5
- coverage/
5
+ coverage/
6
+ _chatgpt-translated/
data/features/cli.feature CHANGED
@@ -40,11 +40,12 @@ Feature: Simple site building
40
40
  Hello, world!
41
41
  """
42
42
  Then I build Jekyll site
43
- Then File "_chatgpt-translated/cn/2023-01-01-hello-cn.md" exists
44
- Then File "_site/2023/01/01/hello.html" exists
45
- Then File "_site/2023/01/01/hello.html" contains "The Chinese: /2023-01-01-hello-chinese.html"
46
- Then File "_site/2023-01-01-hello-chinese.html" exists
47
- Then File "_site/2023-01-01-hello-chinese.html" contains "The original: /2023/01/01/hello.html"
48
- Then File "_site/2023/hello-french.html" exists
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"
45
+ And File "_site/2023/01/01/hello.html" exists
46
+ And File "_site/2023/01/01/hello.html" contains "The Chinese: /2023-01-01-hello-chinese.html"
47
+ And File "_site/2023-01-01-hello-chinese.html" exists
48
+ And File "_site/2023-01-01-hello-chinese.html" contains "The original: /2023/01/01/hello.html"
49
+ And File "_site/2023/hello-french.html" exists
49
50
  And Exit code is zero
50
51
 
@@ -50,10 +50,11 @@ Then(/^Stdout contains "([^"]*)"$/) do |txt|
50
50
  end
51
51
 
52
52
  Then(/^File "([^"]*)" exists$/) do |name|
53
- raise "The file \"#{name}\" is absent:\n#{`tree`}" unless File.exist?(name)
53
+ raise "The file \"#{name}\" is absent:\n#{`tree -s`}" unless File.exist?(name)
54
54
  end
55
55
 
56
56
  Then(/^File "([^"]*)" contains "([^"]*)"$/) do |name, text|
57
+ raise "The file \"#{name}\" is absent" unless File.exist?(name)
57
58
  content = File.read(name)
58
59
  raise "The file \"#{name}\" doesn't contain \"#{text}\":\n#{content}" unless content.include?(text)
59
60
  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.12'
31
+ s.version = '0.0.14'
32
32
  s.license = 'MIT'
33
33
  s.summary = 'Translate Jekyll Pages Through ChatGPT'
34
34
  s.description = [
@@ -92,17 +92,12 @@ through #{@model} in #{(Time.now - start).round(2)}s")
92
92
  end
93
93
 
94
94
  def prompt
95
- if (@source == 'ru') && (@target == 'en')
96
- 'Пожалуйста, переведи этот параграф на английский язык'
97
- elsif (@source == 'en') && (@target == 'ru')
98
- 'Please, translate this paragraph to Russian'
99
- else
100
- [
101
- 'Please, translate this paragraph from',
102
- ISO_639.find_by_code(@source),
103
- 'to',
104
- ISO_639.find_by_code(@target)
105
- ].join(' ')
106
- end
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(' ')
107
102
  end
108
103
  end
@@ -23,6 +23,7 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  require 'jekyll'
26
+ require 'json'
26
27
  require_relative 'chatgpt'
27
28
  require_relative 'permalink'
28
29
  require_relative 'ping'
@@ -60,13 +61,19 @@ class GptTranslate::Generator < Jekyll::Generator
60
61
  plain = GptTranslate::Plain.new(doc.content).to_s
61
62
  config['targets'].each do |target|
62
63
  link = GptTranslate::Permalink.new(doc, target['permalink']).to_path
63
- next if GptTranslate::Ping.new(site, link).found?(doc.path, version)
64
64
  lang = target['language']
65
65
  raise 'Language must be defined for each target' if target.nil?
66
66
  if total >= threshold
67
67
  Jekyll.logger.info("Already generated #{total} pages, that's enough for today")
68
68
  break
69
69
  end
70
+ path = File.join(home, lang, doc.basename.gsub(/\.md$/, "-#{lang}.md"))
71
+ FileUtils.mkdir_p(File.dirname(path))
72
+ File.write(path, '') # in order to surpress warnings in Page ctor
73
+ dest = Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path)).destination(site.dest)
74
+ doc.data["translated-#{lang}-url"] = link
75
+ doc.data['chatgpt-model'] = model
76
+ next if GptTranslate::Ping.new(site, link).found?(dest, version)
70
77
  gpt = GptTranslate::ChatGPT.new(
71
78
  key,
72
79
  model,
@@ -74,17 +81,16 @@ class GptTranslate::Generator < Jekyll::Generator
74
81
  lang
75
82
  )
76
83
  translated = gpt.translate(plain)
77
- path = File.join(home, lang, doc.basename.gsub(/\.md$/, "-#{lang}.md"))
78
- FileUtils.mkdir_p(File.dirname(path))
79
84
  File.write(
80
85
  path,
81
86
  [
82
87
  '---',
83
88
  "layout: #{target['layout'] || layout}",
84
- "title: #{doc.data['title']}",
85
- "permalink: #{link}",
86
- "translated-original-url: #{doc.url}",
87
- "chatgpt-model: #{model}",
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}",
88
94
  '---',
89
95
  '',
90
96
  translated,
@@ -92,8 +98,6 @@ class GptTranslate::Generator < Jekyll::Generator
92
98
  "#{marker}\n{: .jekyll-chatgpt-translate}"
93
99
  ].join("\n")
94
100
  )
95
- doc.data["translated-#{lang}-url"] = link
96
- doc.data['chatgpt-model'] = model
97
101
  site.pages << Jekyll::Page.new(site, site.source, File.dirname(path), File.basename(path))
98
102
  total += 1
99
103
  Jekyll.logger.info("Translated via ChatGPT: #{path}")
@@ -51,21 +51,26 @@ class GptTranslate::Ping
51
51
  home = @site.config['url']
52
52
  return false if home.nil?
53
53
  uri = Iri.new(home).path(@path)
54
- before = Net::HTTP.get_response(URI(uri.to_s))
55
- if before.is_a?(Net::HTTPSuccess)
56
- html = before.body
57
- if html.include?(marker)
58
- Jekyll.logger.info("No need to translate, the page exists at \
59
- #{uri} (#{html.split.count} words), saved to #{file}")
60
- FileUtils.mkdir_p(File.dirname(file))
61
- File.write(file, html)
62
- return true
54
+ begin
55
+ before = Net::HTTP.get_response(URI(uri.to_s))
56
+ if before.is_a?(Net::HTTPSuccess)
57
+ html = before.body
58
+ if html.include?(marker)
59
+ Jekyll.logger.info("No need to translate, the page exists at \
60
+ \"#{uri}\" (#{html.split.count} words), saved to \"#{file}\"")
61
+ FileUtils.mkdir_p(File.dirname(file))
62
+ File.write(file, html)
63
+ return true
64
+ end
65
+ Jekyll.logger.info("Re-translation required for \"#{uri}\"")
66
+ else
67
+ Jekyll.logger.info("The page is absent, will translate \"#{uri}\"")
63
68
  end
64
- Jekyll.logger.info("Re-translation required for #{uri}")
65
- else
66
- Jekyll.logger.info("The page is absent, will translate: #{uri}")
69
+ Jekyll.logger.debug("GET \"#{uri}\": #{before.code}")
70
+ rescue StandardError => e
71
+ Jekyll.logger.debug("Failed to ping \"#{uri}\": #{e.message}")
72
+ Jekyll.logger.info("The page is absent: \"#{uri}\"")
67
73
  end
68
- Jekyll.logger.debug("GET #{uri}: #{before.code}")
69
74
  false
70
75
  end
71
76
  end
@@ -38,28 +38,31 @@ class GptTranslate::Plain
38
38
  end
39
39
 
40
40
  def to_s
41
- @markdown.split(/\n{2,}/).compact.map do |par|
42
- par.gsub!("\n", ' ')
43
- par.gsub!(/\s{2,}/, ' ')
41
+ # To turn compact lists into proper lists
42
+ @markdown.gsub(/([^\n])\n(\s*\*)/, "\\1\n\n\\2").split(/\n{2,}/).compact.map do |par|
43
+ # par.gsub!("\n", ' ')
44
+ par.gsub!("\t", ' ')
45
+ par.gsub!(/ {2,}/, ' ')
44
46
  # Liquid tags are removed, but this implementation is primitive
45
47
  # Seehttps://stackoverflow.com/questions/
46
48
  par.gsub!(/{{[^}]+}}/, '')
47
- par.gsub!(/{%[^%]+%}/, '')
49
+ par.gsub!(/{%.+?%}/, '')
50
+ par.gsub!(/<!--.+?-->/m, '')
48
51
  par.strip!
49
52
  Redcarpet::Markdown.new(Strip).render(par)
50
53
  end.join("\n\n").gsub(/\n{2,}/, "\n\n").strip
51
54
  end
52
55
 
53
- # To ignore/remove Liquid tags.
54
- class NullDrop < Liquid::Drop
55
- def method_missing(*)
56
- nil
57
- end
56
+ # # To ignore/remove Liquid tags.
57
+ # class NullDrop < Liquid::Drop
58
+ # def method_missing(*)
59
+ # nil
60
+ # end
58
61
 
59
- def respond_to_missing?(*)
60
- true
61
- end
62
- end
62
+ # def respond_to_missing?(*)
63
+ # true
64
+ # end
65
+ # end
63
66
 
64
67
  # Markdown to pain text.
65
68
  class Strip < Redcarpet::Render::Base
@@ -23,5 +23,5 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  module GptTranslate
26
- VERSION = '0.0.12'
26
+ VERSION = '0.0.14'
27
27
  end
@@ -23,6 +23,8 @@
23
23
  # SOFTWARE.
24
24
 
25
25
  require 'minitest/autorun'
26
+ require 'tmpdir'
27
+ require 'webmock/minitest'
26
28
  require_relative '../lib/jekyll-chatgpt-translate/generator'
27
29
 
28
30
  # Generator test.
@@ -30,7 +32,123 @@ require_relative '../lib/jekyll-chatgpt-translate/generator'
30
32
  # Copyright:: Copyright (c) 2023 Yegor Bugayenko
31
33
  # License:: MIT
32
34
  class GptTranslate::GeneratorTest < Minitest::Test
35
+ class FakeSite
36
+ attr_reader :config
37
+
38
+ def initialize(config, doc)
39
+ @config = config
40
+ @doc = doc
41
+ end
42
+
43
+ def posts
44
+ FakePosts.new(@doc)
45
+ end
46
+
47
+ def pages
48
+ []
49
+ end
50
+
51
+ def permalink_style
52
+ ''
53
+ end
54
+
55
+ def frontmatter_defaults
56
+ Jekyll::FrontmatterDefaults.new(self)
57
+ end
58
+
59
+ def converters
60
+ [Jekyll::Converters::Markdown.new({ 'markdown_ext' => 'md' })]
61
+ end
62
+
63
+ def source
64
+ ''
65
+ end
66
+
67
+ def dest
68
+ ''
69
+ end
70
+
71
+ def in_theme_dir(base, _foo = nil, _bar = nil)
72
+ base
73
+ end
74
+
75
+ def in_dest_dir(*paths)
76
+ paths[0].dup
77
+ end
78
+ end
79
+
80
+ class FakeDocument
81
+ def initialize(path)
82
+ @path = path
83
+ end
84
+
85
+ def content
86
+ 'Hello, world!'
87
+ end
88
+
89
+ def data
90
+ {}
91
+ end
92
+
93
+ def []=(key, value); end
94
+
95
+ def [](key)
96
+ if key == 'date'
97
+ Time.now
98
+ elsif key == 'title'
99
+ 'Hello!'
100
+ else
101
+ ''
102
+ end
103
+ end
104
+
105
+ def relative_path
106
+ @path
107
+ end
108
+
109
+ def url
110
+ '2023-01-01-hello.html'
111
+ end
112
+
113
+ def basename
114
+ '2023-01-01-hello.md'
115
+ end
116
+ end
117
+
118
+ class FakePosts
119
+ attr_reader :config
120
+
121
+ def initialize(doc)
122
+ @doc = doc
123
+ end
124
+
125
+ def docs
126
+ [FakeDocument.new(@doc)]
127
+ end
128
+ end
129
+
33
130
  def test_simple_scenario
34
- # assert(stdout.include?('DEBUG'))
131
+ Dir.mktmpdir do |home|
132
+ post = File.join(home, '2023-01-01-hello.md')
133
+ File.write(post, "---\ntitle: Hello\n---\n\nHello, world!")
134
+ site = FakeSite.new(
135
+ {
136
+ 'url' => 'https://www.yegor256.com/',
137
+ 'chatgpt-translate' => {
138
+ 'targets' => [
139
+ {
140
+ 'language' => 'cn',
141
+ 'layout' => 'chinese',
142
+ 'permalink' => ':slug.html'
143
+ }
144
+ ]
145
+ }
146
+ },
147
+ FakeDocument.new({})
148
+ )
149
+ gen = GptTranslate::Generator.new
150
+ stub_request(:get, 'https://www.yegor256.com/.html').to_return(body: '')
151
+ gen.generate(site)
152
+ end
35
153
  end
36
154
  end
data/test/test_ping.rb CHANGED
@@ -59,6 +59,15 @@ class GptTranslate::PingTest < Minitest::Test
59
59
  end
60
60
  end
61
61
 
62
+ def test_wrong_address
63
+ WebMock.allow_net_connect!
64
+ site = FakeSite.new({ 'url' => 'https://localhost:1/' })
65
+ ping = GptTranslate::Ping.new(site, '/boom.html')
66
+ Tempfile.open do |f|
67
+ assert(!ping.found?(f, ''))
68
+ end
69
+ end
70
+
62
71
  def test_relative_path
63
72
  assert_raises do
64
73
  GptTranslate::Ping.new({}, '404.html')
data/test/test_plain.rb CHANGED
@@ -47,6 +47,20 @@ class GptTranslate::PlainTest < Minitest::Test
47
47
  )
48
48
  end
49
49
 
50
+ def test_ordered_list
51
+ assert_equal(
52
+ "first\n\nsecond\n\nthird",
53
+ GptTranslate::Plain.new("1. first\n\n2. second\n\n3. third").to_s
54
+ )
55
+ end
56
+
57
+ def test_compact_list
58
+ assert_equal(
59
+ "first\n\nsecond\n\nthird",
60
+ GptTranslate::Plain.new("* first\n* second\n* third").to_s
61
+ )
62
+ end
63
+
50
64
  def test_links
51
65
  assert_equal(
52
66
  'Hello, dude!',
@@ -89,5 +103,20 @@ class GptTranslate::PlainTest < Minitest::Test
89
103
  'Hello, !',
90
104
  GptTranslate::Plain.new('Hello, {% Java %}!').to_s
91
105
  )
106
+ assert_equal(
107
+ 'Hello, !',
108
+ GptTranslate::Plain.new('Hello, {% plantuml "width=50%" %}!').to_s
109
+ )
110
+ end
111
+
112
+ def test_html_comments
113
+ assert_equal(
114
+ 'Hello, !',
115
+ GptTranslate::Plain.new('Hello, <!-- Java -->!').to_s
116
+ )
117
+ assert_equal(
118
+ 'Hello, !',
119
+ GptTranslate::Plain.new("Hello, <!-- \nJava\n -->!").to_s
120
+ )
92
121
  end
93
122
  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.12
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko