jp_quest 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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