diff --git a/Data/Scripts/001_Settings.rb b/Data/Scripts/001_Settings.rb index a81c938d7..02de22a00 100644 --- a/Data/Scripts/001_Settings.rb +++ b/Data/Scripts/001_Settings.rb @@ -372,12 +372,14 @@ module Settings #============================================================================= - # An array of available languages in the game, and their corresponding message - # file in the Data folder. Edit only if you have 2 or more languages to choose - # from. + # An array of available languages in the game, and their corresponding + # filename. Text files for a language are extracted to a folder called + # "Text_filename_core" or "Text_filename_game", and are recompiled into files + # in the Data folder called "messages_filename_core.dat" or + # "messages_filename_game.dat". LANGUAGES = [ - # ["English", "english.dat"], - # ["Deutsch", "deutsch.dat"] +# ["English", "english"], +# ["Deutsch", "deutsch"] ] #============================================================================= diff --git a/Data/Scripts/001_Technical/003_Intl_Messages.rb b/Data/Scripts/001_Technical/003_Intl_Messages.rb index a40b85e0f..3bffc7ba5 100644 --- a/Data/Scripts/001_Technical/003_Intl_Messages.rb +++ b/Data/Scripts/001_Technical/003_Intl_Messages.rb @@ -1,171 +1,101 @@ -def pbAddScriptTexts(items, script) - script.scan(/(?:_I)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s| - string = s[0] - string.gsub!(/\\\"/, "\"") - string.gsub!(/\\\\/, "\\") - items.push(string) - } -end +#=============================================================================== +# +#=============================================================================== +module Translator + module_function -def pbAddRgssScriptTexts(items, script) - script.scan(/(?:_INTL|_ISPRINTF)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s| - string = s[0] - string.gsub!(/\\r/, "\r") - string.gsub!(/\\n/, "\n") - string.gsub!(/\\1/, "\1") - string.gsub!(/\\\"/, "\"") - string.gsub!(/\\\\/, "\\") - items.push(string) - } -end - -def pbSetTextMessages - Graphics.update - begin - t = Time.now.to_i - texts = [] - $RGSS_SCRIPTS.each do |script| - if Time.now.to_i - t >= 5 - t = Time.now.to_i - Graphics.update + def gather_script_and_event_texts + Graphics.update + begin + t = Time.now.to_i + texts = [] + # Get script texts from Scripts.rxdata + $RGSS_SCRIPTS.each do |script| + if Time.now.to_i - t >= 5 + t = Time.now.to_i + Graphics.update + end + scr = Zlib::Inflate.inflate(script[2]) + find_translatable_text_from_RGSS_script(texts, scr) end - scr = Zlib::Inflate.inflate(script[2]) - pbAddRgssScriptTexts(texts, scr) - end - if safeExists?("Data/PluginScripts.rxdata") - plugin_scripts = load_data("Data/PluginScripts.rxdata") - plugin_scripts.each do |plugin| - plugin[2].each do |script| + # If Scripts.rxdata only has 1 section, scripts have been extracted. Get + # script texts from .rb files in Data/Scripts + if $RGSS_SCRIPTS.length == 1 + Dir.all("Data/Scripts").each do |script_file| if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update end - scr = Zlib::Inflate.inflate(script[1]).force_encoding(Encoding::UTF_8) - pbAddRgssScriptTexts(texts, scr) - end - end - end - # Must add messages because this code is used by both game system and Editor - MessageTypes.addMessagesAsHash(MessageTypes::ScriptTexts, texts) - commonevents = load_data("Data/CommonEvents.rxdata") - items = [] - choices = [] - commonevents.compact.each do |event| - if Time.now.to_i - t >= 5 - t = Time.now.to_i - Graphics.update - end - begin - neednewline = false - lastitem = "" - event.list.size.times do |j| - list = event.list[j] - if neednewline && list.code != 401 - if lastitem != "" - lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1 + " " } - items.push(lastitem) - lastitem = "" - end - neednewline = false - end - if list.code == 101 - lastitem += list.parameters[0].to_s - neednewline = true - elsif list.code == 102 - list.parameters[0].length.times do |k| - choices.push(list.parameters[0][k]) - end - neednewline = false - elsif list.code == 401 - lastitem += " " if lastitem != "" - lastitem += list.parameters[0].to_s - neednewline = true - elsif list.code == 355 || list.code == 655 - pbAddScriptTexts(items, list.parameters[0]) - elsif list.code == 111 && list.parameters[0] == 12 - pbAddScriptTexts(items, list.parameters[1]) - elsif list.code == 209 - route = list.parameters[1] - route.list.size.times do |k| - if route.list[k].code == 45 - pbAddScriptTexts(items, route.list[k].parameters[0]) - end - end + File.open(script_file, "rb") do |f| + find_translatable_text_from_RGSS_script(texts, f.read) end end - if neednewline && lastitem != "" - items.push(lastitem) - lastitem = "" + end + # Get script texts from plugin script files + if safeExists?("Data/PluginScripts.rxdata") + plugin_scripts = load_data("Data/PluginScripts.rxdata") + plugin_scripts.each do |plugin| + plugin[2].each do |script| + if Time.now.to_i - t >= 5 + t = Time.now.to_i + Graphics.update + end + scr = Zlib::Inflate.inflate(script[1]).force_encoding(Encoding::UTF_8) + find_translatable_text_from_RGSS_script(texts, scr) + end end end - end - if Time.now.to_i - t >= 5 - t = Time.now.to_i - Graphics.update - end - items |= [] - choices |= [] - items.concat(choices) - MessageTypes.setMapMessagesAsHash(0, items) - mapinfos = pbLoadMapInfos - mapinfos.each_key do |id| - if Time.now.to_i - t >= 5 - t = Time.now.to_i - Graphics.update - end - filename = sprintf("Data/Map%03d.rxdata", id) - next if !pbRgssExists?(filename) - map = load_data(filename) + MessageTypes.addMessagesAsHash(MessageTypes::ScriptTexts, texts) + # Find all text in common events and add them to messages + commonevents = load_data("Data/CommonEvents.rxdata") items = [] choices = [] - map.events.each_value do |event| + commonevents.compact.each do |event| if Time.now.to_i - t >= 5 t = Time.now.to_i Graphics.update end begin - event.pages.size.times do |i| - neednewline = false + neednewline = false + lastitem = "" + event.list.size.times do |j| + list = event.list[j] + if neednewline && list.code != 401 # Continuation of 101 Show Text + if lastitem != "" + lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1 + " " } + items.push(lastitem) + lastitem = "" + end + neednewline = false + end + if list.code == 101 # Show Text + lastitem += list.parameters[0].to_s + neednewline = true + elsif list.code == 102 # Show Choices + list.parameters[0].length.times do |k| + choices.push(list.parameters[0][k]) + end + neednewline = false + elsif list.code == 401 # Continuation of 101 Show Text + lastitem += " " if lastitem != "" + lastitem += list.parameters[0].to_s + neednewline = true + elsif list.code == 355 || list.code == 655 # Script or script continuation line + find_translatable_text_from_event_script(items, list.parameters[0]) + elsif list.code == 111 && list.parameters[0] == 12 # Conditional Branch + find_translatable_text_from_event_script(items, list.parameters[1]) + elsif list.code == 209 # Set Move Route + route = list.parameters[1] + route.list.size.times do |k| + if route.list[k].code == PBMoveRoute::Script + find_translatable_text_from_event_script(items, route.list[k].parameters[0]) + end + end + end + end + if neednewline && lastitem != "" + items.push(lastitem) lastitem = "" - event.pages[i].list.size.times do |j| - list = event.pages[i].list[j] - if neednewline && list.code != 401 - if lastitem != "" - lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1 + " " } - items.push(lastitem) - lastitem = "" - end - neednewline = false - end - if list.code == 101 - lastitem += list.parameters[0].to_s - neednewline = true - elsif list.code == 102 - list.parameters[0].length.times do |k| - choices.push(list.parameters[0][k]) - end - neednewline = false - elsif list.code == 401 - lastitem += " " if lastitem != "" - lastitem += list.parameters[0].to_s - neednewline = true - elsif list.code == 355 || list.code == 655 - pbAddScriptTexts(items, list.parameters[0]) - elsif list.code == 111 && list.parameters[0] == 12 - pbAddScriptTexts(items, list.parameters[1]) - elsif list.code == 209 - route = list.parameters[1] - route.list.size.times do |k| - if route.list[k].code == 45 - pbAddScriptTexts(items, route.list[k].parameters[0]) - end - end - end - end - if neednewline && lastitem != "" - items.push(lastitem) - lastitem = "" - end end end end @@ -176,196 +106,374 @@ def pbSetTextMessages items |= [] choices |= [] items.concat(choices) - MessageTypes.setMapMessagesAsHash(id, items) - if Time.now.to_i - t >= 5 - t = Time.now.to_i - Graphics.update - end - end - rescue Hangup - end - Graphics.update -end - -def pbEachIntlSection(file) - lineno = 1 - re = /^\s*\[\s*([^\]]+)\s*\]\s*$/ - havesection = false - sectionname = nil - lastsection = [] - file.each_line { |line| - if lineno == 1 && line[0].ord == 0xEF && line[1].ord == 0xBB && line[2].ord == 0xBF - line = line[3, line.length - 3] - end - if !line[/^\#/] && !line[/^\s*$/] - if line[re] - if havesection - yield lastsection, sectionname + MessageTypes.setMapMessagesAsHash(0, items) + # Find all text in map events and add them to messages + mapinfos = pbLoadMapInfos + mapinfos.each_key do |id| + if Time.now.to_i - t >= 5 + t = Time.now.to_i + Graphics.update end - lastsection.clear - sectionname = $~[1] - havesection = true - else - if sectionname.nil? - raise _INTL("Expected a section at the beginning of the file (line {1})", lineno) - end - lastsection.push(line.gsub(/\s+$/, "")) - end - end - lineno += 1 - if lineno % 500 == 0 - Graphics.update - end - } - if havesection - yield lastsection, sectionname - end -end - -def pbGetText(infile) - begin - file = File.open(infile, "rb") - rescue - raise _INTL("Can't find {1}", infile) - end - intldat = [] - begin - pbEachIntlSection(file) { |section, name| - next if section.length == 0 - if !name[/^([Mm][Aa][Pp])?(\d+)$/] - raise _INTL("Invalid section name {1}", name) - end - ismap = $~[1] && $~[1] != "" - id = $~[2].to_i - itemlength = 0 - if section[0][/^\d+$/] - intlhash = [] - itemlength = 3 - if ismap - raise _INTL("Section {1} can't be an ordered list (section was recognized as an ordered list because its first line is a number)", name) - end - if section.length % 3 != 0 - raise _INTL("Section {1}'s line count is not divisible by 3 (section was recognized as an ordered list because its first line is a number)", name) - end - else - intlhash = OrderedHash.new - itemlength = 2 - if section.length.odd? - raise _INTL("Section {1} has an odd number of entries (section was recognized as a hash because its first line is not a number)", name) - end - end - i = 0 - loop do - break unless i < section.length - if itemlength == 3 - if !section[i][/^\d+$/] - raise _INTL("Expected a number in section {1}, got {2} instead", name, section[i]) + filename = sprintf("Data/Map%03d.rxdata", id) + next if !pbRgssExists?(filename) + map = load_data(filename) + items = [] + choices = [] + map.events.each_value do |event| + if Time.now.to_i - t >= 5 + t = Time.now.to_i + Graphics.update + end + begin + event.pages.size.times do |i| + neednewline = false + lastitem = "" + event.pages[i].list.size.times do |j| + list = event.pages[i].list[j] + if neednewline && list.code != 401 # Continuation of 101 Show Text + if lastitem != "" + lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1 + " " } + items.push(lastitem) + lastitem = "" + end + neednewline = false + end + if list.code == 101 # Show Text + lastitem += list.parameters[0].to_s + neednewline = true + elsif list.code == 102 # Show Choices + list.parameters[0].length.times do |k| + choices.push(list.parameters[0][k]) + end + neednewline = false + elsif list.code == 401 # Continuation of 101 Show Text + lastitem += " " if lastitem != "" + lastitem += list.parameters[0].to_s + neednewline = true + elsif list.code == 355 || list.code == 655 # Script or script continuation line + find_translatable_text_from_event_script(items, list.parameters[0]) + elsif list.code == 111 && list.parameters[0] == 12 # Conditional Branch + find_translatable_text_from_event_script(items, list.parameters[1]) + elsif list.code == 209 # Set Move Route + route = list.parameters[1] + route.list.size.times do |k| + if route.list[k].code == PBMoveRoute::Script + find_translatable_text_from_event_script(items, route.list[k].parameters[0]) + end + end + end + end + if neednewline && lastitem != "" + items.push(lastitem) + lastitem = "" + end + end end - key = section[i].to_i - i += 1 - else - key = MessageTypes.denormalizeValue(section[i]) end - intlhash[key] = MessageTypes.denormalizeValue(section[i + 1]) - i += 2 - end - if ismap - intldat[0] = [] if !intldat[0] - intldat[0][id] = intlhash - else - intldat[id] = intlhash + if Time.now.to_i - t >= 5 + t = Time.now.to_i + Graphics.update + end + items |= [] + choices |= [] + items.concat(choices) + MessageTypes.setMapMessagesAsHash(id, items) if items.length > 0 + if Time.now.to_i - t >= 5 + t = Time.now.to_i + Graphics.update + end end + rescue Hangup + end + Graphics.update + end + + def find_translatable_text_from_RGSS_script(items, script) + script.force_encoding(Encoding::UTF_8) + script.scan(/(?:_INTL|_ISPRINTF)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s| + string = s[0] + string.gsub!(/\\r/, "\r") + string.gsub!(/\\n/, "\n") + string.gsub!(/\\1/, "\1") + string.gsub!(/\\\"/, "\"") + string.gsub!(/\\\\/, "\\") + items.push(string) } - ensure - file.close end - return intldat -end -def pbCompileText - outfile = File.open("intl.dat", "wb") - begin - intldat = pbGetText("intl.txt") - Marshal.dump(intldat, outfile) - rescue - raise - ensure - outfile.close + def find_translatable_text_from_event_script(items, script) + script.force_encoding(Encoding::UTF_8) + script.scan(/(?:_I)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s| + string = s[0] + string.gsub!(/\\\"/, "\"") + string.gsub!(/\\\\/, "\\") + items.push(string) + } + end + + def normalize_value(value) + if value[/[\r\n\t\x01]|^[\[\]]/] + ret = value.dup + ret.gsub!(/\r/, "<>") + ret.gsub!(/\n/, "<>") + ret.gsub!(/\t/, "<>") + ret.gsub!(/\[/, "<<[>>") + ret.gsub!(/\]/, "<<]>>") + ret.gsub!(/\x01/, "<<1>>") + return ret + end + return value + end + + def denormalize_value(value) + if value[/<<[rnt1\[\]]>>/] + ret = value.dup + ret.gsub!(/<<1>>/, "\1") + ret.gsub!(/<>/, "\r") + ret.gsub!(/<>/, "\n") + ret.gsub!(/<<\[>>/, "[") + ret.gsub!(/<<\]>>/, "]") + ret.gsub!(/<>/, "\t") + return ret + end + return value + end + + #============================================================================= + + def extract_text(language_name = "default", core_text = false, separate_map_files = false) + dir_name = sprintf("Text_%s_%s", language_name, (core_text) ? "core" : "game") + msg_window = pbCreateMessageWindow + # Get text for extraction + orig_messages = Translation.new(language_name) + if core_text + language_messages = orig_messages.core_messages + default_messages = orig_messages.default_core_messages + if !default_messages || default_messages.length == 0 + pbMessageDisplay(msg_window, _INTL("The default core messages file \"messages_core.dat\" was not found.")) + pbDisposeMessageWindow(msg_window) + return + end + else + language_messages = orig_messages.game_messages + default_messages = orig_messages.default_game_messages + if !default_messages || default_messages.length == 0 + pbMessageDisplay(msg_window, _INTL("The default game messages file \"messages_game.dat\" was not found.")) + pbDisposeMessageWindow(msg_window) + return + end + end + # Create folder for extracted text files, or delete existing text files from + # existing destination folder + if Dir.safe?(dir_name) + has_files = false + Dir.all(dir_name).each { |f| has_files = true; break } + if has_files && !pbConfirmMessageSerious(_INTL("Replace all text files in folder '{1}'?", dir_name)) + pbDisposeMessageWindow(msg_window) + return + end + Dir.all(dir_name).each { |f| File.delete(f) } + else + Dir.create(dir_name) + end + # Create a lambda function that helps to write text files + write_header = lambda do |f, with_line| + f.write(0xEF.chr) + f.write(0xBB.chr) + f.write(0xBF.chr) + f.write("# To localize this text for a particular language, please\r\n") + f.write("# translate every second line of this file.\r\n") + f.write("\#-------------------------------\r\n") if with_line + end + # Extract the text + pbMessageDisplay(msg_window, "\\ts[]" + _INTL("Extracting text, please wait.") + "\\wtnp[0]") + # Get all the section IDs to cycle through + max_section_id = default_messages.length + max_section_id = language_messages.length if language_messages && language_messages.length > max_section_id + max_section_id.times do |i| + section_name = getConstantName(MessageTypes, i, false) + next if !section_name + if i == MessageTypes::EventTexts + if separate_map_files + map_infos = pbLoadMapInfos + default_messages[i].each_with_index do |map_msgs, map_id| + next if !map_msgs || map_msgs.length == 0 + filename = sprintf("Map%03d", map_id) + filename += " " + map_infos[map_id].name if map_infos[map_id] + File.open(dir_name + "/" + filename + ".txt", "wb") { |f| + write_header.call(f, true) + translated_msgs = language_messages[i][map_id] if language_messages && language_messages[i] + write_section_texts_to_file(f, sprintf("Map%03d", map_id), translated_msgs, map_msgs) + } + end + else + next if !default_messages[i] || default_messages[i].length == 0 + no_difference = true + default_messages[i].each do |map_msgs| + no_difference = false if map_msgs && map_msgs.length > 0 + break if !map_msgs + end + next if no_difference + File.open(dir_name + "/" + section_name + ".txt", "wb") { |f| + write_header.call(f, false) + default_messages[i].each_with_index do |map_msgs, map_id| + next if !map_msgs || map_msgs.length == 0 + f.write("\#-------------------------------\r\n") + translated_msgs = (language_messages && language_messages[i]) ? language_messages[i][map_id] : nil + write_section_texts_to_file(f, sprintf("Map%03d", map_id), translated_msgs, map_msgs) + end + } + end + else # MessageTypes sections + next if !default_messages[i] || default_messages[i].length == 0 + File.open(dir_name + "/" + section_name + ".txt", "wb") { |f| + write_header.call(f, true) + translated_msgs = (language_messages) ? language_messages[i] : nil + write_section_texts_to_file(f, section_name, translated_msgs, default_messages[i]) + } + end + end + msg_window.textspeed = MessageConfig.pbSettingToTextSpeed($PokemonSystem.textspeed) + if core_text + pbMessageDisplay(msg_window, _INTL("All core text was extracted to files in the folder \"{1}\".\1", dir_name)) + else + pbMessageDisplay(msg_window, _INTL("All game text was extracted to files in the folder \"{1}\".\1", dir_name)) + end + pbMessageDisplay(msg_window, _INTL("To localize this text, translate every second line in those files.\1")) + pbMessageDisplay(msg_window, _INTL("After translating, choose \"Compile Translated Text\" in the Debug menu.")) + pbDisposeMessageWindow(msg_window) + end + + def write_section_texts_to_file(f, section_name, language_msgs, original_msgs = nil) + return if !original_msgs + case original_msgs + when Array + f.write("[#{section_name}]\r\n") + original_msgs.length.times do |j| + next if nil_or_empty?(original_msgs[j]) + f.write("#{j}\r\n") + f.write(normalize_value(original_msgs[j]) + "\r\n") + text = (language_msgs && language_msgs[j]) ? language_msgs[j] : original_msgs[j] + f.write(normalize_value(text) + "\r\n") + end + when Hash + f.write("[#{section_name}]\r\n") + keys = original_msgs.keys + keys.each do |key| + next if nil_or_empty?(original_msgs[key]) + f.write(normalize_value(key) + "\r\n") + text = (language_msgs && language_msgs[key]) ? language_msgs[key] : original_msgs[key] + f.write(normalize_value(text) + "\r\n") + end + end + end + + #============================================================================= + + def compile_text(dir_name, dat_filename) + msg_window = pbCreateMessageWindow + pbMessageDisplay(msg_window, "\\ts[]" + _INTL("Compiling text, please wait.") + "\\wtnp[0]") + outfile = File.open("Data/messages_" + dat_filename + ".dat", "wb") + all_text = [] + begin + text_files = Dir.get("Text_" + dir_name, "*.txt") + text_files.each { |file| compile_text_from_file(file, all_text) } + Marshal.dump(all_text, outfile) + rescue + raise + ensure + outfile.close + end + msg_window.textspeed = MessageConfig.pbSettingToTextSpeed($PokemonSystem.textspeed) + pbMessageDisplay(msg_window, + _INTL("Text files in the folder \"Text_{1}\" were successfully compiled into file \"Data/messages_{2}.dat\".", dir_name, dat_filename)) + pbMessageDisplay(msg_window, _INTL("You may need to close the game to see any changes to messages.")) + pbDisposeMessageWindow(msg_window) + end + + def compile_text_from_file(text_file, all_text) + begin + file = File.open(text_file, "rb") + rescue + raise _INTL("Can't find or open '{1}'.", text_file) + end + begin + Compiler.pbEachSection(file) do |contents, section_name| + next if contents.length == 0 + # Get the section number and whether the section contains a map's event text + section_id = -1 + is_map = false + if section_name.to_i != 0 # Section name is a number + section_id = section_name.to_i + elsif hasConst?(MessageTypes, section_name) # Section name is a constant from MessageTypes + section_id = getConst(MessageTypes, section_name) + elsif section_name[/^Map(\d+)$/i] # Section name is a map number (event text) + is_map = true + section_id = $~[1].to_i + end + raise _INTL("Invalid section name {1}", section_name) if section_id < 0 + # Decide whether the section contains text stored in an ordered list (an + # array) or an ordered hash + item_length = 0 + if contents[0][/^\d+$/] # If first line is a number, text is stored in an array + text_hash = [] + item_length = 3 + if is_map + raise _INTL("Section {1} can't be an ordered list (section was recognized as an ordered list because its first line is a number).", section_name) + end + if contents.length % 3 != 0 + raise _INTL("Section {1}'s line count is not divisible by 3 (section was recognized as an ordered list because its first line is a number).", section_name) + end + else # Text is stored in a hash + text_hash = {} + item_length = 2 + if contents.length.odd? + raise _INTL("Section {1} has an odd number of entries (section was recognized as a hash because its first line is not a number).", section_name) + end + end + # Add text in section to ordered list/hash + i = 0 + loop do + if item_length == 3 + if !contents[i][/^\d+$/] + raise _INTL("Expected a number in section {1}, got {2} instead", section_name, contents[i]) + end + key = contents[i].to_i + i += 1 + else + key = denormalize_value(contents[i]) + key = Translation.stringToKey(key) + end + text_hash[key] = denormalize_value(contents[i + 1]) + i += 2 + break if i >= contents.length + end + # Add ordered list/hash (text_hash) to array of all text (all_text) + all_text[MessageTypes::EventTexts] = [] if is_map && !all_text[MessageTypes::EventTexts] + target_section = (is_map) ? all_text[MessageTypes::EventTexts][section_id] : all_text[section_id] + if target_section + if text_hash.is_a?(Hash) + text_hash.keys.each { |key| target_section[key] = text_hash[key] if text_hash[key] } + else # text_hash is an array + text_hash.each_with_index { |line, i| target_section[i] = line if line } + end + elsif is_map + all_text[MessageTypes::EventTexts][section_id] = text_hash + else + all_text[section_id] = text_hash + end + end + ensure + file.close + end end end - - -class OrderedHash < Hash - def initialize - @keys = [] - super - end - - def keys - return @keys.clone - end - - def inspect - str = "{" - @keys.length.times do |i| - str += ", " if i > 0 - str += @keys[i].inspect + "=>" + self[@keys[i]].inspect - end - str += "}" - return str - end - - alias to_s inspect - - def []=(key, value) - oldvalue = self[key] - if !oldvalue && value - @keys.push(key) - elsif !value - @keys |= [] - @keys -= [key] - end - super(key, value) - end - - def self._load(string) - ret = self.new - keysvalues = Marshal.load(string) - keys = keysvalues[0] - values = keysvalues[1] - keys.length.times do |i| - ret[keys[i]] = values[i] - end - return ret - end - - def _dump(_depth = 100) - values = [] - @keys.each do |key| - values.push(self[key]) - end - return Marshal.dump([@keys, values]) - end -end - - - -class Messages - def initialize(filename = nil, delayLoad = false) - @messages = nil - @filename = filename - if @filename && !delayLoad - loadMessageFile(@filename) - end - end - - def delayedLoad - if @filename && !@messages - loadMessageFile(@filename) - @filename = nil - end - end +#=============================================================================== +# +#=============================================================================== +class Translation + attr_reader :core_messages, :game_messages def self.stringToKey(str) if str && str[/[\r\n\t\1]|^\s+|\s+$|\s{2,}/] @@ -378,272 +486,228 @@ class Messages return str end - def self.normalizeValue(value) - if value[/[\r\n\t\x01]|^[\[\]]/] - ret = value.clone - ret.gsub!(/\r/, "<>") - ret.gsub!(/\n/, "<>") - ret.gsub!(/\t/, "<>") - ret.gsub!(/\[/, "<<[>>") - ret.gsub!(/\]/, "<<]>>") - ret.gsub!(/\x01/, "<<1>>") - return ret - end - return value + def initialize(filename = nil, delay_load = false) + @default_core_messages = nil + @default_game_messages = nil + @core_messages = nil # A translation file + @game_messages = nil # A translation file + @filename = filename + load_message_files(@filename) if @filename && !delay_load end - def self.denormalizeValue(value) - if value[/<<[rnt1\[\]]>>/] - ret = value.clone - ret.gsub!(/<<1>>/, "\1") - ret.gsub!(/<>/, "\r") - ret.gsub!(/<>/, "\n") - ret.gsub!(/<<\[>>/, "[") - ret.gsub!(/<<\]>>/, "]") - ret.gsub!(/<>/, "\t") - return ret - end - return value + def default_core_messages + load_default_messages + return @default_core_messages end - def self.writeObject(f, msgs, secname, origMessages = nil) - return if !msgs - case msgs - when Array - f.write("[#{secname}]\r\n") - msgs.length.times do |j| - next if nil_or_empty?(msgs[j]) - value = Messages.normalizeValue(msgs[j]) - origValue = "" - if origMessages - origValue = Messages.normalizeValue(origMessages.get(secname, j)) - else - origValue = Messages.normalizeValue(MessageTypes.get(secname, j)) - end - f.write("#{j}\r\n") - f.write(origValue + "\r\n") - f.write(value + "\r\n") + def default_game_messages + load_default_messages + return @default_game_messages + end + + def load_message_files(filename) + begin + core_filename = sprintf("Data/messages_%s_core.dat", filename) + if safeExists?(core_filename) + pbRgssOpen(core_filename, "rb") { |f| @core_messages = Marshal.load(f) } end - when OrderedHash - f.write("[#{secname}]\r\n") - keys = msgs.keys - keys.each do |key| - next if nil_or_empty?(msgs[key]) - value = Messages.normalizeValue(msgs[key]) - valkey = Messages.normalizeValue(key) - # key is already serialized - f.write(valkey + "\r\n") - f.write(value + "\r\n") + @core_messages = nil if !@core_messages.is_a?(Array) + game_filename = sprintf("Data/messages_%s_game.dat", filename) + if safeExists?(game_filename) + pbRgssOpen(game_filename, "rb") { |f| @game_messages = Marshal.load(f) } end + @game_messages = nil if !@game_messages.is_a?(Array) + rescue + @core_messages = nil + @game_messages = nil end end - def messages - return @messages || [] + def load_default_messages + return if @default_core_messages + begin + if safeExists?("Data/messages_core.dat") + pbRgssOpen("Data/messages_core.dat", "rb") { |f| @default_core_messages = Marshal.load(f) } + end + @default_core_messages = [] if !@default_core_messages.is_a?(Array) + if safeExists?("Data/messages_game.dat") + pbRgssOpen("Data/messages_game.dat", "rb") { |f| @default_game_messages = Marshal.load(f) } + end + @default_game_messages = [] if !@default_game_messages.is_a?(Array) + rescue + @default_core_messages = [] + @default_game_messages = [] + end end - def extract(outfile) -# return if !@messages - origMessages = Messages.new("Data/messages.dat") - File.open(outfile, "wb") { |f| - f.write(0xef.chr) - f.write(0xbb.chr) - f.write(0xbf.chr) - f.write("# To localize this text for a particular language, please\r\n") - f.write("# translate every second line of this file.\r\n") - if origMessages.messages[0] - origMessages.messages[0].length.times do |i| - msgs = origMessages.messages[0][i] - Messages.writeObject(f, msgs, "Map#{i}", origMessages) - end - end - (1...origMessages.messages.length).each do |i| - msgs = origMessages.messages[i] - Messages.writeObject(f, msgs, i, origMessages) - end - } + def save_default_messages + File.open("Data/messages_core.dat", "wb") { |f| Marshal.dump(@default_core_messages, f) } + File.open("Data/messages_game.dat", "wb") { |f| Marshal.dump(@default_game_messages, f) } end def setMessages(type, array) - @messages = [] if !@messages - arr = [] - array.length.times do |i| - arr[i] = (array[i]) ? array[i] : "" - end - @messages[type] = arr + load_default_messages + @default_game_messages[type] = priv_add_to_array(type, array, nil) end def addMessages(type, array) - @messages = [] if !@messages - arr = (@messages[type]) ? @messages[type] : [] - array.length.times do |i| - arr[i] = (array[i]) ? array[i] : (arr[i]) ? arr[i] : "" - end - @messages[type] = arr - end - - def self.createHash(_type, array) - arr = OrderedHash.new - array.length.times do |i| - if array[i] - key = Messages.stringToKey(array[i]) - arr[key] = array[i] - end - end - return arr - end - - def self.addToHash(_type, array, hash) - hash = OrderedHash.new if !hash - array.length.times do |i| - if array[i] - key = Messages.stringToKey(array[i]) - hash[key] = array[i] - end - end - return hash - end - - def setMapMessagesAsHash(type, array) - @messages = [] if !@messages - @messages[0] = [] if !@messages[0] - @messages[0][type] = Messages.createHash(type, array) - end - - def addMapMessagesAsHash(type, array) - @messages = [] if !@messages - @messages[0] = [] if !@messages[0] - @messages[0][type] = Messages.addToHash(type, array, @messages[0][type]) + load_default_messages + @default_game_messages[type] = priv_add_to_array(type, array, @default_game_messages[type]) end def setMessagesAsHash(type, array) - @messages = [] if !@messages - @messages[type] = Messages.createHash(type, array) + load_default_messages + @default_game_messages[type] = priv_add_to_hash(type, array, nil) end def addMessagesAsHash(type, array) - @messages = [] if !@messages - @messages[type] = Messages.addToHash(type, array, @messages[type]) + load_default_messages + @default_game_messages[type] = priv_add_to_hash(type, array, @default_game_messages[type]) end - def saveMessages(filename = nil) - filename = "Data/messages.dat" if !filename - File.open(filename, "wb") { |f| Marshal.dump(@messages, f) } + def setMapMessagesAsHash(map_id, array) + load_default_messages + @default_game_messages[MessageTypes::EventTexts] ||= [] + @default_game_messages[MessageTypes::EventTexts][map_id] = priv_add_to_hash(MessageTypes::EventTexts, array, nil, map_id) end - def loadMessageFile(filename) - begin - pbRgssOpen(filename, "rb") { |f| @messages = Marshal.load(f) } - if !@messages.is_a?(Array) - @messages = nil - raise "Corrupted data" - end - return @messages - rescue - @messages = nil - return nil - end - end - - def set(type, id, value) - delayedLoad - return if !@messages - return if !@messages[type] - @messages[type][id] = value - end - - def getCount(type) - delayedLoad - return 0 if !@messages - return 0 if !@messages[type] - return @messages[type].length + def addMapMessagesAsHash(map_id, array) + load_default_messages + @default_game_messages[MessageTypes::EventTexts] ||= [] + @default_game_messages[MessageTypes::EventTexts][map_id] = priv_add_to_hash( + MessageTypes::EventTexts, array, @default_game_messages[MessageTypes::EventTexts][map_id], map_id) end def get(type, id) - delayedLoad - return "" if !@messages - return "" if !@messages[type] - return "" if !@messages[type][id] - return @messages[type][id] - end - - def getFromHash(type, key) - delayedLoad - return key if !@messages || !@messages[type] || !key - id = Messages.stringToKey(key) - return key if !@messages[type][id] - return @messages[type][id] - end - - def getFromMapHash(type, key) - delayedLoad - return key if !@messages - return key if !@messages[0] - return key if !@messages[0][type] && !@messages[0][0] - id = Messages.stringToKey(key) - if @messages[0][type] && @messages[0][type][id] - return @messages[0][type][id] - elsif @messages[0][0] && @messages[0][0][id] - return @messages[0][0][id] + delayed_load_message_files + if @game_messages && @game_messages[type] && @game_messages[type][id] + return @game_messages[type][id] end - return key + if @core_messages && @core_messages[type] && @core_messages[type][id] + return @core_messages[type][id] + end + return "" + end + + def getFromHash(type, text) + delayed_load_message_files + key = Translation.stringToKey(text) + return text if nil_or_empty?(key) + if @game_messages && @game_messages[type] && @game_messages[type][key] + return @game_messages[type][key] + end + if @core_messages && @core_messages[type] && @core_messages[type][key] + return @core_messages[type][key] + end + return text + end + + def getFromMapHash(map_id, text) + delayed_load_message_files + key = Translation.stringToKey(text) + return text if nil_or_empty?(key) + if @game_messages && @game_messages[MessageTypes::EventTexts] + if @game_messages[MessageTypes::EventTexts][map_id] && @game_messages[MessageTypes::EventTexts][map_id][key] + return @game_messages[MessageTypes::EventTexts][map_id][key] + elsif @game_messages[MessageTypes::EventTexts][0] && @game_messages[MessageTypes::EventTexts][0][key] + return @game_messages[MessageTypes::EventTexts][0][key] + end + end + if @core_messages && @core_messages[MessageTypes::EventTexts] + if @core_messages[MessageTypes::EventTexts][map_id] && @core_messages[MessageTypes::EventTexts][map_id][key] + return @core_messages[MessageTypes::EventTexts][map_id][key] + elsif @core_messages[MessageTypes::EventTexts][0] && @core_messages[MessageTypes::EventTexts][0][key] + return @core_messages[MessageTypes::EventTexts][0][key] + end + end + return text + end + + private + + def delayed_load_message_files + return if !@filename || @core_messages + load_message_files(@filename) + @filename = nil + end + + def priv_add_to_array(type, array, ret) + @default_core_messages[type] ||= [] + ret = [] if !ret + array.each_with_index do |text, i| + ret[i] = text if !nil_or_empty?(text) && @default_core_messages[type][i] != text + end + return ret + end + + def priv_add_to_hash(type, array, ret, map_id = 0) + if type == MessageTypes::EventTexts + @default_core_messages[type] ||= [] + @default_core_messages[type][map_id] ||= {} + default_keys = @default_core_messages[type][map_id].keys + else + @default_core_messages[type] ||= {} + default_keys = @default_core_messages[type].keys + end + ret = {} if !ret + array.each do |text| + next if !text + key = Translation.stringToKey(text) + ret[key] = text if !default_keys.include?(key) + end + return ret end end - - +#=============================================================================== +# +#=============================================================================== module MessageTypes - # Value 0 is used for common event and map event text - Species = 1 - Kinds = 2 - Entries = 3 - FormNames = 4 - Moves = 5 - MoveDescriptions = 6 - Items = 7 - ItemPlurals = 8 - ItemDescriptions = 9 - Abilities = 10 - AbilityDescs = 11 - Types = 12 - TrainerTypes = 13 - TrainerNames = 14 - BeginSpeech = 15 - EndSpeechWin = 16 - EndSpeechLose = 17 - RegionNames = 18 - PlaceNames = 19 - PlaceDescriptions = 20 - MapNames = 21 - PhoneMessages = 22 - TrainerLoseText = 23 - ScriptTexts = 24 - RibbonNames = 25 - RibbonDescriptions = 26 - StorageCreator = 27 - ItemPortionNames = 28 - ItemPortionNamePlurals = 29 - @@messages = Messages.new - @@messagesFallback = Messages.new("Data/messages.dat", true) + # NOTE: These constants aren't numbered in any particular order, but these + # numbers are retained for backwards compatibility with older extracted + # text files. + EventTexts = 0 # Used for text in both common events and map events + Species = 1 + SpeciesCategories = 2 + PokedexEntries = 3 + SpeciesForms = 4 + Moves = 5 + MoveDescriptions = 6 + Items = 7 + ItemPlurals = 8 + ItemDescriptions = 9 + Abilities = 10 + AbilityDescriptions = 11 + Types = 12 + TrainerTypes = 13 + TrainerNames = 14 + FrontierIntroSpeeches = 15 + FrontierEndSpeechesWin = 16 + FrontierEndSpeechesLose = 17 + Regions = 18 + RegionLocations = 19 + RegionDescriptions = 20 + MapNames = 21 + PhoneMessages = 22 + TrainerLoseTexts = 23 + ScriptTexts = 24 + RibbonNames = 25 + RibbonDescriptions = 26 + StorageCreator = 27 + ItemPortions = 28 + ItemPortionPlurals = 29 + @@messages = Translation.new - def self.stringToKey(str) - return Messages.stringToKey(str) + def self.load_default_messages + @@messages.load_default_messages end - def self.normalizeValue(value) - return Messages.normalizeValue(value) + def self.load_message_files(filename) + @@messages.load_message_files(filename) end - def self.denormalizeValue(value) - Messages.denormalizeValue(value) - end - - def self.writeObject(f, msgs, secname) - Messages.denormalizeValue(str) - end - - def self.extract(outfile) - @@messages.extract(outfile) + def self.save_default_messages + @@messages.save_default_messages end def self.setMessages(type, array) @@ -654,71 +718,38 @@ module MessageTypes @@messages.addMessages(type, array) end - def self.createHash(type, array) - Messages.createHash(type, array) - end - - def self.addMapMessagesAsHash(type, array) - @@messages.addMapMessagesAsHash(type, array) - end - - def self.setMapMessagesAsHash(type, array) - @@messages.setMapMessagesAsHash(type, array) + def self.setMessagesAsHash(type, array) + @@messages.setMessagesAsHash(type, array) end def self.addMessagesAsHash(type, array) @@messages.addMessagesAsHash(type, array) end - def self.setMessagesAsHash(type, array) - @@messages.setMessagesAsHash(type, array) + def self.setMapMessagesAsHash(type, array) + @@messages.setMapMessagesAsHash(type, array) end - def self.saveMessages(filename = nil) - @@messages.saveMessages(filename) - end - - def self.loadMessageFile(filename) - @@messages.loadMessageFile(filename) + def self.addMapMessagesAsHash(type, array) + @@messages.addMapMessagesAsHash(type, array) end def self.get(type, id) - ret = @@messages.get(type, id) - if ret == "" - ret = @@messagesFallback.get(type, id) - end - return ret - end - - def self.getCount(type) - c1 = @@messages.getCount(type) - c2 = @@messagesFallback.getCount(type) - return c1 > c2 ? c1 : c2 - end - - def self.getOriginal(type, id) - return @@messagesFallback.get(type, id) + return @@messages.get(type, id) end def self.getFromHash(type, key) - @@messages.getFromHash(type, key) + return @@messages.getFromHash(type, key) end def self.getFromMapHash(type, key) - @@messages.getFromMapHash(type, key) + return @@messages.getFromMapHash(type, key) end end - - -def pbLoadMessages(file) - return MessageTypes.loadMessageFile(file) -end - -def pbGetMessageCount(type) - return MessageTypes.getCount(type) -end - +#=============================================================================== +# +#=============================================================================== def pbGetMessage(type, id) return MessageTypes.get(type, id) end diff --git a/Data/Scripts/003_Game processing/001_StartGame.rb b/Data/Scripts/003_Game processing/001_StartGame.rb index 10d86b247..05f51a011 100644 --- a/Data/Scripts/003_Game processing/001_StartGame.rb +++ b/Data/Scripts/003_Game processing/001_StartGame.rb @@ -30,7 +30,7 @@ module Game # Set language (and choose language if there is no save file) if Settings::LANGUAGES.length >= 2 $PokemonSystem.language = pbChooseLanguage if save_data.empty? - pbLoadMessages("Data/" + Settings::LANGUAGES[$PokemonSystem.language][1]) + MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1]) end end diff --git a/Data/Scripts/007_Objects and windows/011_Messages.rb b/Data/Scripts/007_Objects and windows/011_Messages.rb index 0f1153a43..f73d438e9 100644 --- a/Data/Scripts/007_Objects and windows/011_Messages.rb +++ b/Data/Scripts/007_Objects and windows/011_Messages.rb @@ -260,9 +260,11 @@ def pbGetBasicMapNameFromId(id) end def pbGetMapNameFromId(id) - name = pbGetMessage(MessageTypes::MapNames, id) - name = pbGetBasicMapNameFromId(id) if nil_or_empty?(name) - name.gsub!(/\\PN/, $player.name) if $player + name = GameData::MapMetadata.get(id)&.name + if nil_or_empty?(name) + name = pbGetBasicMapNameFromId(id) + name.gsub!(/\\PN/, $player.name) if $player + end return name end diff --git a/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb b/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb index 53b1996ae..bf161e340 100644 --- a/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb +++ b/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb @@ -15,7 +15,7 @@ module GameData "SectionName" => [:id, "u"], "Name" => [:real_name, "s"], "Filename" => [:filename, "s"], - "Point" => [:point, "^uussUUUU"], + "Point" => [:point, "^uusSUUUU"], "Flags" => [:flags, "*s"] } @@ -33,7 +33,7 @@ module GameData # @return [String] the translated name of this region def name - return pbGetMessage(MessageTypes::RegionNames, @id) + return pbGetMessageFromHash(MessageTypes::Regions, @real_name) end def has_flag?(flag) diff --git a/Data/Scripts/010_Data/002_PBS data/004_Ability.rb b/Data/Scripts/010_Data/002_PBS data/004_Ability.rb index 3ac438a41..c3d59d7b4 100644 --- a/Data/Scripts/010_Data/002_PBS data/004_Ability.rb +++ b/Data/Scripts/010_Data/002_PBS data/004_Ability.rb @@ -35,7 +35,7 @@ module GameData # @return [String] the translated description of this ability def description - return pbGetMessageFromHash(MessageTypes::AbilityDescs, @real_description) + return pbGetMessageFromHash(MessageTypes::AbilityDescriptions, @real_description) end def has_flag?(flag) diff --git a/Data/Scripts/010_Data/002_PBS data/006_Item.rb b/Data/Scripts/010_Data/002_PBS data/006_Item.rb index 1b0ec0177..af01994b7 100644 --- a/Data/Scripts/010_Data/002_PBS data/006_Item.rb +++ b/Data/Scripts/010_Data/002_PBS data/006_Item.rb @@ -143,13 +143,13 @@ module GameData # @return [String] the translated portion name of this item def portion_name - return pbGetMessageFromHash(MessageTypes::ItemPortionNames, @real_portion_name) if @real_portion_name + return pbGetMessageFromHash(MessageTypes::ItemPortions, @real_portion_name) if @real_portion_name return name end # @return [String] the translated plural version of the portion name of this item def portion_name_plural - return pbGetMessageFromHash(MessageTypes::ItemPortionNamePlurals, @real_portion_name_plural) if @real_portion_name_plural + return pbGetMessageFromHash(MessageTypes::ItemPortionPlurals, @real_portion_name_plural) if @real_portion_name_plural return name_plural end diff --git a/Data/Scripts/010_Data/002_PBS data/008_Species.rb b/Data/Scripts/010_Data/002_PBS data/008_Species.rb index 202ad0257..8e5aacef5 100644 --- a/Data/Scripts/010_Data/002_PBS data/008_Species.rb +++ b/Data/Scripts/010_Data/002_PBS data/008_Species.rb @@ -225,17 +225,17 @@ module GameData # @return [String] the translated name of this form of this species def form_name - return pbGetMessageFromHash(MessageTypes::FormNames, @real_form_name) + return pbGetMessageFromHash(MessageTypes::SpeciesForms, @real_form_name) end # @return [String] the translated Pokédex category of this species def category - return pbGetMessageFromHash(MessageTypes::Kinds, @real_category) + return pbGetMessageFromHash(MessageTypes::SpeciesCategories, @real_category) end # @return [String] the translated Pokédex entry of this species def pokedex_entry - return pbGetMessageFromHash(MessageTypes::Entries, @real_pokedex_entry) + return pbGetMessageFromHash(MessageTypes::PokedexEntries, @real_pokedex_entry) end def default_form diff --git a/Data/Scripts/010_Data/002_PBS data/015_Trainer.rb b/Data/Scripts/010_Data/002_PBS data/015_Trainer.rb index 7192b96ff..0d206b36f 100644 --- a/Data/Scripts/010_Data/002_PBS data/015_Trainer.rb +++ b/Data/Scripts/010_Data/002_PBS data/015_Trainer.rb @@ -107,7 +107,7 @@ module GameData # @return [String] the translated in-battle lose message of this trainer def lose_text - return pbGetMessageFromHash(MessageTypes::TrainerLoseText, @real_lose_text) + return pbGetMessageFromHash(MessageTypes::TrainerLoseTexts, @real_lose_text) end # Creates a battle-ready version of a trainer's data. diff --git a/Data/Scripts/010_Data/002_PBS data/018_MapMetadata.rb b/Data/Scripts/010_Data/002_PBS data/018_MapMetadata.rb index 06f57c58a..8068d96ad 100644 --- a/Data/Scripts/010_Data/002_PBS data/018_MapMetadata.rb +++ b/Data/Scripts/010_Data/002_PBS data/018_MapMetadata.rb @@ -115,7 +115,10 @@ module GameData # @return [String] the translated name of this map def name - return pbGetMapNameFromId(@id) + ret = pbGetMessageFromHash(MessageTypes::MapNames, @real_name) + ret = pbGetBasicMapNameFromId(@id) if nil_or_empty?(ret) + ret.gsub!(/\\PN/, $player.name) if $player + return ret end def has_flag?(flag) diff --git a/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb b/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb index 69d64f050..a72190551 100644 --- a/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb +++ b/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb @@ -7,7 +7,7 @@ class PokemonGlobalMetadata attr_accessor :bicycle attr_accessor :surfing attr_accessor :diving - attr_accessor :sliding + attr_accessor :ice_sliding attr_accessor :fishing # Player data attr_accessor :startTime @@ -58,7 +58,7 @@ class PokemonGlobalMetadata @bicycle = false @surfing = false @diving = false - @sliding = false + @ice_sliding = false @fishing = false # Player data @startTime = Time.now diff --git a/Data/Scripts/016_UI/009_UI_RegionMap.rb b/Data/Scripts/016_UI/009_UI_RegionMap.rb index bd902a26e..5303936a6 100644 --- a/Data/Scripts/016_UI/009_UI_RegionMap.rb +++ b/Data/Scripts/016_UI/009_UI_RegionMap.rb @@ -185,7 +185,7 @@ class PokemonRegionMap_Scene @map.point.each do |point| next if point[0] != x || point[1] != y return "" if point[7] && (@wallmap || point[7] <= 0 || !$game_switches[point[7]]) - name = pbGetMessageFromHash(MessageTypes::PlaceNames, point[2]) + name = pbGetMessageFromHash(MessageTypes::RegionLocations, point[2]) return (@editor) ? point[2] : name end return "" @@ -213,7 +213,8 @@ class PokemonRegionMap_Scene @map.point.each do |point| next if point[0] != x || point[1] != y return "" if point[7] && (@wallmap || point[7] <= 0 || !$game_switches[point[7]]) - mapdesc = pbGetMessageFromHash(MessageTypes::PlaceDescriptions, point[3]) + return "" if !point[3] + mapdesc = pbGetMessageFromHash(MessageTypes::RegionDescriptions, point[3]) return (@editor) ? point[3] : mapdesc end return "" diff --git a/Data/Scripts/016_UI/010_UI_Phone.rb b/Data/Scripts/016_UI/010_UI_Phone.rb index 43f657c3e..a229361a8 100644 --- a/Data/Scripts/016_UI/010_UI_Phone.rb +++ b/Data/Scripts/016_UI/010_UI_Phone.rb @@ -95,7 +95,7 @@ class PokemonPhone_Scene end # Set info text infotext = _INTL("Registered
") - infotext += _INTL(" {1}
", @sprites["list"].commands.length) + infotext += _INTL("{1}
", @sprites["list"].commands.length) infotext += _INTL("Waiting for a rematch{1}", rematch_count) @sprites["info"].text = infotext pbRefreshScreen diff --git a/Data/Scripts/016_UI/013_UI_Load.rb b/Data/Scripts/016_UI/013_UI_Load.rb index d58b84d38..2d7235909 100644 --- a/Data/Scripts/016_UI/013_UI_Load.rb +++ b/Data/Scripts/016_UI/013_UI_Load.rb @@ -325,7 +325,7 @@ class PokemonLoadScreen when cmd_language @scene.pbEndScene $PokemonSystem.language = pbChooseLanguage - pbLoadMessages("Data/" + Settings::LANGUAGES[$PokemonSystem.language][1]) + MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1]) if show_continue @save_data[:pokemon_system] = $PokemonSystem File.open(SaveData::FILE_PATH, "wb") { |file| Marshal.dump(@save_data, file) } diff --git a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb index e10f5c9ad..d5241c15b 100644 --- a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb +++ b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/001_Challenge_BattleChallenge.rb @@ -98,8 +98,8 @@ class BattleChallenge opponent = pbGenerateBattleTrainer(self.nextTrainer, self.rules) bttrainers = pbGetBTTrainers(@id) trainerdata = bttrainers[self.nextTrainer] - opponent.lose_text = pbGetMessageFromHash(MessageTypes::EndSpeechLose, trainerdata[4]) - opponent.win_text = pbGetMessageFromHash(MessageTypes::EndSpeechWin, trainerdata[3]) + opponent.lose_text = pbGetMessageFromHash(MessageTypes::FrontierEndSpeechesLose, trainerdata[4]) + opponent.win_text = pbGetMessageFromHash(MessageTypes::FrontierEndSpeechesWin, trainerdata[3]) ret = pbOrganizedBattleEx(opponent, self.rules) return ret end @@ -376,8 +376,8 @@ class BattleFactoryData pbGetMessageFromHash(MessageTypes::TrainerNames, trainerdata[1]), trainerdata[0] ) - @opponent.lose_text = pbGetMessageFromHash(MessageTypes::EndSpeechLose, trainerdata[4]) - @opponent.win_text = pbGetMessageFromHash(MessageTypes::EndSpeechWin, trainerdata[3]) + @opponent.lose_text = pbGetMessageFromHash(MessageTypes::FrontierEndSpeechesLose, trainerdata[4]) + @opponent.win_text = pbGetMessageFromHash(MessageTypes::FrontierEndSpeechesWin, trainerdata[3]) opponentPkmn = pbBattleFactoryPokemon(pbBattleChallenge.rules, @bcdata.wins, @bcdata.swaps, @rentals) @opponent.party = opponentPkmn.sample(3) end @@ -401,8 +401,8 @@ class BattleFactoryData pbGetMessageFromHash(MessageTypes::TrainerNames, trainerdata[1]), trainerdata[0] ) - @opponent.lose_text = pbGetMessageFromHash(MessageTypes::EndSpeechLose, trainerdata[4]) - @opponent.win_text = pbGetMessageFromHash(MessageTypes::EndSpeechWin, trainerdata[3]) + @opponent.lose_text = pbGetMessageFromHash(MessageTypes::FrontierEndSpeechesLose, trainerdata[4]) + @opponent.win_text = pbGetMessageFromHash(MessageTypes::FrontierEndSpeechesWin, trainerdata[3]) opponentPkmn = pbBattleFactoryPokemon(pbBattleChallenge.rules, @bcdata.wins, @bcdata.swaps, [].concat(@rentals).concat(@oldopponent)) @opponent.party = opponentPkmn.sample(3) diff --git a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb index 0e8118a8b..c4536f6dd 100644 --- a/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb +++ b/Data/Scripts/018_Alternate battle modes/001_Battle Frontier/002_Challenge_Data.rb @@ -92,7 +92,7 @@ def pbBattleChallengeBeginSpeech return "..." if !pbBattleChallenge.pbInProgress? bttrainers = pbGetBTTrainers(pbBattleChallenge.currentChallenge) tr = bttrainers[pbBattleChallenge.nextTrainer] - return (tr) ? pbGetMessageFromHash(MessageTypes::BeginSpeech, tr[2]) : "..." + return (tr) ? pbGetMessageFromHash(MessageTypes::FrontierIntroSpeeches, tr[2]) : "..." end #=============================================================================== diff --git a/Data/Scripts/019_Utilities/001_Utilities.rb b/Data/Scripts/019_Utilities/001_Utilities.rb index 1665f7eaf..ee23519dc 100644 --- a/Data/Scripts/019_Utilities/001_Utilities.rb +++ b/Data/Scripts/019_Utilities/001_Utilities.rb @@ -134,12 +134,13 @@ def getID(mod, constant) return constant end -def getConstantName(mod, value) +def getConstantName(mod, value, raise_if_none = true) mod = Object.const_get(mod) if mod.is_a?(Symbol) mod.constants.each do |c| return c.to_s if mod.const_get(c.to_sym) == value end - raise _INTL("Value {1} not defined by a constant in {2}", value, mod.name) + raise _INTL("Value {1} not defined by a constant in {2}", value, mod.name) if raise_if_none + return nil end def getConstantNameOrValue(mod, value) diff --git a/Data/Scripts/020_Debug/002_Animation editor/002_AnimEditor_ControlsButtons.rb b/Data/Scripts/020_Debug/002_Animation editor/002_AnimEditor_ControlsButtons.rb index a51e3167f..598920fab 100644 --- a/Data/Scripts/020_Debug/002_Animation editor/002_AnimEditor_ControlsButtons.rb +++ b/Data/Scripts/020_Debug/002_Animation editor/002_AnimEditor_ControlsButtons.rb @@ -432,13 +432,13 @@ class Slider < UIControl color = Color.new(120, 120, 120) bitmap.fill_rect(x, y, width, height, Color.new(0, 0, 0, 0)) size = bitmap.text_size(self.label).width - leftarrows = bitmap.text_size(_INTL(" << ")) + leftarrows = bitmap.text_size(_INTL("<<")) numbers = bitmap.text_size(" XXXX ").width - rightarrows = bitmap.text_size(_INTL(" >> ")) + rightarrows = bitmap.text_size(_INTL(">>")) bitmap.font.color = color shadowtext(bitmap, x, y, size, height, self.label) x += size - shadowtext(bitmap, x, y, leftarrows.width, height, _INTL(" << "), + shadowtext(bitmap, x, y, leftarrows.width, height, _INTL("<<"), self.disabled || self.curvalue == self.minvalue) @leftarrow = Rect.new(x, y, leftarrows.width, height) x += leftarrows.width @@ -447,7 +447,7 @@ class Slider < UIControl shadowtext(bitmap, x, y, numbers, height, " #{self.curvalue} ", false, 1) end x += numbers - shadowtext(bitmap, x, y, rightarrows.width, height, _INTL(" >> "), + shadowtext(bitmap, x, y, rightarrows.width, height, _INTL(">>"), self.disabled || self.curvalue == self.maxvalue) @rightarrow = Rect.new(x, y, rightarrows.width, height) end @@ -681,12 +681,12 @@ class TextSlider < UIControl color = Color.new(120, 120, 120) bitmap.fill_rect(x, y, width, height, Color.new(0, 0, 0, 0)) size = bitmap.text_size(self.label).width - leftarrows = bitmap.text_size(_INTL(" << ")) - rightarrows = bitmap.text_size(_INTL(" >> ")) + leftarrows = bitmap.text_size(_INTL("<<")) + rightarrows = bitmap.text_size(_INTL(">>")) bitmap.font.color = color shadowtext(bitmap, x, y, size, height, self.label) x += size - shadowtext(bitmap, x, y, leftarrows.width, height, _INTL(" << "), + shadowtext(bitmap, x, y, leftarrows.width, height, _INTL("<<"), self.disabled || self.curvalue == self.minvalue) @leftarrow = Rect.new(x, y, leftarrows.width, height) x += leftarrows.width @@ -695,7 +695,7 @@ class TextSlider < UIControl shadowtext(bitmap, x, y, @maxoptionwidth, height, " #{@options[self.curvalue]} ", false, 1) end x += @maxoptionwidth - shadowtext(bitmap, x, y, rightarrows.width, height, _INTL(" >> "), + shadowtext(bitmap, x, y, rightarrows.width, height, _INTL(">>"), self.disabled || self.curvalue == self.maxvalue) @rightarrow = Rect.new(x, y, rightarrows.width, height) end diff --git a/Data/Scripts/020_Debug/003_Debug menus/002_Debug_MenuCommands.rb b/Data/Scripts/020_Debug/003_Debug menus/002_Debug_MenuCommands.rb index 115635945..13f084dc6 100644 --- a/Data/Scripts/020_Debug/003_Debug menus/002_Debug_MenuCommands.rb +++ b/Data/Scripts/020_Debug/003_Debug menus/002_Debug_MenuCommands.rb @@ -1088,20 +1088,58 @@ MenuHandlers.add(:debug_menu, :mystery_gift, { }) MenuHandlers.add(:debug_menu, :extract_text, { - "name" => _INTL("Extract Text"), + "name" => _INTL("Extract Text For Translation"), "parent" => :other_menu, - "description" => _INTL("Extract all text in the game to a single file for translating."), + "description" => _INTL("Extract all text in the game to text files for translating."), "effect" => proc { - pbExtractText + if Settings::LANGUAGES.length == 0 + pbMessage(_INTL("No languages are defined in the LANGUAGES array in Settings.")) + pbMessage(_INTL("You need to add at least one language to LANGUAGES first, to choose which one to extract text for.")) + next + end + # Choose a language from Settings to name the extraction folder after + cmds = [] + Settings::LANGUAGES.each { |val| cmds.push(val[0]) } + cmds.push(_INTL("Cancel")) + language_index = pbMessage(_INTL("Choose a language to extract text for."), cmds, cmds.length) + next if language_index == cmds.length - 1 + language_name = Settings::LANGUAGES[language_index][1] + # Choose whether to extract core text or game text + text_type = pbMessage(_INTL("Choose a language to extract text for."), + [_INTL("Game-specific text"), _INTL("Core text"), _INTL("Cancel")], 3) + next if text_type == 2 + # If game text, choose whether to extract map texts to map-specific files or + # to one big file + map_files = 0 + if text_type == 0 + map_files = pbMessage(_INTL("How many text files should map event texts be extracted to?"), + [_INTL("One big file"), _INTL("One file per map"), _INTL("Cancel")], 3) + next if map_files == 2 + end + # Extract the chosen set of text for the chosen language + Translator.extract_text(language_name, text_type == 1, map_files == 1) } }) MenuHandlers.add(:debug_menu, :compile_text, { - "name" => _INTL("Compile Text"), + "name" => _INTL("Compile Translated Text"), "parent" => :other_menu, - "description" => _INTL("Import text and converts it into a language file."), + "description" => _INTL("Import text files and convert them into a language file."), "effect" => proc { - pbCompileTextUI + # Find all folders with a particular naming convention + cmds = Dir.glob("Text_*_*") + if cmds.length == 0 + pbMessage(_INTL("No language folders found to compile.")) + pbMessage(_INTL("Language folders must be named \"Text_SOMETHING_core\" or \"Text_SOMETHING_game\" and be in the root folder.")) + next + end + cmds.push(_INTL("Cancel")) + # Ask which folder to compile into a .dat file + folder_index = pbMessage(_INTL("Choose a language folder to compile."), cmds, cmds.length) + next if folder_index == cmds.length - 1 + # Compile the text files in the chosen folder + dat_filename = cmds[folder_index].gsub!(/^Text_/, "") + Translator.compile_text(cmds[folder_index], dat_filename) } }) diff --git a/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb b/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb index 9a6dd6cae..70d5231ae 100644 --- a/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb +++ b/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb @@ -546,37 +546,6 @@ end -#=============================================================================== -# Text import/export for localisation -#=============================================================================== -def pbExtractText - msgwindow = pbCreateMessageWindow - if safeExists?("intl.txt") && - !pbConfirmMessageSerious(_INTL("intl.txt already exists. Overwrite it?")) - pbDisposeMessageWindow(msgwindow) - return - end - pbMessageDisplay(msgwindow, _INTL("Please wait.\\wtnp[0]")) - MessageTypes.extract("intl.txt") - pbMessageDisplay(msgwindow, _INTL("All text in the game was extracted and saved to intl.txt.\1")) - pbMessageDisplay(msgwindow, _INTL("To localize the text for a particular language, translate every second line in the file.\1")) - pbMessageDisplay(msgwindow, _INTL("After translating, choose \"Compile Text.\"")) - pbDisposeMessageWindow(msgwindow) -end - -def pbCompileTextUI - msgwindow = pbCreateMessageWindow - pbMessageDisplay(msgwindow, _INTL("Please wait.\\wtnp[0]")) - begin - pbCompileText - pbMessageDisplay(msgwindow, _INTL("Successfully compiled text and saved it to intl.dat.\1")) - pbMessageDisplay(msgwindow, _INTL("To use the file in a game, place the file in the Data folder under a different name, and edit the Settings::LANGUAGES array in the scripts.")) - rescue RuntimeError - pbMessageDisplay(msgwindow, _INTL("Failed to compile text: {1}", $!.message)) - end - pbDisposeMessageWindow(msgwindow) -end - #=============================================================================== # Battle animations import/export #=============================================================================== diff --git a/Data/Scripts/021_Compiler/001_Compiler.rb b/Data/Scripts/021_Compiler/001_Compiler.rb index ef4596a67..023b78a20 100644 --- a/Data/Scripts/021_Compiler/001_Compiler.rb +++ b/Data/Scripts/021_Compiler/001_Compiler.rb @@ -151,7 +151,7 @@ module Compiler } end - # Unused + # Used by translated text compiler def pbEachSection(f) lineno = 1 havesection = false @@ -164,21 +164,21 @@ module Compiler line.force_encoding(Encoding::UTF_8) if !line[/^\#/] && !line[/^\s*$/] if line[/^\s*\[\s*(.+?)\s*\]\s*$/] - yield lastsection, sectionname if havesection + yield lastsection, sectionname if havesection + lastsection.clear sectionname = $~[1] - lastsection = [] havesection = true else if sectionname.nil? - raise _INTL("Expected a section at the beginning of the file (line {1}). Sections begin with '[name of section]'", lineno) + raise _INTL("Expected a section at the beginning of the file (line {1}). Sections begin with '[name of section]'.", lineno) end - lastsection.push(line.gsub(/^\s+/, "").gsub(/\s+$/, "")) + lastsection.push(line.strip) end end lineno += 1 Graphics.update if lineno % 500 == 0 } - yield lastsection, sectionname if havesection + yield lastsection, sectionname if havesection end # Unused @@ -845,9 +845,9 @@ module Compiler compile_animations compile_trainer_events(mustCompile) Console.echo_li(_INTL("Saving messages...")) - pbSetTextMessages - MessageTypes.saveMessages - MessageTypes.loadMessageFile("Data/messages.dat") if safeExists?("Data/messages.dat") + Translator.gather_script_and_event_texts + MessageTypes.save_default_messages + MessageTypes.load_default_messages if safeExists?("Data/messages_core.dat") Console.echo_done(true) Console.echo_li(_INTL("Reloading cache...")) System.reload_cache diff --git a/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb b/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb index 912504ec6..c4f59bc83 100644 --- a/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb +++ b/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb @@ -90,9 +90,9 @@ module Compiler end point_names.uniq! interest_names.uniq! - MessageTypes.setMessages(MessageTypes::RegionNames, region_names) - MessageTypes.setMessagesAsHash(MessageTypes::PlaceNames, point_names) - MessageTypes.setMessagesAsHash(MessageTypes::PlaceDescriptions, interest_names) + MessageTypes.setMessagesAsHash(MessageTypes::Regions, region_names) + MessageTypes.setMessagesAsHash(MessageTypes::RegionLocations, point_names) + MessageTypes.setMessagesAsHash(MessageTypes::RegionDescriptions, interest_names) end #============================================================================= @@ -199,7 +199,7 @@ module Compiler ability_descriptions.push(ability.real_description) end MessageTypes.setMessagesAsHash(MessageTypes::Abilities, ability_names) - MessageTypes.setMessagesAsHash(MessageTypes::AbilityDescs, ability_descriptions) + MessageTypes.setMessagesAsHash(MessageTypes::AbilityDescriptions, ability_descriptions) end #============================================================================= @@ -262,8 +262,8 @@ module Compiler end MessageTypes.setMessagesAsHash(MessageTypes::Items, item_names) MessageTypes.setMessagesAsHash(MessageTypes::ItemPlurals, item_names_plural) - MessageTypes.setMessagesAsHash(MessageTypes::ItemPortionNames, item_portion_names) - MessageTypes.setMessagesAsHash(MessageTypes::ItemPortionNamePlurals, item_portion_names_plural) + MessageTypes.setMessagesAsHash(MessageTypes::ItemPortions, item_portion_names) + MessageTypes.setMessagesAsHash(MessageTypes::ItemPortionPlurals, item_portion_names_plural) MessageTypes.setMessagesAsHash(MessageTypes::ItemDescriptions, item_descriptions) end @@ -370,9 +370,9 @@ module Compiler species_pokedex_entries.push(species.real_pokedex_entry) end MessageTypes.setMessagesAsHash(MessageTypes::Species, species_names) - MessageTypes.setMessagesAsHash(MessageTypes::FormNames, species_form_names) - MessageTypes.setMessagesAsHash(MessageTypes::Kinds, species_categories) - MessageTypes.setMessagesAsHash(MessageTypes::Entries, species_pokedex_entries) + MessageTypes.setMessagesAsHash(MessageTypes::SpeciesForms, species_form_names) + MessageTypes.setMessagesAsHash(MessageTypes::SpeciesCategories, species_categories) + MessageTypes.setMessagesAsHash(MessageTypes::PokedexEntries, species_pokedex_entries) end #============================================================================= @@ -508,9 +508,9 @@ module Compiler species_categories.push(species.real_category) species_pokedex_entries.push(species.real_pokedex_entry) end - MessageTypes.addMessagesAsHash(MessageTypes::FormNames, species_form_names) - MessageTypes.addMessagesAsHash(MessageTypes::Kinds, species_categories) - MessageTypes.addMessagesAsHash(MessageTypes::Entries, species_pokedex_entries) + MessageTypes.addMessagesAsHash(MessageTypes::SpeciesForms, species_form_names) + MessageTypes.addMessagesAsHash(MessageTypes::SpeciesCategories, species_categories) + MessageTypes.addMessagesAsHash(MessageTypes::PokedexEntries, species_pokedex_entries) end #============================================================================= @@ -908,7 +908,7 @@ module Compiler lose_texts.push(trainer.real_lose_text) end MessageTypes.setMessagesAsHash(MessageTypes::TrainerNames, trainer_names) - MessageTypes.setMessagesAsHash(MessageTypes::TrainerLoseText, lose_texts) + MessageTypes.setMessagesAsHash(MessageTypes::TrainerLoseTexts, lose_texts) end #============================================================================= @@ -932,9 +932,9 @@ module Compiler } end sections = [] - MessageTypes.setMessagesAsHash(MessageTypes::BeginSpeech, []) - MessageTypes.setMessagesAsHash(MessageTypes::EndSpeechWin, []) - MessageTypes.setMessagesAsHash(MessageTypes::EndSpeechLose, []) + MessageTypes.setMessagesAsHash(MessageTypes::FrontierIntroSpeeches, []) + MessageTypes.setMessagesAsHash(MessageTypes::FrontierEndSpeechesWin, []) + MessageTypes.setMessagesAsHash(MessageTypes::FrontierEndSpeechesLose, []) File.open(path, "rb") { |f| FileLineData.file = path idx = 0 @@ -1022,9 +1022,9 @@ module Compiler } end MessageTypes.addMessagesAsHash(MessageTypes::TrainerNames, trainernames) - MessageTypes.addMessagesAsHash(MessageTypes::BeginSpeech, beginspeech) - MessageTypes.addMessagesAsHash(MessageTypes::EndSpeechWin, endspeechwin) - MessageTypes.addMessagesAsHash(MessageTypes::EndSpeechLose, endspeechlose) + MessageTypes.addMessagesAsHash(MessageTypes::FrontierIntroSpeeches, beginspeech) + MessageTypes.addMessagesAsHash(MessageTypes::FrontierEndSpeechesWin, endspeechwin) + MessageTypes.addMessagesAsHash(MessageTypes::FrontierEndSpeechesLose, endspeechlose) return sections end @@ -1153,7 +1153,7 @@ module Compiler # Get map names for translating map_names = [] GameData::MapMetadata.each { |map| map_names[map.id] = map.real_name } - MessageTypes.setMessages(MessageTypes::MapNames, map_names) + MessageTypes.setMessagesAsHash(MessageTypes::MapNames, map_names) end #============================================================================= diff --git a/Data/Scripts/999_Main/999_Main.rb b/Data/Scripts/999_Main/999_Main.rb index 2e9bcfb78..bb021dbe0 100644 --- a/Data/Scripts/999_Main/999_Main.rb +++ b/Data/Scripts/999_Main/999_Main.rb @@ -24,7 +24,7 @@ end def mainFunctionDebug begin - MessageTypes.loadMessageFile("Data/messages.dat") if safeExists?("Data/messages.dat") + MessageTypes.load_default_messages if safeExists?("Data/messages_core.dat") PluginManager.runPlugins Compiler.main Game.initialize