#===============================================================================
# * Localization
#
#
#-------------------------------------------------------------------------------
# Description:
#
# Language Localization,
# supports most languages except chinese, korean and japanese
#-------------------------------------------------------------------------------
# Setup:
#
# Translate files in language folder
#
# Edit everything underneath
#-------------------------------------------------------------------------------
# Commands:
#
# Localization.change('behavior',\'HERE') Use either 'Strict' or 'Loose'
#
# Localization.change('method',\'HERE') Use either 'Cached' or 'Streaming'
#
# Localization.change('language',\'HERE') Change for a different language
#===============================================================================
module Localization
LOCALIZATION_DELIMITER = '=' #delimiter for each dialogue pairs
LOCALIZATION_DEFAULT = 'EN' #default language
LOCALIZATION_CREDITS_LINE = 1 #credits line number
LOCALIZATION_METHOD_LINE = 2 #used method
LOCALIZATION_START_LINE = 3 #starting line number
LOCALIZATION_FOLDER = 'language' #location of localization folder
LOCALIZATION_CONFIG = 'config.txt' #name of the configuration file
LOCALIZATION_TEXT_DEFAULT = 'empty' #replacement for empty values
LOCALIZATION_MAX_LINE = 4 #maximum lines allowed for each dialogue
MESSAGE_WINDOW_WIDTH = 380 #The maximum width of text in message windows
MESSAGE_WINDOW_HEIGHT = 160 #The maximum height of text in message windows
@method = 'cached' #Cached = already loaded streaming = constant loading
@behavior = 'loose' #strict = exit upon error loose = warning upon error
@dialogues = Hash.new #initialize container to store dialogues
@split_method = '>' #splitting method
@character_split = 1 #initialization method declaration
def self.init
@errors = {
:folder => nil,
:def_lang => nil,
:cur_lang => nil,
:save_lang => nil,
:change_lang => nil,
:bad_id => Array.new,
:duplicate => Array.new,
:empty => Array.new,
:line => Array.new,
:size => Array.new
}
#make sure localization folder exists
if !File.directory?(LOCALIZATION_FOLDER) then @errors[:folder] = 1 ; self.check end
#make sure default language file exists
if !File.exist?("#{LOCALIZATION_FOLDER}\\#{LOCALIZATION_DEFAULT}.txt") then @errors[:def_lang] = 1 ; self.check end
#set localization configuration file
@filename = "#{LOCALIZATION_FOLDER}\\#{LOCALIZATION_CONFIG}"
if !File.exists?(@filename)
begin
File.open(@filename, 'wb') {|file| file.write(LOCALIZATION_DEFAULT) }
rescue
@errors[:save_lang] = 1
self.check
end
end
change('language')
end
def self.read(id)
text = ''
case @method
when 'streaming'
text = ''
found = false
@file = File.new(@filename)
index = 1
@file.each { |line|
if !line.empty? && index >= LOCALIZATION_START_LINE && line.include?('=')
key = line.split(LOCALIZATION_DELIMITER)[0]
if !(key =~ /^(\w+)$/) then @errors[:bad_id].push(index) end
if @dialogues.has_key?(key.to_sym) then @errors[:duplicate].push(index) end
if key == id
val = line.slice(key.size+LOCALIZATION_DELIMITER.size,line.size).squeeze(" ")
if self.get('cur_width',val) < MESSAGE_WINDOW_WIDTH * LOCALIZATION_MAX_LINE
if self.get('cur_width',val) >= MESSAGE_WINDOW_WIDTH then
last = ''
adjusted_string = ''
run = 1
new_line = true
text_chunks = Array.new
line_chunks = Array.new
tolerance = 0
if @split_method == 'char' then @character_split = self.get('max_line_width') end
val.split('\n').each { |fragment| text_chunks.push(fragment) }
text_chunks = text_chunks.reverse
while text_chunks.size > 0
last = text_chunks[text_chunks.size - 1] + "\n"
text_chunks.pop
if self.get('cur_width',last) >= MESSAGE_WINDOW_WIDTH
if @split_method == 'word'
last.split(' ').each { |fragment2| line_chunks.push(fragment2) }
line_chunks = line_chunks.reverse
while line_chunks.size > 0
if self.get('cur_width',adjusted_string) + self.get('cur_width',line_chunks[line_chunks.size - 1]) - tolerance > MESSAGE_WINDOW_WIDTH * run then adjusted_string << "\n" ; run += 1 ; newline = true ; end
if newline == true
adjusted_string << line_chunks[line_chunks.size - 1]
newline = false
else
adjusted_string << ' ' + line_chunks[line_chunks.size - 1]
end
line_chunks.pop
end
elsif @split_method == 'char'
adjusted_string = last.scan(/.{#{@character_split}}|.+/).join("\n")
end
else
tolerance += self.get('cur_width',last)
adjusted_string << last
newline = true
end
end
val = adjusted_string.lstrip
end
if val.empty?
text = LOCALIZATION_TEXT_DEFAULT
elsif val.scan(/\n/).size - 1 > LOCALIZATION_MAX_LINE
text = "Warning, this text has too many lines, line #{index}"
else
text = val
end
else
text = "Warning, this text was too big, line #{index}"
end
found = true
end
end
index += 1
}
if !found then text = "ID[#{id}] not found" end
else
if @dialogues[id.to_sym] != nil
text = String.new(@dialogues[id.to_sym])
else
text = "invalid ID specified"
end
end
return text.chomp
end
def self.check()
@errors.each { |key,val|
if val != nil && val.class != Array
case key
when :folder
print "Localization folder is missing,\nthis game will not run without its contents"
exit
when :def_lang
print "Default language file is missing,\nthis game will not run without it"
exit
when :save_lang
#this error is very rare, only happen in a very strictly set UAC or intentional file modification by user
print "Unable to modify localization file\nPlease make sure this game has elevated priveleges"
print "Run the game as administrator\nOr simply place the game folder in other than C:/ drive"
exit
when :change_lang
print "Language has been changed into : #{val}"
when :cur_lang
if @behavior == 'strict'
print "#{@current_language} was not found\nMake sure the specified language file is present in Localization folder"
exit
else
print "#{@current_language} was not found, switching to default language"
end
end
elsif val != nil && val.class == Array
if val.size > 0
text = ''
val.each { |v|
if text.empty?
text << (v.to_s)
else
text << (', ' + v.to_s)
end
}
case key
when :bad_id
print "Bad IDs detected on line :\n#{text}\nAlphabets, numbers & Underscores only\n"
when :empty
print "Empty values detected on line :\n#{text}\nEmpty values will return 'empty value'\n"
when :duplicate
print "Duplicate keys detected on line :\n#{text}\nDuplicate values are be ignored\n"
when :line
print "Too many lines detected on line :\n#{text}\nThis game allow a maximum of #{LOCALIZATION_MAX_LINE} lines per dialogue\n"
when :size
print "Big text detected on line :\n#{text}\nThis game allow a maximum of #{MESSAGE_WINDOW_WIDTH*LOCALIZATION_MAX_LINE} total dialogue width\n"
end
if @behavior == 'strict' then exit end
end
end
}
@errors = {
:folder => nil,
:def_lang => nil,
:cur_lang => nil,
:save_lang => nil,
:change_lang => nil,
:bad_id => Array.new,
:duplicate => Array.new,
:empty => Array.new,
:line => Array.new,
:size => Array.new
}
end
#change different variables which will change how localization works in general
def self.change(subject,value = nil)
case subject
when 'behavior'
@behavior = value
when 'method'
@method = value
when 'language'
different_language = false
@filename = "#{LOCALIZATION_FOLDER}\\#{LOCALIZATION_CONFIG}"
@dialogues.clear
if value != nil && value != @current_language
begin
File.open(@filename, 'wb') {|file| file.write(value.upcase) }
rescue
@errors[:save_lang] = 1
self.check
end
different_language = true
end
@current_language = IO.readlines(@filename)[0].chomp.upcase
@filename = "#{LOCALIZATION_FOLDER}\\#{@current_language}.txt"
if !File.exists?(@filename)
@errors[:cur_lang] = 1
self.check
change('language',LOCALIZATION_DEFAULT)
return
end
#retrieve split method and language name from localization file
@split_method = IO.readlines(@filename)[LOCALIZATION_METHOD_LINE - 1].chomp
if @split_method != 'word' && @split_method != 'char' then @split_method = 'word' end
if @split_method == 'char' then @character_split = self.get('max_line_width') end
@language_name = IO.readlines(@filename)[LOCALIZATION_CREDITS_LINE - 1].split('-')[0]
if different_language then @errors[:change_lang] = @language_name ; self.check end
if @method != 'streaming'
@file = File.new(@filename)
index = 1
@file.each { |line|
if !line.empty? && index >= LOCALIZATION_START_LINE && line.include?('=')
key = line.split(LOCALIZATION_DELIMITER)[0]
if !(key =~ /^(\w+)$/)
@errors[:bad_id].push(index)
else
if @dialogues.has_key?(key.to_sym)
@errors[:duplicate].push(index)
else
val = line.slice(key.size+LOCALIZATION_DELIMITER.size,line.size).squeeze(" ")
if self.get('cur_width',val) < MESSAGE_WINDOW_WIDTH * LOCALIZATION_MAX_LINE
if self.get('cur_width',val) >= MESSAGE_WINDOW_WIDTH then
last = ''
adjusted_string = ''
run = 1
new_line = true
text_chunks = Array.new
line_chunks = Array.new
tolerance = 0
val.split('\n').each { |fragment| text_chunks.push(fragment) }
text_chunks = text_chunks.reverse
while text_chunks.size > 0
last = text_chunks[text_chunks.size - 1] + "\n"
text_chunks.pop
if self.get('cur_width',last) >= MESSAGE_WINDOW_WIDTH
if @split_method == 'word'
last.split(' ').each { |fragment2| line_chunks.push(fragment2) }
line_chunks = line_chunks.reverse
while line_chunks.size > 0
if self.get('cur_width',adjusted_string) + self.get('cur_width',line_chunks[line_chunks.size - 1]) - tolerance > MESSAGE_WINDOW_WIDTH * run then adjusted_string << "\n" ; run += 1 ; newline = true ; end
if newline == true
adjusted_string << line_chunks[line_chunks.size - 1]
newline = false
else
adjusted_string << ' ' + line_chunks[line_chunks.size - 1]
end
line_chunks.pop
end
elsif @split_method == 'char'
adjusted_string = last.scan(/.{#{@character_split}}|.+/).join("\n")
end
else
tolerance += self.get('cur_width',last)
adjusted_string << last
newline = true
end
end
val = adjusted_string.lstrip
end
if val.empty?
@errors[:empty].push(index)
elsif val.scan(/\n/).size - 1 > LOCALIZATION_MAX_LINE
@errors[:line].push(index)
else
@dialogues[key.to_sym] = val
end
else
@errors[:size].push(index)
end
end
end
end
index += 1
}
end
self.check
end
end
#get various attributes
def self.get(subject,value = nil)
case subject
when 'cur_width'; return Bitmap.new(1, 1).text_size(value).width
when 'cur_height'; return Bitmap.new(1, 1).text_size(value).height
when 'max_line_width'
multiplier = 1
text = 'あ' * multiplier
while Bitmap.new(1, 1).text_size(text).width < MESSAGE_WINDOW_WIDTH
multiplier += 1
text = 'あ' * multiplier
end
return multiplier
end
end
#scan any localization-related command and return its result to draw text method
def self.scan(command)
if command != nil
return command.gsub(/\\[Rr][Ee][Cc]\[(.+)\]/) { self.read($1) }
end
end
Localization.init
end
#-------------------------------------------------------------------------------
# - Over Ride
#-------------------------------------------------------------------------------
class Game_Temp
def message_text=(string)
@message_text = Localization.scan(string)
end
end
class Interpreter
alias old_command_101 command_101
def command_101
if $game_temp.message_text != nil
return false
end
@message_waiting = true
$game_temp.message_proc = Proc.new { @message_waiting = false }
$game_temp.message_text = Localization.scan(@list[@index].parameters[0]) + "\n"
line_count = $game_temp.message_text.scan(/\n/).size
loop do
if @list[@index+1].code == 401
$game_temp.message_text += @list[@index+1].parameters[0] + "\n"
line_count += 1
else
if @list[@index+1].code == 102
if @list[@index+1].parameters[0].size <= 4 - line_count
@index += 1
$game_temp.choice_start = line_count
setup_choices(@list[@index].parameters)
end
elsif @list[@index+1].code == 103
if line_count < 4
@index += 1
$game_temp.num_input_start = line_count
$game_temp.num_input_variable_id = @list[@index].parameters[0]
$game_temp.num_input_digits_max = @list[@index].parameters[1]
end
end
return true
end
@index += 1
end
end
end
class Bitmap
alias old_draw_text draw_text
def draw_text(*args)
args.each_index {|i| if args[i].is_a?(String) then args[i] = Localization.scan(args[i]) end }
old_draw_text(*args)
end
end