modpack_localizer 0.1.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.
- 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
|