jp_quest 0.2.0 → 0.3.0

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.
@@ -1,6 +1,14 @@
1
1
  module JpQuest
2
2
  # インデントを扱うモジュール
3
3
  module IndentHelper
4
+ # インデントを数える
5
+ #
6
+ # @param [String] unstripped_line stripされていない行
7
+ # @return [Integer] インデントの数
8
+ def count_indent(unstripped_line)
9
+ unstripped_line.length - unstripped_line.lstrip.length
10
+ end
11
+
4
12
  # インデントを作成
5
13
  #
6
14
  # @param [Integer] indent インデント数
@@ -16,21 +24,5 @@ module JpQuest
16
24
  def middle_indent(indent)
17
25
  " " * (indent + 1)
18
26
  end
19
-
20
- # インデントを調整
21
- #
22
- # @param [Integer] indent インデント数
23
- # @return [Integer] 調整後のインデント数
24
- def normalize_indent(indent)
25
- dup_indent = 12
26
- if indent > dup_indent
27
- half = 2
28
- half_indent = indent / half
29
- # インデントの数は偶数にする
30
- half_indent.even? ? half_indent : half_indent + 1
31
- else
32
- indent
33
- end
34
- end
35
27
  end
36
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JpQuest
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/jp_quest.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "ruby-progressbar"
4
- require_relative "jp_quest/version"
5
- require_relative "jp_quest/help"
6
- require_relative "jp_quest/performer"
4
+ require_relative "jp_quest/util/version"
5
+ require_relative "jp_quest/util/help"
6
+ require_relative "jp_quest/snbt/performer"
7
7
 
8
8
  # SNBT形式のファイルを翻訳する
9
9
  # 翻訳できるプロパティ
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jp_quest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - milkeclair
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-09 00:00:00.000000000 Z
11
+ date: 2024-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jp_translator_from_gpt
@@ -70,17 +70,17 @@ files:
70
70
  - dist/example.rb
71
71
  - dist/start.bat
72
72
  - lib/jp_quest.rb
73
- - lib/jp_quest/error.rb
74
- - lib/jp_quest/extractor/description.rb
75
- - lib/jp_quest/extractor/subtitle.rb
76
- - lib/jp_quest/extractor/title.rb
77
- - lib/jp_quest/formatter.rb
78
- - lib/jp_quest/help.rb
79
- - lib/jp_quest/indent_helper.rb
80
- - lib/jp_quest/performer.rb
81
- - lib/jp_quest/reader.rb
82
- - lib/jp_quest/version.rb
83
- - lib/jp_quest/writer.rb
73
+ - lib/jp_quest/snbt/description_extractor.rb
74
+ - lib/jp_quest/snbt/formatter.rb
75
+ - lib/jp_quest/snbt/performer.rb
76
+ - lib/jp_quest/snbt/reader.rb
77
+ - lib/jp_quest/snbt/subtitle_extractor.rb
78
+ - lib/jp_quest/snbt/title_extractor.rb
79
+ - lib/jp_quest/snbt/writer.rb
80
+ - lib/jp_quest/util/error.rb
81
+ - lib/jp_quest/util/help.rb
82
+ - lib/jp_quest/util/indent_helper.rb
83
+ - lib/jp_quest/util/version.rb
84
84
  - rake_helper.rb
85
85
  - sig/jp_quest.rbs
86
86
  homepage: https://github.com/milkeclair/jp_quest
