From 546f29f42e10edf94a90b96051453896cf2abe05 Mon Sep 17 00:00:00 2001 From: Mathew Velasquez Date: Sun, 28 Feb 2016 20:38:21 -0500 Subject: [PATCH] Preliminary i18n improvements --- binding-mri/binding-util.cpp | 9 ++--- binding-mri/oneshot-binding.cpp | 2 +- binding-mri/steam-binding.cpp | 5 +++ scripts/Ed_Message.rb | 2 +- scripts/Game_Oneshot.rb | 23 +----------- scripts/Main.rb | 3 ++ scripts/Persistent.rb | 63 +++++++++++++++++++++++++++++++++ scripts/SaveLoad.rb | 10 +++--- scripts/Scene_Title.rb | 2 -- scripts/_scripts.txt | 3 ++ scripts/i18n_Language.rb | 15 ++++---- scripts/i18n_Translator.rb | 31 +++++++++------- src/oneshot.cpp | 8 +++-- src/steam.cpp | 48 +++++++++++++++++++++++++ src/steam.h | 1 + 15 files changed, 166 insertions(+), 59 deletions(-) create mode 100644 scripts/Persistent.rb diff --git a/binding-mri/binding-util.cpp b/binding-mri/binding-util.cpp index e73d999..9af7925 100644 --- a/binding-mri/binding-util.cpp +++ b/binding-mri/binding-util.cpp @@ -150,8 +150,7 @@ rb_get_args(int argc, VALUE *argv, const char *format, ...) VALUE *str = va_arg(ap, VALUE*); VALUE tmp = *arg; - if (!RB_TYPE_P(tmp, RUBY_T_STRING)) - rb_raise(rb_eTypeError, "Argument %d: Expected string", argI); + tmp = rb_str_to_str(tmp); *str = tmp; ++argI; @@ -169,8 +168,7 @@ rb_get_args(int argc, VALUE *argv, const char *format, ...) VALUE tmp = *arg; - if (!RB_TYPE_P(tmp, RUBY_T_STRING)) - rb_raise(rb_eTypeError, "Argument %d: Expected string", argI); + tmp = rb_str_to_str(tmp); *s = RSTRING_PTR(tmp); *len = RSTRING_LEN(tmp); @@ -188,8 +186,7 @@ rb_get_args(int argc, VALUE *argv, const char *format, ...) VALUE tmp = *arg++; - if (!RB_TYPE_P(tmp, RUBY_T_STRING)) - rb_raise(rb_eTypeError, "Argument %d: Expected string", argI); + tmp = rb_str_to_str(tmp); *s = RSTRING_PTR(tmp); ++argI; diff --git a/binding-mri/oneshot-binding.cpp b/binding-mri/oneshot-binding.cpp index c9b2e04..bd31000 100644 --- a/binding-mri/oneshot-binding.cpp +++ b/binding-mri/oneshot-binding.cpp @@ -47,7 +47,7 @@ void oneshotBindingInit() //Constants rb_const_set(module, rb_intern("USER_NAME"), rb_str_new2(shState->oneshot().userName().c_str())); rb_const_set(module, rb_intern("SAVE_PATH"), rb_str_new2(shState->oneshot().savePath().c_str())); - rb_const_set(module, rb_intern("LANG"), ID2SYM(rb_intern(shState->oneshot().lang().c_str()))); + rb_const_set(module, rb_intern("LANG"), rb_str_new2(shState->oneshot().lang().c_str())); rb_const_set(msg, rb_intern("INFO"), INT2FIX(Oneshot::MSG_INFO)); rb_const_set(msg, rb_intern("YESNO"), INT2FIX(Oneshot::MSG_YESNO)); rb_const_set(msg, rb_intern("WARN"), INT2FIX(Oneshot::MSG_WARN)); diff --git a/binding-mri/steam-binding.cpp b/binding-mri/steam-binding.cpp index d00ec53..7d15073 100644 --- a/binding-mri/steam-binding.cpp +++ b/binding-mri/steam-binding.cpp @@ -65,8 +65,13 @@ void steamBindingInit() /* Constants */ #ifdef STEAM rb_const_set(module, rb_intern("USER_NAME"), rb_str_new2(shState->steam().userName().c_str())); + if (shState->steam().lang().empty()) + rb_const_set(module, rb_intern("LANG"), Qnil); + else + rb_const_set(module, rb_intern("LANG"), rb_str_new2(shState->steam().lang().c_str())); #else rb_const_set(module, rb_intern("USER_NAME"), Qnil); + rb_const_set(module, rb_intern("LANG"), Qnil); #endif /* Functions */ diff --git a/scripts/Ed_Message.rb b/scripts/Ed_Message.rb index e2c8838..ba26f5d 100644 --- a/scripts/Ed_Message.rb +++ b/scripts/Ed_Message.rb @@ -61,7 +61,7 @@ class Ed_Message widths = [] # Pre-process text - text_raw = $game_temp.message_ed_text + text_raw = $game_temp.message_ed_text.to_str # Substitute variables, actors, player name, newlines, etc text_raw.gsub!(/\\v\[([0-9]+)\]/) do diff --git a/scripts/Game_Oneshot.rb b/scripts/Game_Oneshot.rb index fe55d7b..c997ecd 100644 --- a/scripts/Game_Oneshot.rb +++ b/scripts/Game_Oneshot.rb @@ -6,34 +6,13 @@ class Game_Oneshot attr_accessor :plight_timer # start of plight's plight def initialize - user_name = Oneshot::USER_NAME.split(/\s+/) + user_name = (Steam.enabled? ? Steam::USER_NAME : Oneshot::USER_NAME).split(/\s+/) if user_name[0].casecmp('the') == 0 || user_name[0].casecmp('a') == 0 @player_name = user_name.join(' ') else @player_name = user_name[0] end @lights = {} - self.lang = Oneshot::LANG @plight_timer = nil end - - # lang - def lang - @lang - end - def lang=(val) - @lang = val - $tr = Translator.new(val) - $language = Language.get(val) - Font.default_name = $language.font - Oneshot.set_yes_no(tr('Yes'), tr('No')) - end - - # MARSHAL - def marshal_dump - [@player_name, @lang, @plight_timer] - end - def marshal_load(array) - @player_name, self.lang, @plight_timer = array - end end diff --git a/scripts/Main.rb b/scripts/Main.rb index 3423cb6..f795b47 100644 --- a/scripts/Main.rb +++ b/scripts/Main.rb @@ -8,6 +8,9 @@ begin Graphics.frame_rate = 60 Font.default_size = 20 + # Load persistent data + Persistent.load + # Prepare for transition Graphics.freeze # Make scene object (title screen) diff --git a/scripts/Persistent.rb b/scripts/Persistent.rb new file mode 100644 index 0000000..345f9aa --- /dev/null +++ b/scripts/Persistent.rb @@ -0,0 +1,63 @@ +# Persistent data independent from save file. +# Stores settings and the like. +class LanguageCode + attr_accessor :full + attr_accessor :lang + attr_accessor :region + + def initialize(str) + parts = str.split(/[_-]/) + if parts.size == 1 + @lang, @region = parts.first.downcase.to_sym, nil + @full = @lang + elsif parts.size == 2 + @lang, @region = parts.first.downcase.to_sym, parts.last.upcase.to_sym + @full = (parts.first.downcase + '_' + parts.last.upcase).to_sym + else + raise "malformed language code: #{str}" + end + end +end + +class Persistent + attr_reader :lang + + def initialize + self.lang = LanguageCode.new(Steam::LANG || Oneshot::LANG) + end + + # Members + def lang=(val) + @lang = val + $tr = Translator.new(@lang) + $lang = Language.get(@lang) + Font.default_name = $lang.font + Oneshot.set_yes_no(tr('Yes'), tr('No')) + end + + # MARSHAL + def marshal_dump + [@lang] + end + def marshal_load(array) + self.lang = array.first + end + + # Save/Load global instance of persistent + FILE_NAME = Oneshot::SAVE_PATH + '/persistent.dat' + def self.save + File.open(FILE_NAME, 'wb') do |file| + Marshal.dump($persistent, file) + end + end + + def self.load + if FileTest.exist?(FILE_NAME) + File.open(FILE_NAME, 'rb') do |file| + $persistent = Marshal.load(file) + end + else + $persistent = Persistent.new + end + end +end diff --git a/scripts/SaveLoad.rb b/scripts/SaveLoad.rb index cdbbe4e..645de69 100644 --- a/scripts/SaveLoad.rb +++ b/scripts/SaveLoad.rb @@ -1,9 +1,7 @@ -def save_file_name - Oneshot::SAVE_PATH + '/save.dat' -end +SAVE_FILE_NAME = Oneshot::SAVE_PATH + '/save.dat' def save - File.open(save_file_name, 'wb') do |file| + File.open(SAVE_FILE_NAME, 'wb') do |file| # Wrire frame count for measuring play time Marshal.dump(Graphics.frame_count, file) # Increase save count by 1 @@ -27,8 +25,8 @@ def save end def load - return false unless FileTest.exist?(save_file_name) - File.open(save_file_name, 'rb') do |file| + return false unless FileTest.exist?(SAVE_FILE_NAME) + File.open(SAVE_FILE_NAME, 'rb') do |file| # Read frame count for measuring play time Graphics.frame_count = Marshal.load(file) # Read each type of game object diff --git a/scripts/Scene_Title.rb b/scripts/Scene_Title.rb index a5e8c80..36dbd29 100644 --- a/scripts/Scene_Title.rb +++ b/scripts/Scene_Title.rb @@ -29,8 +29,6 @@ class Scene_Title # Load save game/initialize data $game_temp = Game_Temp.new new_game unless load - # Translate database items - $tr.translate_database # Make system object $game_system = Game_System.new # Skip title screen if debug mode diff --git a/scripts/_scripts.txt b/scripts/_scripts.txt index 1e9a683..ade732a 100644 --- a/scripts/_scripts.txt +++ b/scripts/_scripts.txt @@ -96,6 +96,9 @@ Scene_Battle 4 Scene_Name Scene_Debug +# Persistent data +Persistent + # Internationalization i18n_Translator i18n_Language diff --git a/scripts/i18n_Language.rb b/scripts/i18n_Language.rb index 1e6fd24..4571229 100644 --- a/scripts/i18n_Language.rb +++ b/scripts/i18n_Language.rb @@ -9,13 +9,16 @@ class Language @character_table_height = character_table_height end - def self.get(code = :en) - return LANGUAGES[code] || LANGUAGES[:en] + def self.get(lc) + return LANGUAGES[lc.full] || LANGUAGES[lc.lang] || LANGUAGES[:en] end + FONT_WESTERN = 'Terminus (TTF)' + FONT_CJK = 'WenQuanYi Micro Hei' + LANGUAGES = { :en => Language.new( - 'Terminus (TTF)', [ + FONT_WESTERN, [ "A","B","C","D","E", "F","G","H","I","J", "K","L","M","N","O", @@ -36,7 +39,7 @@ class Language ], 8 ), :es => Language.new( - 'Terminus (TTF)', [ + FONT_WESTERN, [ "A","B","C","D","E", "F","G","H","I","J", "K","L","M","N","O", @@ -59,7 +62,7 @@ class Language ], 9 ), :ja => Language.new( - 'WenQuanYi Micro Hei', [ + FONT_CJK, [ "あ","い","う","え","お", "か","き","く","け","こ", "さ","し","す","せ","そ", @@ -102,7 +105,7 @@ class Language ], 9 ), :ko => Language.new( - 'WenQuanYi Micro Hei', [ + FONT_CJK, [ "가","개","갸","거","게", "겨","고","교","구","규", "그","기","나","내","냐", diff --git a/scripts/i18n_Translator.rb b/scripts/i18n_Translator.rb index 348646d..4a814a2 100644 --- a/scripts/i18n_Translator.rb +++ b/scripts/i18n_Translator.rb @@ -11,14 +11,13 @@ end # Translator class: translate text to another language class Translator # Create Translator object - def initialize(code = :en) - if code == :en - @data = nil - else + def initialize(lc) + @data = nil + [lc.full.to_s, lc.lang.to_s].each do |name| begin - @data = load_data("Languages/#{code.to_s}.rxdata") + @data = load_data("Langauges/#{name}.rxdata") + break rescue - @data = nil end end end @@ -71,10 +70,18 @@ class Translator end end -# Hacky translation helper function -# def tr(text) -# $tr.script(Kernel.caller.first.split(':', 3)[1], text) -# end -def tr(text) - text +# Translatable string +class TrString + def initialize(str) + @str = str + end + + def to_str + @str + end + alias :to_s :to_str +end + +def tr(text) + TrString.new(text) end diff --git a/src/oneshot.cpp b/src/oneshot.cpp index 789a79e..2744053 100644 --- a/src/oneshot.cpp +++ b/src/oneshot.cpp @@ -227,7 +227,9 @@ Oneshot::Oneshot(const RGSSThreadData &threadData) //Get language code WCHAR wlang[9]; GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, wlang, sizeof(wlang) / sizeof(WCHAR)); - p->lang = w32_fromWide(wlang); + p->lang = w32_fromWide(wlang) + "_"; + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, wlang, sizeof(wlang) / sizeof(WCHAR)); + p->lang += w32_fromWide(wlang); //Get user's name ULONG size = 0; @@ -260,9 +262,9 @@ Oneshot::Oneshot(const RGSSThreadData &threadData) const char *code = (lc_all ? lc_all : lang); if (code) { - //find first non alphanumeric character, copy language code + //find first dot, copy language code int end = 0; - for (; code[end] && (code[end] >= 'a' && code[end] <= 'z'); ++end) {} + for (; code[end] && code[end] != '.'; ++end) {} p->lang = std::string(code, end); } else diff --git a/src/steam.cpp b/src/steam.cpp index fcadcea..80ec72d 100644 --- a/src/steam.cpp +++ b/src/steam.cpp @@ -5,6 +5,45 @@ #include #include "steamshim/steamshim_child.h" +/* Language map */ +static const std::map langToCode = { + {"brazilian", "pt_BR"}, + {"bulgarian", "bg"}, + {"czech", "cz"}, + {"danish", "da"}, + {"dutch", "nl"}, + {"english", "en"}, + {"finnish", "fi"}, + {"french", "fr"}, + {"german", "de"}, + {"greek", "el"}, + {"hungarian", "hu"}, + {"italian", "it"}, + {"japanese", "ja"}, + {"koreana", "ko"}, + {"korean", "ko"}, + {"norwegian", "no"}, + {"polish", "pl"}, + {"portuguese", "pt"}, + {"romanian", "ro"}, + {"russian", "ru"}, + {"schinese", "zh_CN"}, + {"spanish", "es"}, + {"swedish", "sv"}, + {"tchinese", "zh_TW"}, + {"thai", "th"}, + {"turkish", "tr"}, + {"ukrainian", "uk"}, +}; + +static std::string steamToIsoLang(const char *steamLang) +{ + auto it = langToCode.find(steamLang); + if (it != langToCode.end()) + return it->second; + return "en"; +} + /* Achievements */ static const char *const achievementNames[] = { "SaveWorld", @@ -18,6 +57,7 @@ struct SteamPrivate std::map achievements; std::string userName; + std::string lang; SteamPrivate() { @@ -66,6 +106,8 @@ struct SteamPrivate case SHIMEVENT_GETPERSONANAME: userName = e->name; break; + case SHIMEVENT_GETCURRENTGAMELANGUAGE: + lang = steamToIsoLang(e->name); default: break; } @@ -75,6 +117,7 @@ struct SteamPrivate bool initialized() { return !userName.empty() + && !lang.empty() && achievements.size() == NUM_ACHIEVEMENTS; } }; @@ -95,6 +138,11 @@ const std::string &Steam::userName() const return p->userName; } +const std::string &Steam::lang() const +{ + return p->lang; +} + void Steam::unlock(const char *name) { p->setAchievement(name, true); diff --git a/src/steam.h b/src/steam.h index bd03143..169eaf8 100644 --- a/src/steam.h +++ b/src/steam.h @@ -13,6 +13,7 @@ public: bool isUnlocked(const char *name); const std::string &userName() const; + const std::string &lang() const; private: Steam();