modpack_localizer 0.1.0

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