@@ -1,112 +0,0 @@
1
- module JpQuest
2
- # SNBT形式のファイルからdescription: ["some description"]を抽出するモジュール
3
- module DescriptionExtractor
4
- # description: ["some description"]を抽出する
5
- #
6
- # @param [String] file_path ファイルのパス
7
- # @return [Array<Hash>] 説明、開始行番号、終了行番号の配列
8
- def extract_descriptions(file_path)
9
- lines = File.readlines(file_path)
10
- extract_from_file(lines)
11
- end
12
-
13
- private
14
-
15
- # ファイルから説明を抽出する
16
- #
17
- # @param [Array<String>] lines 行の配列
18
- # @return [Array<Hash>] 説明、開始行番号、終了行番号、インデントのハッシュの配列
19
- def extract_from_file(lines)
20
- descs = []
21
- desc_content = []
22
- start_line = nil
23
-
24
- lines.each_with_index do |line, index|
25
- indent = count_indent(line)
26
-
27
- # 1行の説明の場合はそのままハッシュに変換
28
- # 複数行の場合は、開始行と終了行の間の説明を抽出する
29
- if oneline?(line)
30
- descs << build_oneline(line, index, indent)
31
- elsif start_line?(line)
32
- start_line = index
33
- elsif middle_line?(line, start_line)
34
- desc_content << line.strip
35
- elsif end_line?(line, start_line)
36
- descs << build_multiline(desc_content, start_line, index, indent)
37
- start_line = nil
38
- desc_content = []
39
- end
40
- end
41
-
42
- descs
43
- end
44
-
45
- # 1行かどうか
46
- #
47
- # @param [String] line 行
48
- # @return [Boolean]
49
- def oneline?(line)
50
- oneline_description?(line)
51
- end
52
-
53
- # 開始行かどうか
54
- #
55
- # @param [String] line 行
56
- # @return [Boolean]
57
- def start_line?(line)
58
- start_of?(line, key: :description)
59
- end
60
-
61
- # 終了行かどうか
62
- #
63
- # @param [String] line 行
64
- # @param [Integer] start_line 開始行番号
65
- # @return [Boolean]
66
- def end_line?(line, start_line)
67
- line.strip.end_with?("]") && start_line
68
- end
69
-
70
- # 中間行かどうか
71
- #
72
- # @param [String] line 行
73
- # @param [Integer] start_line 開始行番号
74
- # @return [Boolean]
75
- def middle_line?(line, start_line)
76
- line.strip != "]" && start_line
77
- end
78
-
79
- # 1行の処理
80
- #
81
- # @param [String] line 行
82
- # @param [Integer] index 行番号
83
- # @param [Integer] indent インデント
84
- # @return [Hash] 説明、開始行番号、終了行番号、インデントのハッシュ
85
- def build_oneline(line, index, indent)
86
- {
87
- type: :description,
88
- text: extract_oneline(line, is_desc: true),
89
- start_line: index,
90
- end_line: index,
91
- indent: indent
92
- }
93
- end
94
-
95
- # 複数行の処理
96
- #
97
- # @param [Array<String>] content 説明の配列
98
- # @param [Integer] start_line 開始行番号
99
- # @param [Integer] index 行番号
100
- # @param [Integer] indent インデント
101
- # @return [Hash] 説明、開始行番号、終了行番号、インデントのハッシュ
102
- def build_multiline(content, start_line, index, indent)
103
- {
104
- type: :description,
105
- text: content.join("\n"),
106
- start_line: start_line,
107
- end_line: index,
108
- indent: indent
109
- }
110
- end
111
- end
112
- end
@@ -1,26 +0,0 @@
1
- module JpQuest
2
- # SNBT形式のファイルからsubtitle: "some subtitle"を抽出するモジュール
3
- module SubtitleExtractor
4
- # subtitle: "some subtitle"を抽出する
5
- #
6
- # @param [String] file_path ファイルのパス
7
- # @return [Array<Hash>] サブタイトルと行番号の配列
8
- def extract_subtitles(file_path)
9
- subtitles = []
10
- lines = File.readlines(file_path)
11
- lines.each_with_index do |line, index|
12
- next unless start_of?(line, key: :subtitle)
13
-
14
- subtitles << {
15
- type: :subtitle,
16
- text: extract_oneline(line),
17
- start_line: index,
18
- end_line: index,
19
- indent: count_indent(line)
20
- }
21
- end
22
-
23
- subtitles
24
- end
25
- end
26
- end
@@ -1,26 +0,0 @@
1
- module JpQuest
2
- # SNBT形式のファイルからtitle: "some title"を抽出するモジュール
3
- module TitleExtractor
4
- # title: "some title"を抽出する
5
- #
6
- # @param [String] file_path ファイルのパス
7
- # @return [Array<Hash>] タイトルと行番号の配列
8
- def extract_titles(file_path)
9
- titles = []
10
- lines = File.readlines(file_path)
11
- lines.each_with_index do |line, index|
12
- next unless start_of?(line, key: :title)
13
-
14
- titles << {
15
- type: :title,
16
- text: extract_oneline(line),
17
- start_line: index,
18
- end_line: index,
19
- indent: count_indent(line)
20
- }
21
- end
22
-
23
- titles
24
- end
25
- end
26
- end
@@ -1,148 +0,0 @@
1
- require_relative "indent_helper"
2
-
3
- module JpQuest
4
- # 翻訳された内容をSNBT形式に整形するクラス
5
- class Formatter
6
- include IndentHelper
7
-
8
- # 保存できるように整形
9
- #
10
- # @param [Hash] content コンテンツ
11
- # @param [String] indent インデント
12
- # @return [String] SNBT形式に整形したコンテンツ
13
- def format_overwritable_lines(content, indent)
14
- full_lines = add_missing_lines(content)
15
- format_for_snbt(full_lines, indent, content)
16
- end
17
-
18
- # SNBT形式に整形
19
- #
20
- # @param [Array<String>] lines 行
21
- # @param [String] indent インデント
22
- # @param [Hash] content コンテンツ
23
- # @return [String] SNBT形式に整形した行
24
- def format_for_snbt(lines, indent, content)
25
- lines = prepare_lines_for_snbt(lines, content)
26
- formatted_lines = format_lines(lines, indent, content)
27
- "#{indent}#{content[:type]}: #{formatted_lines}"
28
- end
29
-
30
- # 不足している行を追加
31
- #
32
- # @param [Hash] content コンテンツ
33
- # @return [void]
34
- def add_missing_lines(content)
35
- required_lines = extract_required_line_counts(content)
36
- lines = content[:text].split("\n")
37
-
38
- while lines.length < required_lines
39
- lines << empty_middle_line(content[:indent])
40
- end
41
-
42
- lines
43
- end
44
-
45
- private
46
-
47
- # 不要な文字を削除する
48
- #
49
- # @param [String] lines 行
50
- # @param [Hash] content コンテンツ
51
- # @return [Array<String>] 不要な文字を削除した行
52
- def prepare_lines_for_snbt(lines, content)
53
- lines.map! { |line| delete_quotes(line) }
54
- lines.map!(&:strip) unless content[:type] == :description
55
- lines
56
- end
57
-
58
- # SNBT形式に変換しやすい形に整形
59
- #
60
- # @param [Array<String>] lines 行
61
- # @param [String] indent インデント
62
- # @param [Hash] content コンテンツ
63
- # @return [String] SNBT形式に変換しやすく整形した行
64
- def format_lines(lines, indent, content)
65
- if lines.length == 1
66
- content[:type] == :description ? "[#{lines[0].strip}]" : lines[0].strip.to_s
67
- else
68
- # [
69
- # "Hello"
70
- # "World"
71
- # ]
72
- mid_indent = middle_indent(content[:indent])
73
- lines = lines.map { |line| "#{mid_indent}#{line.strip}" }
74
- "[\n#{lines.join("\n")}\n#{indent}]"
75
- end
76
- end
77
-
78
- # 必要な行数を抽出
79
- #
80
- # @param [Hash] content コンテンツ
81
- # @return [Integer] 必要な行数
82
- def extract_required_line_counts(content)
83
- # start_lineが1、end_lineが5の場合、必要な行数はブラケットを抜いて3行
84
- # そのため、(end(5) - start(1)) + 1行 - ブラケット2行 = 3行となる
85
- line_offset, without_brackets = 1, 2
86
-
87
- (content[:end_line] - content[:start_line]) + line_offset - without_brackets
88
- end
89
-
90
- # 中間行の空行を作成
91
- #
92
- # @return [String] 空行
93
- def empty_middle_line(indent)
94
- middle_indent(indent).to_s
95
- end
96
-
97
- # 不要な引用符を削除
98
- #
99
- # @param [String] line 行
100
- # @return [String] 不要な引用符を削除した行
101
- def delete_quotes(line)
102
- line = delete_dup_quotes(line)
103
- line = delete_jp_quotes(line)
104
- delete_curved_quotes(line)
105
- end
106
-
107
- # 不要なダブルクオートを削除
108
- #
109
- # @param [String] line 行
110
- # @return [String] 不要なダブルクオートを削除した行
111
- def delete_dup_quotes(line)
112
- # 行間にある余計なダブルクオートを削除するため、一度全てのダブルクオートを削除している
113
- # 全て削除したあと、行頭、行末にダブルクオートを追加する
114
- line = line.gsub('"', "")
115
- line_start = /^(\s*)/
116
- line = line.sub(line_start, "\"")
117
- "#{line}\""
118
- end
119
-
120
- # 不要な鍵括弧を削除
121
- #
122
- # @param [String] line 行
123
- # @return [String] 不要な鍵括弧を削除した行
124
- def delete_jp_quotes(line)
125
- # 「Hello」
126
- deletable_reg = /「.*」/
127
- return line unless line.match?(deletable_reg)
128
-
129
- jp_quotes = [/「/, /」/]
130
- jp_quotes.each { |quo| line = line.gsub(quo, "") }
131
- line
132
- end
133
-
134
- # 不要な曲がった引用符を削除
135
- #
136
- # @param [String] line 行
137
- # @return [String] 不要な曲がった引用符を削除した行
138
- def delete_curved_quotes(line)
139
- # “Hello”
140
- deletable_reg = /“.*”/
141
- return line unless line.match?(deletable_reg)
142
-
143
- curved_quotes = [/“/, /”/]
144
- curved_quotes.each { |quo| line = line.gsub(quo, "") }
145
- line
146
- end
147
- end
148
- end
@@ -1,74 +0,0 @@
1
- require "jp_translator_from_gpt"
2
- require_relative "help"
3
- require_relative "error"
4
- require_relative "reader"
5
- require_relative "writer"
6
-
7
- module JpQuest
8
- # 翻訳を実行するクラス
9
- # JpTranslatorFromGptを使用して翻訳を行う
10
- class Performer
11
- # @param [Boolean] output_logs ログを出力するか
12
- # @param [Array<String>] except_words 翻訳しない単語
13
- # @param [String] exchange_language どの言語に翻訳するか
14
- # @param [Boolean] display_help ヘルプを表示するか
15
- # @return [JpQuest::Performer]
16
- def initialize(output_logs: true, except_words: [], exchange_language: "japanese", display_help: true)
17
- @translator = JpTranslatorFromGpt::Translator.new(
18
- output_logs: output_logs,
19
- except_words: except_words,
20
- exchange_language: exchange_language
21
- )
22
- @reader = nil
23
- @writer = nil
24
- @progress_bar = nil
25
-
26
- JpQuest.help if display_help
27
- end
28
-
29
- # ファイルを翻訳して出力する
30
- #
31
- # @param [String] file_path ファイルのパス
32
- # @return [void]
33
- def perform(file_path)
34
- file_path = File.expand_path(file_path)
35
- validate_path(file_path)
36
-
37
- @reader, @writer = JpQuest::Reader.new(file_path), JpQuest::Writer.new(file_path)
38
- results = @reader.extract_all.flatten
39
- @progress_bar = JpQuest.create_progress_bar(file_path, results.length)
40
-
41
- results.each do |result|
42
- result[:text] = @translator.translate(result[:text])
43
- @writer.overwrites(result)
44
- @progress_bar.increment
45
- end
46
-
47
- puts "Completed!"
48
- end
49
-
50
- # ディレクトリ内のファイルを翻訳して出力する
51
- #
52
- # @param [String] dir_path ディレクトリのパス
53
- # @return [void]
54
- def perform_directory(dir_path: "quests")
55
- dir_path = File.expand_path(dir_path)
56
- validate_path(dir_path)
57
-
58
- # **でサブディレクトリも含めて取得
59
- Dir.glob("#{dir_path}/**/*.snbt").each do |file_path|
60
- perform(file_path)
61
- end
62
- end
63
-
64
- # ファイルの存在を確認する
65
- # 存在しない場合はPathNotFoundErrorを投げる
66
- #
67
- # @param [String] path ファイルのパス
68
- # @return [void]
69
- def validate_path(path)
70
- path = File.expand_path(path)
71
- raise JpQuest::PathNotFoundError.new(path) unless File.exist?(path)
72
- end
73
- end
74
- end
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "extractor/title"
4
- require_relative "extractor/subtitle"
5
- require_relative "extractor/description"
6
-
7
- module JpQuest
8
- # SNBT形式のファイルからタイトル、サブタイトル、説明を抽出するクラス
9
- class Reader
10
- include TitleExtractor
11
- include SubtitleExtractor
12
- include DescriptionExtractor
13
-
14
- DESC_START_LENGTH = 14
15
- DESC_END_LENGTH = -2
16
-
17
- # @param [String] file_path ファイルのパス
18
- # @return [JpQuest::Reader]
19
- def initialize(file_path)
20
- @file_path = file_path
21
- end
22
-
23
- # タイトル、サブタイトル、説明を抽出する
24
- #
25
- # @return [Array<Array<Hash>>] タイトル、サブタイトル、説明の配列
26
- def extract_all
27
- titles = extract_titles
28
- subtitles = extract_subtitles
29
- descriptions = extract_descriptions
30
-
31
- [titles, subtitles, descriptions]
32
- end
33
-
34
- # タイトルを抽出する
35
- #
36
- # @return [Array<Hash>] タイトル、行番号、インデントの配列
37
- def extract_titles
38
- super(@file_path)
39
- end
40
-
41
- # サブタイトルを抽出する
42
- #
43
- # @return [Array<Hash>] サブタイトル、行番号、インデントの配列
44
- def extract_subtitles
45
- super(@file_path)
46
- end
47
-
48
- # 説明を抽出する
49
- #
50
- # @return [Array<Hash>] 説明、開始行、終了行、インデントの配列
51
- def extract_descriptions
52
- super(@file_path)
53
- end
54
-
55
- # インデントを数える
56
- #
57
- # @param [String] unstripped_line stripされていない行
58
- # @return [Integer] インデントの数
59
- def count_indent(unstripped_line)
60
- unstripped_line.length - unstripped_line.lstrip.length
61
- end
62
-
63
- # title: "some title"のような形式から、"some title"を抽出する
64
- # 説明の場合は、description: ["some description"]のような形式から、"some description"を抽出する
65
- #
66
- # @param [String] line 行
67
- # @param [Boolean] is_desc 説明かどうか
68
- # @return [String] タイトル or サブタイトル or 説明
69
- def extract_oneline(line, is_desc: false)
70
- stripped_line = line.strip
71
- return stripped_line.split(":", 2)[1] unless is_desc
72
-
73
- if oneline_description?(line)
74
- stripped_line[DESC_START_LENGTH..DESC_END_LENGTH]
75
- elsif start_of?(line, key: :description)
76
- stripped_line.split("[", 2)[1]
77
- else
78
- stripped_line.split("]", 2)[0]
79
- end
80
- end
81
-
82
- # 1行の説明かどうか
83
- #
84
- # @param [String] line 行
85
- # @return [Boolean]
86
- def oneline_description?(line)
87
- stripped_line = line.strip
88
- start_of?(line, key: :description) && stripped_line.end_with?("]")
89
- end
90
-
91
- # どのコンテンツの開始行か
92
- #
93
- # @param [String] line 行
94
- # @param [Symbol] key コンテンツの種類
95
- # @return [Boolean]
96
- def start_of?(line, key:)
97
- stripped_line = line.strip
98
- sections = {
99
- title: "title:",
100
- subtitle: "subtitle:",
101
- description: "description: ["
102
- }
103
-
104
- stripped_line.start_with?(sections[key])
105
- end
106
- end
107
- end
@@ -1,72 +0,0 @@
1
- require_relative "error"
2
- require_relative "formatter"
3
- require_relative "indent_helper"
4
-
5
- module JpQuest
6
- # 翻訳された内容を整形して出力するクラス
7
- class Writer
8
- include IndentHelper
9
-
10
- # @param [String] file_path ファイルのパス
11
- # @return [JpQuest::Writer]
12
- def initialize(file_path)
13
- @input_file_path = file_path
14
- # questsとすると、quests.snbtも変換されてしまうので、quests/とする
15
- @output_file_path = file_path.gsub("quests/", "output/quests/")
16
- @formatter = JpQuest::Formatter.new
17
- end
18
-
19
- # 翻訳された内容でoutput_file_pathを上書きする
20
- #
21
- # @param [Hash] translated_contents 翻訳された内容
22
- # @return [void]
23
- def overwrites(translated_contents)
24
- # ディレクトリが存在しない場合は作成
25
- FileUtils.mkdir_p(File.dirname(@output_file_path))
26
-
27
- # 一度上書きしている場合は上書き後のファイルを読み込む
28
- # 常に上書き前のファイルを読み込むと、前回の上書きが消えてしまう
29
- lines = File.readlines(first_overwrite? ? @input_file_path : @output_file_path)
30
- formatted_lines = overwrite_lines(lines, translated_contents)
31
-
32
- File.open(@output_file_path, "w") do |file|
33
- file.puts formatted_lines
34
- end
35
-
36
- handle_line_count_error(@output_file_path, lines.length)
37
- end
38
-
39
- private
40
-
41
- # 行を上書きする
42
- #
43
- # @param [Array<String>] lines 行
44
- # @param [Hash] content コンテンツ
45
- # @return [Array<String>] 上書きされた行
46
- def overwrite_lines(lines, content)
47
- indent = create_indent(content[:indent])
48
- overwritable_lines = @formatter.format_overwritable_lines(content, indent)
49
- lines[content[:start_line]..content[:end_line]] = overwritable_lines.split("\n")
50
- lines
51
- end
52
-
53
- # 最初の上書きかどうか
54
- #
55
- # @return [Boolean] 最初の上書きかどうか
56
- def first_overwrite?
57
- !File.exist?(@output_file_path)
58
- end
59
-
60
- # 翻訳前と翻訳後の行数が異なる場合はエラーを発生させる
61
- #
62
- # @param [String] output_file_path 出力ファイルのパス
63
- # @param [Integer] before_line_count 翻訳前の行数
64
- # @return [void]
65
- def handle_line_count_error(output_file_path, before_line_count)
66
- after_line_count = File.readlines(output_file_path).length
67
- return if before_line_count == after_line_count
68
-
69
- raise JpQuest::InvalidLineCountError.new(before_line_count, after_line_count)
70
- end
71
- end
72
- end
File without changes