epub-translator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0d3a2097137099f87c52a3cfa7358d9ba79d12288631267913371df4ba8e9eb2
4
+ data.tar.gz: d212b682ab130cca1d1740946b36f25d0537d22b9f85929aab12895c57980e96
5
+ SHA512:
6
+ metadata.gz: 051da38c1df90cc5da8a37541dc404906c7a4f45023088cbf3c72c3bf8525d390ca59c470977929a819f31a27d284a6bab4447ba66699c0a9e2ae183d00f5291
7
+ data.tar.gz: 7b39119783ac14f4487728fd2bcf5fa6c200bc75f7e38e7c18c8d980fea98947892e2eed104cc182cba46fd322375e6279b4cea70aed09531b747cdd326f0f0d
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,91 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7
3
+ Exclude:
4
+ - 'lib/monkey_patch/*'
5
+
6
+ Style/StringLiterals:
7
+ Enabled: false
8
+ EnforcedStyle: double_quotes
9
+
10
+ Style/HashSyntax:
11
+ Enabled: false
12
+
13
+ Style/EmptyElse:
14
+ Enabled: false
15
+
16
+ Style/BlockDelimiters:
17
+ Enabled: false
18
+
19
+ Style/GuardClause:
20
+ Enabled: false
21
+
22
+ Style/SymbolArray:
23
+ Enabled: false
24
+
25
+ Style/TrailingCommaInArguments:
26
+ Enabled: false
27
+
28
+ Style/TrailingCommaInHashLiteral:
29
+ Enabled: false
30
+
31
+ Style/NegatedIf:
32
+ Enabled: false
33
+
34
+ Style/UnlessLogicalOperators:
35
+ Enabled: true
36
+ EnforcedStyle: forbid_logical_operators
37
+
38
+ Style/RescueStandardError:
39
+ Enabled: false
40
+
41
+ Style/StringLiteralsInInterpolation:
42
+ Enabled: true
43
+ EnforcedStyle: double_quotes
44
+
45
+ Style/PercentLiteralDelimiters:
46
+ Enabled: false
47
+
48
+ Style/WordArray:
49
+ Enabled: false
50
+
51
+ Style/AccessorGrouping:
52
+ Enabled: false
53
+
54
+ Style/IfUnlessModifier:
55
+ Enabled: false
56
+
57
+ Style/Documentation:
58
+ Enabled: false
59
+
60
+ Layout/ArgumentAlignment:
61
+ EnforcedStyle: with_fixed_indentation
62
+
63
+ Layout/DotPosition:
64
+ EnforcedStyle: trailing
65
+
66
+ Layout/LineLength:
67
+ Max: 120
68
+
69
+ Layout/MultilineMethodCallIndentation:
70
+ EnforcedStyle: indented
71
+
72
+ Lint/UnusedBlockArgument:
73
+ Enabled: false
74
+
75
+ Metrics/AbcSize:
76
+ Enabled: false
77
+
78
+ Metrics/BlockLength:
79
+ Enabled: false
80
+
81
+ Metrics/MethodLength:
82
+ Enabled: false
83
+
84
+ Metrics/ParameterLists:
85
+ Enabled: false
86
+
87
+ Metrics/PerceivedComplexity:
88
+ Enabled: false
89
+
90
+ Metrics/CyclomaticComplexity:
91
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-07-02
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in epub-translator.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "rubocop", "~> 1.21"
13
+
14
+ gem 'rib'
15
+
16
+ # gem 'natsukantou', path: '../natsukantou'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 lulalala
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Epub::Translator
2
+
3
+ Translate Epub file with machine translation services such as DeepL.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ $ bundle add epub-translator
10
+
11
+ If bundler is not being used to manage dependencies, install the gem by executing:
12
+
13
+ $ gem install epub-translator
14
+
15
+ ## Usage
16
+
17
+ To run it as a command:
18
+
19
+ $ epub-translator [EPUB_FILE]
20
+
21
+ It's a wizard that guides you through setting up a translator configuration...
22
+
23
+ ![Wizard](./docs/eg_wizard_config.png)
24
+
25
+ and then asks you how to translate:
26
+
27
+ ![Wizard](./docs/eg_wizard_translate.png)
28
+
29
+ After you have the translated file, you can choose to interlace it against the original by running:
30
+
31
+ $ epub-interlace [EPUB_FILE_1] [EPUB_FILE_2]
32
+
33
+ It will generate another EPUB file, with translated paragraphs next to their sources.
34
+
35
+ ![Interlaced result](./docs/eg_interlace.png)
36
+
37
+ ## Feature
38
+
39
+ Epub::Translator utilized [Natsukantou](https://gitlab.com/lulalala/natsukantou) gem, which aims to be configurable. For example, it comes with its own glossary middleware, and can handle HTML Ruby Markup. The wizard will ask you which you want to use, but you can read [here](https://gitlab.com/lulalala/natsukantou#middlewares) to learn more about them.
40
+
41
+ ### Translators
42
+
43
+ [List of supported translation services](https://gitlab.com/lulalala/natsukantou#translators)
44
+
45
+ ## Development
46
+
47
+ The XML related operations are purposefully extracted into its own gem: [Natsukantou](https://gitlab.com/lulalala/natsukantou).
48
+
49
+ This gem uses [Oga XML parser](https://gitlab.com/yorickpeterse/oga) and [epub-parser](https://gitlab.com/KitaitiMakoto/epub-parser) (both hosted on GitLab, yay!).
50
+
51
+ ## Contributing
52
+
53
+ Bug reports and merge requests are welcome on GitLab at https://gitlab.com/lulalala/epub-translator.
54
+
55
+ ## License
56
+
57
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
58
+
59
+ The EPUB included in the repository is [The Madman by Khalil Gibran](https://standardebooks.org/ebooks/khalil-gibran/the-madman), which is licensed under [public domain](https://creativecommons.org/publicdomain/zero/1.0/). I use it for testing purposes because it is a very short book.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
Binary file
Binary file
Binary file
data/docs/logo.png ADDED
Binary file
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/epub/translator/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "epub-translator"
7
+ spec.version = Epub::Translator::VERSION
8
+ spec.authors = ["lulalala"]
9
+ spec.email = ["mark@goodlife.tw"]
10
+
11
+ spec.summary = "Tools to translate EPUB books"
12
+ spec.homepage = "http://gitlab.com/lulalala/epub-translator"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.7.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+ spec.metadata["changelog_uri"] = spec.homepage
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency "epub-maker", "~> 0.1.7"
32
+ spec.add_dependency "epub-parser", "~> 0.4.6"
33
+ spec.add_dependency "middleware", "~> 0.1.0"
34
+ spec.add_dependency "natsukantou", "~> 0.1.2"
35
+ spec.add_dependency "tty-prompt", "~> 0.23"
36
+
37
+ spec.add_development_dependency "debug", ">= 1.0.0"
38
+ spec.add_development_dependency "pry"
39
+ # For more information and examples about making a new gem, check out our
40
+ # guide at: https://bundler.io/guides/creating_gem.html
41
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require 'optparse'
6
+ require "natsukantou"
7
+ require "epub/translator/prompt/interlace"
8
+
9
+ optparse = OptionParser.new do |opts|
10
+ opts.banner = "Usage: epub-interlace EPUB_FILE_1 EPUB_FILE_2"
11
+ end
12
+
13
+ optparse.parse!
14
+ if ARGV.empty?
15
+ puts optparse
16
+ exit(-1)
17
+ end
18
+
19
+ prompt = TTY::Prompt.new
20
+
21
+ Epub::Translator::Prompt::Interlace.new.execute(
22
+ prompt: prompt,
23
+ epub_paths: [ARGV[0], ARGV[1]],
24
+ )
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "epub/translator"
6
+ require "epub/translator/prompt/translate"
7
+ require 'optparse'
8
+
9
+ options = {}
10
+
11
+ optparse = OptionParser.new do |opts|
12
+ opts.banner = "Usage: epub-translator [options] EPUB_FILE"
13
+
14
+ opts.on("-c", "--config CONFIG_FILE",
15
+ "Config file generated after the first time this is run") do |v|
16
+ options[:config_path] = v
17
+ end
18
+ end
19
+
20
+ optparse.parse!
21
+ if ARGV.empty?
22
+ puts optparse
23
+ exit(-1)
24
+ end
25
+
26
+ epub_path = ARGV[0]
27
+
28
+ prompt = TTY::Prompt.new
29
+
30
+ Epub::Translator::Prompt::Translate.new.execute(
31
+ prompt: prompt,
32
+ epub_path: epub_path,
33
+ config_path: options[:config_path]
34
+ )
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'epub/maker'
4
+ require 'oga'
5
+ require 'fileutils'
6
+ require 'rake/ext/string'
7
+ require 'tty-prompt'
8
+
9
+ module Epub
10
+ module Translator
11
+ module Prompt
12
+ class Interlace
13
+ attr_reader :epub_paths, :interlace_level, :prompt
14
+
15
+ def execute(
16
+ epub_paths:,
17
+ prompt: ::TTY::Prompt.new
18
+ )
19
+ @epub_paths = epub_paths
20
+ @prompt = prompt
21
+
22
+ epub_paths.each do |path|
23
+ return unless valid_epub?(path) # rubocop:disable Lint/NonLocalExitFromIterator
24
+ end
25
+
26
+ new_epub_path = prompt.ask(
27
+ 'Where do you want to save the result?',
28
+ value: epub_paths.last.pathmap("%X.interlaced%x")
29
+ )
30
+
31
+ @interlace_level = prompt.ask(
32
+ "Please input CSS path of DOM level to interlace:",
33
+ value: 'body p,h1,h2,h3,h4,h5,h6'
34
+ )
35
+
36
+ FileUtils.copy(epub_paths[0], new_epub_path)
37
+
38
+ epub = EPUB::Parser.parse(new_epub_path)
39
+ epub_other = EPUB::Parser.parse(epub_paths[1])
40
+
41
+ epub.each_page_on_spine.select(&:xhtml?).each do |item|
42
+ next if item.content_document.oga.css(interlace_level).empty?
43
+
44
+ item_other = epub_other.manifest[item.id]
45
+ lang_other = item_other.content_document.oga.lang
46
+
47
+ new_dom = item.content_document.oga
48
+ new_dom.interlace(
49
+ item_other.content_document.oga,
50
+ level: interlace_level
51
+ )
52
+
53
+ # Style
54
+ new_dom.lang = nil
55
+ style = Oga::XML::Element.new(name: 'style')
56
+ style.inner_text = "*:lang(#{lang_other}) { color: #999 !IMPORTANT; }"
57
+ new_dom.at_css('head').node_set.push(style)
58
+
59
+ item.content = new_dom.to_xml
60
+ end
61
+ epub.save
62
+
63
+ puts 'Interlacing complete.'
64
+ end
65
+
66
+ private
67
+
68
+ def valid_epub?(path)
69
+ EPUB::Parser.parse(path)
70
+ true
71
+ rescue Errno::ENOENT
72
+ warn 'Epub not found'
73
+ false
74
+ rescue Archive::Zip::UnzipError
75
+ warn 'Not an Epub'
76
+ false
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-prompt'
4
+ require 'rake/ext/string'
5
+ require 'natsukantou/setup/config_load_or_prompt'
6
+ require 'fileutils'
7
+
8
+ module Epub
9
+ module Translator
10
+ module Prompt
11
+ class Translate
12
+ attr_reader :prompt
13
+
14
+ def execute(epub_path:, config_path: nil, prompt: ::TTY::Prompt.new)
15
+ begin
16
+ EPUB::Parser.parse(epub_path)
17
+ rescue Errno::ENOENT
18
+ warn 'Epub not found'
19
+ return
20
+ rescue Archive::Zip::UnzipError
21
+ warn 'Not an Epub'
22
+ return
23
+ end
24
+
25
+ translator = Natsukantou::Setup::ConfigLoadOrPrompt.new.execute(
26
+ prompt: prompt,
27
+ config_path: config_path,
28
+ )
29
+
30
+ lang_from = prompt.ask("Language code to translate from?")
31
+ lang_from = Natsukantou::LanguageCode.new(lang_from)
32
+ lang_to = prompt.ask("Language code to translate to?")
33
+ lang_to = Natsukantou::LanguageCode.new(lang_to)
34
+
35
+ # Make a copy for modification
36
+
37
+ new_epub_path = prompt.ask(
38
+ 'Where do you want to save the result?',
39
+ value: epub_path.pathmap("%X.#{lang_to.code}%x")
40
+ )
41
+ FileUtils.copy(epub_path, new_epub_path)
42
+
43
+ book = EPUB::Parser.parse(new_epub_path)
44
+
45
+ items = book.each_page_on_spine.select(&:xhtml?)
46
+ items_untranslated_index = items.map.with_index do |item, index|
47
+ lang = Natsukantou::LanguageCode.new(item.content_document.oga.lang)
48
+ lang.is?(lang_to) ? nil : index
49
+ end.compact
50
+
51
+ choices = items.map.with_index do |item, index|
52
+ translated = !items_untranslated_index.include?(index)
53
+ [item_label(item, translated), item]
54
+ end.to_h
55
+
56
+ targets = prompt.multi_select(
57
+ "Select items to translate:",
58
+ choices,
59
+ default: items_untranslated_index.map { |index| index + 1 },
60
+ per_page: 30, echo: false
61
+ )
62
+
63
+ targets.each do |item|
64
+ output_translation_progress(item)
65
+
66
+ env = Natsukantou::Env.new(
67
+ dom: item.content_document.oga,
68
+ lang_from: lang_from,
69
+ lang_to: lang_to
70
+ )
71
+ translator.call(env)
72
+
73
+ item.content = env[:dom].to_xml
74
+ item.save
75
+ end
76
+
77
+ if !targets.empty?
78
+ puts 'Translation complete.'
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def output_translation_progress(item)
85
+ puts item.content_document.title
86
+ end
87
+
88
+ def item_label(item, translated)
89
+ text = "#{item.content_document.title} (#{item.id})"
90
+
91
+ if translated
92
+ text += " (already in #{item.content_document.oga.lang})"
93
+ end
94
+
95
+ text
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Epub
4
+ module Translator
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'epub/maker'
4
+ require 'natsukantou'
5
+
6
+ require_relative "translator/version"
7
+
8
+ module Epub
9
+ module Translator
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module Epub
2
+ module Translator
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: epub-translator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - lulalala
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-10-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: epub-maker
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: epub-parser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.6
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.6
41
+ - !ruby/object:Gem::Dependency
42
+ name: middleware
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: natsukantou
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: tty-prompt
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.23'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.23'
83
+ - !ruby/object:Gem::Dependency
84
+ name: debug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 1.0.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 1.0.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description:
112
+ email:
113
+ - mark@goodlife.tw
114
+ executables:
115
+ - epub-interlace
116
+ - epub-translator
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".rspec"
121
+ - ".rubocop.yml"
122
+ - CHANGELOG.md
123
+ - Gemfile
124
+ - LICENSE.txt
125
+ - README.md
126
+ - Rakefile
127
+ - docs/eg_interlace.png
128
+ - docs/eg_wizard_config.png
129
+ - docs/eg_wizard_translate.png
130
+ - docs/logo.png
131
+ - epub-translator.gemspec
132
+ - exe/epub-interlace
133
+ - exe/epub-translator
134
+ - lib/epub/translator.rb
135
+ - lib/epub/translator/prompt/interlace.rb
136
+ - lib/epub/translator/prompt/translate.rb
137
+ - lib/epub/translator/version.rb
138
+ - sig/epub/translator.rbs
139
+ homepage: http://gitlab.com/lulalala/epub-translator
140
+ licenses:
141
+ - MIT
142
+ metadata:
143
+ homepage_uri: http://gitlab.com/lulalala/epub-translator
144
+ source_code_uri: http://gitlab.com/lulalala/epub-translator
145
+ changelog_uri: http://gitlab.com/lulalala/epub-translator
146
+ post_install_message:
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: 2.7.0
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubygems_version: 3.3.7
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: Tools to translate EPUB books
165
+ test_files: []