modpack_localizer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.licensed.yml +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +52 -0
- data/CHANGELOG.md +43 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/LICENSE.txt +21 -0
- data/README.md +79 -0
- data/Rakefile +39 -0
- data/dist/Gemfile +3 -0
- data/dist/example.rb +10 -0
- data/dist/start.bat +4 -0
- data/lib/modpack_localizer/jar/performer.rb +147 -0
- data/lib/modpack_localizer/jar/reader.rb +158 -0
- data/lib/modpack_localizer/jar/writer.rb +121 -0
- data/lib/modpack_localizer/snbt/description_extractor.rb +118 -0
- data/lib/modpack_localizer/snbt/formatter.rb +196 -0
- data/lib/modpack_localizer/snbt/performer.rb +92 -0
- data/lib/modpack_localizer/snbt/reader.rb +102 -0
- data/lib/modpack_localizer/snbt/subtitle_extractor.rb +32 -0
- data/lib/modpack_localizer/snbt/title_extractor.rb +32 -0
- data/lib/modpack_localizer/snbt/writer.rb +74 -0
- data/lib/modpack_localizer/util/error.rb +47 -0
- data/lib/modpack_localizer/util/help.rb +156 -0
- data/lib/modpack_localizer/util/indent_helper.rb +28 -0
- data/lib/modpack_localizer/util/version.rb +5 -0
- data/lib/modpack_localizer.rb +66 -0
- data/rake_helper.rb +29 -0
- data/sig/jp_quest.rbs +4 -0
- metadata +187 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
require "zip"
|
2
|
+
require "json"
|
3
|
+
require "countries"
|
4
|
+
require "iso-639"
|
5
|
+
|
6
|
+
module ModpackLocalizer
|
7
|
+
module JAR
|
8
|
+
# .jarファイルから言語ファイルの内容とメタデータを抽出するクラス
|
9
|
+
class Reader
|
10
|
+
# 例: ja_jp
|
11
|
+
LOCALE_CODE_REGEX = /\A[a-z]{2,3}_[a-z]{2,3}\z/
|
12
|
+
|
13
|
+
# performerに渡すレスポンス
|
14
|
+
#
|
15
|
+
# @param [Boolean] need_translation 翻訳が必要か
|
16
|
+
# @param [Hash] json 言語ファイルの内容
|
17
|
+
# @param [String] file_name ファイル名
|
18
|
+
# @param [String] locale_code ロケールコード
|
19
|
+
# @param [String] mod_name mod名
|
20
|
+
# @return [LangData]
|
21
|
+
LangData = Struct.new(
|
22
|
+
:need_translation, :json, :file_name, :locale_code, :mod_name
|
23
|
+
)
|
24
|
+
|
25
|
+
# locale_codeが渡された場合、languageとcountry_nameは不要
|
26
|
+
#
|
27
|
+
# @param [String] file_path ファイルのパス
|
28
|
+
# @param [String] language 言語
|
29
|
+
# @param [String] country_name 国
|
30
|
+
# @param [String] locale_code ロケールコード
|
31
|
+
# @return [ModpackLocalizer::JAR::Reader]
|
32
|
+
def initialize(file_path, language, country_name, locale_code)
|
33
|
+
@file_path, @language, @country_name = file_path, language, country_name
|
34
|
+
@locale_code =
|
35
|
+
locale_code&.downcase || make_locale_code(get_language_code(language), get_country_code(country_name))
|
36
|
+
# 引数としてlocale_codeが渡された時はチェックしない
|
37
|
+
# brb(Netherlands)のような、正規表現にマッチしないlocale_codeが存在するため(brbはISO 639-3でqbr_NL)
|
38
|
+
validate_locale_code(@locale_code) unless locale_code
|
39
|
+
end
|
40
|
+
|
41
|
+
# 言語ファイルの内容とメタデータを抽出する
|
42
|
+
#
|
43
|
+
# @return [LangData] 言語ファイルの内容とメタデータ
|
44
|
+
# @raise [ModpackLocalizer::InvalidRegionCodeError] locale_codeが不正な場合
|
45
|
+
def extract_lang_json_and_meta_data
|
46
|
+
Zip::File.open(@file_path) do |jar|
|
47
|
+
# 対象の言語ファイルが存在する場合は翻訳が必要ない
|
48
|
+
target_lang_file = find_lang_json(jar, @locale_code)
|
49
|
+
if target_lang_file
|
50
|
+
return LangData.new(
|
51
|
+
false, {}, target_lang_file, nil, extract_mod_name(target_lang_file)
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
lang_file = find_lang_json(jar, "en_us")
|
56
|
+
raw_json = JSON.parse(lang_file.get_input_stream.read)
|
57
|
+
|
58
|
+
LangData.new(
|
59
|
+
true, except_comment(raw_json), lang_file, @locale_code, extract_mod_name(lang_file)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# フルパスからファイル名を抽出する
|
65
|
+
#
|
66
|
+
# @param [Zip::Entry] file ファイル
|
67
|
+
# @return [String] ファイル名
|
68
|
+
def extract_file_name(file)
|
69
|
+
file.name.split("/").last
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# ロケールコードのバリデーション
|
75
|
+
#
|
76
|
+
# @param [String] locale_code ロケールコード
|
77
|
+
# @return [Boolean]
|
78
|
+
def validate_locale_code(locale_code)
|
79
|
+
return if locale_code.match(LOCALE_CODE_REGEX)
|
80
|
+
|
81
|
+
raise ModpackLocalizer::InvalidRegionCodeError.new(locale_code)
|
82
|
+
end
|
83
|
+
|
84
|
+
# .jar内の言語ファイルを取得する
|
85
|
+
#
|
86
|
+
# @param [Zip::File] opened_jar .jarファイル
|
87
|
+
# @param [String] locale_code ロケールコード
|
88
|
+
# @return [Zip::Entry] 言語ファイル
|
89
|
+
def find_lang_json(opened_jar, locale_code)
|
90
|
+
lang_files = opened_jar.glob("**/lang/*.json")
|
91
|
+
|
92
|
+
lang_files.find { |entry| target_locale_file?(entry, locale_code) }
|
93
|
+
end
|
94
|
+
|
95
|
+
# JSONからコメントを除外する
|
96
|
+
#
|
97
|
+
# @param [Hash] hash JSONのハッシュ
|
98
|
+
# @return [Hash] コメントを除外したハッシュ
|
99
|
+
def except_comment(hash)
|
100
|
+
hash.except("_comment")
|
101
|
+
end
|
102
|
+
|
103
|
+
# 対象の言語ファイルかどうか
|
104
|
+
#
|
105
|
+
# @param [Zip::Entry] file ファイル
|
106
|
+
# @param [String] locale_code ロケールコード
|
107
|
+
# @return [Boolean]
|
108
|
+
def target_locale_file?(file, locale_code)
|
109
|
+
file_name = extract_file_name(file)
|
110
|
+
|
111
|
+
file_name.include?("#{locale_code}.json")
|
112
|
+
end
|
113
|
+
|
114
|
+
# フルパスからmod名を抽出する
|
115
|
+
#
|
116
|
+
# @param [Zip::Entry] file ファイル
|
117
|
+
# @return [String] mod名
|
118
|
+
def extract_mod_name(file)
|
119
|
+
file.name.split("/").last(3).first
|
120
|
+
end
|
121
|
+
|
122
|
+
# ロケールコードを生成する
|
123
|
+
#
|
124
|
+
# @param [String] lang_code 言語コード
|
125
|
+
# @param [String] country_code 国コード
|
126
|
+
# @return [String] ロケールコード
|
127
|
+
def make_locale_code(lang_code, country_code)
|
128
|
+
"#{lang_code}_#{country_code}"
|
129
|
+
end
|
130
|
+
|
131
|
+
# 言語名から言語コードを取得する
|
132
|
+
#
|
133
|
+
# @param [String] language_name 言語名
|
134
|
+
# @return [String] 言語コード
|
135
|
+
def get_language_code(language_name)
|
136
|
+
result = ISO_639.find_by_english_name(optimize(language_name))
|
137
|
+
result&.alpha2&.downcase || result&.alpha3&.downcase
|
138
|
+
end
|
139
|
+
|
140
|
+
# 国名から国コードを取得する
|
141
|
+
#
|
142
|
+
# @param [String] country_name 国名
|
143
|
+
# @return [String] 国コード
|
144
|
+
def get_country_code(country_name)
|
145
|
+
result = ISO3166::Country.find_country_by_any_name(optimize(country_name))
|
146
|
+
result&.alpha2&.downcase || result&.alpha3&.downcase
|
147
|
+
end
|
148
|
+
|
149
|
+
# ISO関連のgemが受け付けられる形式に変換
|
150
|
+
#
|
151
|
+
# @param [String] str 文字列
|
152
|
+
# @return [String] 変換後の文字列
|
153
|
+
def optimize(str)
|
154
|
+
str.gsub("_", " ").split.map(&:capitalize).join(" ")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "zip"
|
2
|
+
|
3
|
+
module ModpackLocalizer
|
4
|
+
module JAR
|
5
|
+
# .jarファイルを翻訳してリソースパックを作成するクラス
|
6
|
+
class Writer
|
7
|
+
# 1.19.2
|
8
|
+
PACK_FORMAT = 9
|
9
|
+
|
10
|
+
# @return [ModpackLocalizer::JAR::Writer]
|
11
|
+
def initialize
|
12
|
+
@output_path_base = "output/mods/modpack_localizer"
|
13
|
+
@output_path = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# リソースパックを作成する
|
17
|
+
#
|
18
|
+
# @param [ModpackLocalizer::JAR::Reader::LangData] results 言語ファイルの内容とメタデータ
|
19
|
+
# @return [void]
|
20
|
+
def make_resource_pack(results)
|
21
|
+
results.file_name = replace_default_locale_code(results.file_name.name, results.locale_code)
|
22
|
+
@output_path = merge_base_path(results.file_name)
|
23
|
+
|
24
|
+
make_file(@output_path, results.json)
|
25
|
+
mcmeta_info = mcmeta(PACK_FORMAT, results.locale_code)
|
26
|
+
make_file(mcmeta_info[:file_path], mcmeta_info[:meta_data])
|
27
|
+
|
28
|
+
zipping_resource_pack
|
29
|
+
end
|
30
|
+
|
31
|
+
# zipにする前に作成したリソースパックを削除する
|
32
|
+
# return [void]
|
33
|
+
def remove_before_zipping_directory
|
34
|
+
FileUtils.rm_rf(@output_path_base)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# ファイル名のロケールコードを指定のロケールコードに置き換える
|
40
|
+
#
|
41
|
+
# @param [String] file_name ファイル名
|
42
|
+
# @param [String] locale_code ロケールコード
|
43
|
+
# @return [String] ロケールコードが置き換わったファイル名
|
44
|
+
def replace_default_locale_code(file_name, locale_code)
|
45
|
+
file_name.gsub("en_us", locale_code)
|
46
|
+
end
|
47
|
+
|
48
|
+
# ファイルの出力先のパスを生成する
|
49
|
+
#
|
50
|
+
# @param [String] file_name ファイル名
|
51
|
+
# @return [String] ファイルの出力先のパス
|
52
|
+
def merge_base_path(file_name)
|
53
|
+
"#{@output_path_base}/#{file_name}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# ファイルを作成する
|
57
|
+
#
|
58
|
+
# @param [String] path ファイルのパス
|
59
|
+
# @param [Hash] data ファイルに書き込むデータ
|
60
|
+
# @return [void]
|
61
|
+
def make_file(path, data = nil)
|
62
|
+
expand_path = File.expand_path(path)
|
63
|
+
dir_path = File.dirname(expand_path)
|
64
|
+
|
65
|
+
FileUtils.mkdir_p(dir_path) unless File.directory?(dir_path)
|
66
|
+
|
67
|
+
File.open(expand_path, "w") do |file|
|
68
|
+
file.puts(data.nil? ? "" : JSON.pretty_generate(data))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# rubocop:disable Lint/SymbolConversion
|
73
|
+
# pack.mcmetaを作成するための情報
|
74
|
+
#
|
75
|
+
# @param [Integer] pack_format リソースパックのバージョン
|
76
|
+
# @param [String] locale_code ロケールコード
|
77
|
+
# @return [Hash] pack.mcmetaの情報
|
78
|
+
def mcmeta(pack_format, locale_code)
|
79
|
+
meta_data = {
|
80
|
+
"pack": {
|
81
|
+
"pack_format": pack_format,
|
82
|
+
"description": "Localized for #{locale_code} by ModpackLocalizer"
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
file_path = "#{@output_path_base}/pack.mcmeta"
|
87
|
+
{ file_path: file_path, meta_data: meta_data }
|
88
|
+
end
|
89
|
+
# rubocop:enable Lint/SymbolConversion
|
90
|
+
|
91
|
+
# リソースパックをzipにする
|
92
|
+
#
|
93
|
+
# @return [void]
|
94
|
+
def zipping_resource_pack
|
95
|
+
Zip::File.open("#{@output_path_base}.zip", create: true) do |zip|
|
96
|
+
extract_inner_files { |file| add_file_to_zip(zip, file) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# ディレクトリ内のファイルをzipに追加する
|
101
|
+
#
|
102
|
+
# @yield [String] file ファイル
|
103
|
+
# @return [void]
|
104
|
+
def extract_inner_files(&block)
|
105
|
+
Dir.glob("#{@output_path_base}/**/*").each do |file|
|
106
|
+
block.call(file)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# ファイルをzipに追加する
|
111
|
+
#
|
112
|
+
# @param [Zip::File] zip zipファイル
|
113
|
+
# @param [String] file ファイル
|
114
|
+
# @return [void]
|
115
|
+
def add_file_to_zip(zip, file)
|
116
|
+
entry_name = file.sub("#{@output_path_base}/", "")
|
117
|
+
zip.add(entry_name, file) unless zip.find_entry(entry_name)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require_relative "../util/indent_helper"
|
2
|
+
|
3
|
+
module ModpackLocalizer
|
4
|
+
module SNBT
|
5
|
+
# SNBT形式のファイルからdescription: ["some description"]を抽出するモジュール
|
6
|
+
module DescriptionExtractor
|
7
|
+
include IndentHelper
|
8
|
+
|
9
|
+
# description: ["some description"]を抽出する
|
10
|
+
#
|
11
|
+
# @param [String] file_path ファイルのパス
|
12
|
+
# @return [Array<Hash>] 説明、開始行番号、終了行番号の配列
|
13
|
+
def extract_descriptions(file_path)
|
14
|
+
lines = File.readlines(file_path)
|
15
|
+
extract_from_file(lines)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# ファイルから説明を抽出する
|
21
|
+
#
|
22
|
+
# @param [Array<String>] lines 行の配列
|
23
|
+
# @return [Array<Hash>] 説明、開始行番号、終了行番号、インデントのハッシュの配列
|
24
|
+
def extract_from_file(lines)
|
25
|
+
descs = []
|
26
|
+
desc_content = []
|
27
|
+
start_line = nil
|
28
|
+
|
29
|
+
lines.each_with_index do |line, index|
|
30
|
+
indent = count_indent(line)
|
31
|
+
|
32
|
+
# 1行の説明の場合はそのままハッシュに変換
|
33
|
+
# 複数行の場合は、開始行と終了行の間の説明を抽出する
|
34
|
+
if oneline?(line)
|
35
|
+
descs << build_oneline(line, index, indent)
|
36
|
+
elsif start_line?(line)
|
37
|
+
start_line = index
|
38
|
+
elsif middle_line?(line, start_line)
|
39
|
+
desc_content << line.strip
|
40
|
+
elsif end_line?(line, start_line)
|
41
|
+
descs << build_multiline(desc_content, start_line, index, indent)
|
42
|
+
start_line = nil
|
43
|
+
desc_content = []
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
descs
|
48
|
+
end
|
49
|
+
|
50
|
+
# 1行かどうか
|
51
|
+
#
|
52
|
+
# @param [String] line 行
|
53
|
+
# @return [Boolean]
|
54
|
+
def oneline?(line)
|
55
|
+
oneline_description?(line)
|
56
|
+
end
|
57
|
+
|
58
|
+
# 開始行かどうか
|
59
|
+
#
|
60
|
+
# @param [String] line 行
|
61
|
+
# @return [Boolean]
|
62
|
+
def start_line?(line)
|
63
|
+
start_of?(line, key: :description)
|
64
|
+
end
|
65
|
+
|
66
|
+
# 終了行かどうか
|
67
|
+
#
|
68
|
+
# @param [String] line 行
|
69
|
+
# @param [Integer] start_line 開始行番号
|
70
|
+
# @return [Boolean]
|
71
|
+
def end_line?(line, start_line)
|
72
|
+
line.strip.end_with?("]") && start_line
|
73
|
+
end
|
74
|
+
|
75
|
+
# 中間行かどうか
|
76
|
+
#
|
77
|
+
# @param [String] line 行
|
78
|
+
# @param [Integer] start_line 開始行番号
|
79
|
+
# @return [Boolean]
|
80
|
+
def middle_line?(line, start_line)
|
81
|
+
line.strip != "]" && start_line
|
82
|
+
end
|
83
|
+
|
84
|
+
# 1行の処理
|
85
|
+
#
|
86
|
+
# @param [String] line 行
|
87
|
+
# @param [Integer] index 行番号
|
88
|
+
# @param [Integer] indent インデント
|
89
|
+
# @return [Hash] 説明、開始行番号、終了行番号、インデントのハッシュ
|
90
|
+
def build_oneline(line, index, indent)
|
91
|
+
{
|
92
|
+
type: :description,
|
93
|
+
text: extract_oneline(line, is_desc: true),
|
94
|
+
start_line: index,
|
95
|
+
end_line: index,
|
96
|
+
indent: indent
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
# 複数行の処理
|
101
|
+
#
|
102
|
+
# @param [Array<String>] content 説明の配列
|
103
|
+
# @param [Integer] start_line 開始行番号
|
104
|
+
# @param [Integer] index 行番号
|
105
|
+
# @param [Integer] indent インデント
|
106
|
+
# @return [Hash] 説明、開始行番号、終了行番号、インデントのハッシュ
|
107
|
+
def build_multiline(content, start_line, index, indent)
|
108
|
+
{
|
109
|
+
type: :description,
|
110
|
+
text: content.join("\n"),
|
111
|
+
start_line: start_line,
|
112
|
+
end_line: index,
|
113
|
+
indent: indent
|
114
|
+
}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require_relative "../util/indent_helper"
|
2
|
+
|
3
|
+
module ModpackLocalizer
|
4
|
+
module SNBT
|
5
|
+
# 翻訳された内容をSNBT形式に整形するクラス
|
6
|
+
class Formatter
|
7
|
+
include IndentHelper
|
8
|
+
|
9
|
+
# 保存できるように整形
|
10
|
+
#
|
11
|
+
# @param [Hash] content コンテンツ
|
12
|
+
# @param [String] indent インデント
|
13
|
+
# @return [String] SNBT形式に整形したコンテンツ
|
14
|
+
def format_overwritable_lines(content, indent)
|
15
|
+
full_lines = adjust_line_length(content)
|
16
|
+
format_for_snbt(full_lines, indent, content)
|
17
|
+
end
|
18
|
+
|
19
|
+
# SNBT形式に整形
|
20
|
+
#
|
21
|
+
# @param [Array<String>] lines 行
|
22
|
+
# @param [String] indent インデント
|
23
|
+
# @param [Hash] content コンテンツ
|
24
|
+
# @return [String] SNBT形式に整形した行
|
25
|
+
def format_for_snbt(lines, indent, content)
|
26
|
+
lines = prepare_lines_for_snbt(lines, content)
|
27
|
+
formatted_lines = format_lines(lines, indent, content)
|
28
|
+
"#{indent}#{content[:type]}: #{formatted_lines}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# 行数をstart_line~end_lineと一致させる
|
32
|
+
#
|
33
|
+
# @param [Hash] content コンテンツ
|
34
|
+
# @return [void]
|
35
|
+
def adjust_line_length(content)
|
36
|
+
required_lines = extract_required_line_counts(content)
|
37
|
+
lines = content[:text].split("\n")
|
38
|
+
|
39
|
+
delete_over_lines(lines, required_lines)
|
40
|
+
add_missing_lines(lines, required_lines, content[:indent])
|
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_unwanted_symbols(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
|
+
# @param [Array<String>] lines 行
|
93
|
+
# @param [Integer] required_lines 必要な行数
|
94
|
+
# @return [void]
|
95
|
+
def delete_over_lines(lines, required_lines)
|
96
|
+
return unless lines.length > required_lines
|
97
|
+
|
98
|
+
gap_length = lines.length - required_lines
|
99
|
+
gap_length.times do
|
100
|
+
index = lines.index("")
|
101
|
+
lines.delete_at(index) if index
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# 不足している行を追加
|
106
|
+
#
|
107
|
+
# @param [Array<String>] lines 行
|
108
|
+
# @param [Integer] required_lines 必要な行数
|
109
|
+
# @param [String] indent インデント
|
110
|
+
# @return [void]
|
111
|
+
def add_missing_lines(lines, required_lines, indent)
|
112
|
+
return unless lines.length < required_lines
|
113
|
+
|
114
|
+
while lines.length < required_lines
|
115
|
+
lines << empty_middle_line(indent)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# 中間行の空行を作成
|
120
|
+
#
|
121
|
+
# @return [String] 空行
|
122
|
+
def empty_middle_line(indent)
|
123
|
+
middle_indent(indent).to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
# 不要な記号を削除
|
127
|
+
#
|
128
|
+
# @param [String] line 行
|
129
|
+
# @return [String] 不要な記号を削除した行
|
130
|
+
def delete_unwanted_symbols(line)
|
131
|
+
line = delete_backslash(line)
|
132
|
+
line = delete_semicolon(line)
|
133
|
+
line = delete_dup_quotes(line)
|
134
|
+
line = delete_jp_quotes(line)
|
135
|
+
delete_curved_quotes(line)
|
136
|
+
end
|
137
|
+
|
138
|
+
# 不要なバックスラッシュを削除
|
139
|
+
#
|
140
|
+
# @param [String] line 行
|
141
|
+
# @return [String] 不要なバックスラッシュを削除した行
|
142
|
+
def delete_backslash(line)
|
143
|
+
line.gsub("\\", "")
|
144
|
+
end
|
145
|
+
|
146
|
+
# 不要なセミコロンを削除
|
147
|
+
#
|
148
|
+
# @param [String] line 行
|
149
|
+
# @return [String] 不要なセミコロンを削除した行
|
150
|
+
def delete_semicolon(line)
|
151
|
+
line.gsub(";", "")
|
152
|
+
end
|
153
|
+
|
154
|
+
# 不要なダブルクオートを削除
|
155
|
+
#
|
156
|
+
# @param [String] line 行
|
157
|
+
# @return [String] 不要なダブルクオートを削除した行
|
158
|
+
def delete_dup_quotes(line)
|
159
|
+
# 行間にある余計なダブルクオートを削除するため、一度全てのダブルクオートを削除している
|
160
|
+
# 全て削除したあと、行頭、行末にダブルクオートを追加する
|
161
|
+
line = line.gsub('"', "")
|
162
|
+
line_start = /^(\s*)/
|
163
|
+
line = line.sub(line_start, "\"")
|
164
|
+
"#{line}\""
|
165
|
+
end
|
166
|
+
|
167
|
+
# 不要な鍵括弧を削除
|
168
|
+
#
|
169
|
+
# @param [String] line 行
|
170
|
+
# @return [String] 不要な鍵括弧を削除した行
|
171
|
+
def delete_jp_quotes(line)
|
172
|
+
# 「Hello」
|
173
|
+
deletable_reg = /「.*」/
|
174
|
+
return line unless line.match?(deletable_reg)
|
175
|
+
|
176
|
+
jp_quotes = [/「/, /」/]
|
177
|
+
jp_quotes.each { |quo| line = line.gsub(quo, "") }
|
178
|
+
line
|
179
|
+
end
|
180
|
+
|
181
|
+
# 不要な曲がった引用符を削除
|
182
|
+
#
|
183
|
+
# @param [String] line 行
|
184
|
+
# @return [String] 不要な曲がった引用符を削除した行
|
185
|
+
def delete_curved_quotes(line)
|
186
|
+
# “Hello”
|
187
|
+
deletable_reg = /“.*”/
|
188
|
+
return line unless line.match?(deletable_reg)
|
189
|
+
|
190
|
+
curved_quotes = [/“/, /”/]
|
191
|
+
curved_quotes.each { |quo| line = line.gsub(quo, "") }
|
192
|
+
line
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "jp_translator_from_gpt"
|
2
|
+
require_relative "../util/help"
|
3
|
+
require_relative "../util/error"
|
4
|
+
require_relative "reader"
|
5
|
+
require_relative "writer"
|
6
|
+
|
7
|
+
module ModpackLocalizer
|
8
|
+
module SNBT
|
9
|
+
# .snbtの翻訳を実行するクラス
|
10
|
+
# JpTranslatorFromGptを使用して翻訳を行う
|
11
|
+
class Performer
|
12
|
+
# @param [Boolean] output_logs APIのログを出力するか
|
13
|
+
# @param [Array<String>] except_words 翻訳しない単語
|
14
|
+
# @param [String] language 言語
|
15
|
+
# @param [Boolean] display_help ヘルプを表示するか
|
16
|
+
# @return [ModpackLocalizer::SNBT::Performer]
|
17
|
+
def initialize(output_logs: true, except_words: [], language: "Japanese", display_help: true)
|
18
|
+
@translator = JpTranslatorFromGpt::Translator.new(
|
19
|
+
output_logs: output_logs,
|
20
|
+
except_words: except_words,
|
21
|
+
exchange_language: language
|
22
|
+
)
|
23
|
+
@reader, @writer, @progress_bar, @loggable = nil
|
24
|
+
|
25
|
+
ModpackLocalizer.help if display_help
|
26
|
+
end
|
27
|
+
|
28
|
+
# .snbtファイルを翻訳して出力する
|
29
|
+
#
|
30
|
+
# @param [String] file_path ファイルのパス
|
31
|
+
# @param [Boolean] loggable 翻訳ログを出力するか
|
32
|
+
# @return [void]
|
33
|
+
def perform(file_path, loggable: true)
|
34
|
+
@loggable = loggable
|
35
|
+
file_path = File.expand_path(file_path)
|
36
|
+
validate_path(file_path)
|
37
|
+
|
38
|
+
@reader, @writer = ModpackLocalizer::SNBT::Reader.new(file_path), ModpackLocalizer::SNBT::Writer.new(file_path)
|
39
|
+
results = @reader.extract_all.flatten
|
40
|
+
init_progress_bar(file_path, results.length) if @loggable
|
41
|
+
|
42
|
+
results.each do |result|
|
43
|
+
result[:text] = @translator.translate(result[:text])
|
44
|
+
@writer.overwrites(result)
|
45
|
+
@progress_bar.increment if @loggable
|
46
|
+
end
|
47
|
+
|
48
|
+
puts "Quest translation completed!"
|
49
|
+
end
|
50
|
+
|
51
|
+
# ディレクトリ内の.snbtファイルを翻訳して出力する
|
52
|
+
#
|
53
|
+
# @param [String] dir_path ディレクトリのパス
|
54
|
+
# @param [Boolean] loggable 翻訳ログを出力するか
|
55
|
+
# @return [void]
|
56
|
+
def perform_directory(dir_path: "quests", loggable: true)
|
57
|
+
puts "Performing directory: #{dir_path}" unless loggable
|
58
|
+
dir_path = File.expand_path(dir_path)
|
59
|
+
validate_path(dir_path)
|
60
|
+
|
61
|
+
# **でサブディレクトリも含めて取得
|
62
|
+
snbt_files = Dir.glob("#{dir_path}/**/*.snbt")
|
63
|
+
if snbt_files.empty?
|
64
|
+
puts "SNBT files not found in: #{dir_path}"
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
snbt_files.each { |file_path| perform(file_path, loggable: loggable) }
|
69
|
+
end
|
70
|
+
|
71
|
+
# ファイルの存在性のバリデーション
|
72
|
+
#
|
73
|
+
# @param [String] path ファイルのパス
|
74
|
+
# @return [void]
|
75
|
+
def validate_path(path)
|
76
|
+
path = File.expand_path(path)
|
77
|
+
raise ModpackLocalizer::PathNotFoundError.new(path) unless File.exist?(path)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# プログレスバーを初期化する
|
83
|
+
#
|
84
|
+
# @param [String] file_path ファイルのパス
|
85
|
+
# @param [Integer] length プログレスバーの長さ
|
86
|
+
# @return [void]
|
87
|
+
def init_progress_bar(file_path, length)
|
88
|
+
@progress_bar = ModpackLocalizer.create_progress_bar(file_path, length)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|