diff --git a/scripts/Data_Settings.rb b/scripts/Data_Settings.rb index 811f1f7..a2b12a1 100644 --- a/scripts/Data_Settings.rb +++ b/scripts/Data_Settings.rb @@ -125,262 +125,262 @@ end class Window_Settings # if parameter is linked to data in class Settings, then default value in this structure is ignored DATA = { - tr("Audio") => [ + "Audio" => [ { :type => :slider, - :name => tr('BGM Volume'), + :name => "BGM Volume", :parameter => :bgm_volume, :min => 0, :max => 100 }, { :type => :slider, - :name => tr('SFX Volume'), + :name => "SFX Volume", :parameter => :sfx_volume, :min => 0, :max => 100 }, { :type => :bool, - :name => tr('ITS TIME FOR FIGHT CRIME'), + :name => "ITS TIME FOR FIGHT CRIME", :parameter => :use_fight_crime_track }, ], - tr("Video") => [ - { :type => :sep, :name => tr("Screen")}, + "Video" => [ + { :type => :sep, :name => "Screen"}, { :type => :enum, - :name => tr('Resolution'), + :name => "Resolution", :default => 0, :parameter => Graphics::RESOLUTION_OVERRIDDEN ? nil : :resolution, :values => Graphics::RESOLUTION_OVERRIDDEN ? ["Resolution is redefined via config"] : Graphics.resolutions_names_list }, { :type => :bool, - :name => tr('Fullscreen'), + :name => "Fullscreen", :parameter => :fullscreen }, { :type => :bool, - :name => tr('Frameskip'), + :name => "Frameskip", :parameter => :frameskip }, { :type => :enum, - :name => tr('Scaling mode'), + :name => "Scaling mode", :parameter => :scaling_mode, - :values => [tr("Nearest Neighbor"), tr("Smooth(old)")] + :values => ["Nearest Neighbor", "Smooth(old)"] }, { :type => :bool, - :name => tr('Dynamic Light'), + :name => "Dynamic Light", :parameter => :light, }, { :type => :bool, - :name => tr('World machine shader'), + :name => "World machine shader", :parameter => :twm_shader }, { :type => :bool, - :name => tr('Colorblind mode'), + :name => "Colorblind mode", :parameter => :colorblind }, ], - tr("UI") => [ + "UI" => [ { :type => :bool, - :name => tr('In-Game Timer'), + :name => "In-Game Timer", :parameter => :in_game_timer }, { :type => :enum, - :name => tr('Language'), + :name => "Language", :parameter => :language, :values => Language::LANGUAGES }, { :type => :enum, - :name => tr('FastTravel UI'), + :name => "FastTravel UI", :parameter => :fasttravel_ui, - :values => [tr("Original"), tr("WME (WIP)")] + :values => ["Original", "WME (WIP)"] }, ], - tr("Gameplay") => [ + "Gameplay" => [ { :type => :enum, - :name => tr('Default movement'), + :name => "Default movement", :parameter => :movement, - :values => [tr("Walk"), tr("Run")] + :values => ["Walk", "Run"] }, { :type => :bool, - :name => tr('Skip Text (R)'), + :name => "Skip Text (R)", :parameter => :skip_text }, { :type => :bool, - :name => tr('Purple message box for Entity'), + :name => "Purple message box for Entity", :parameter => :en_purple_messagebox }, { :type => :bool, - :name => tr('Enforce april fools'), + :name => "Enforce april fools", :parameter => :enforce_april_fools }, { :type => :bool, - :name => tr('True Memory Mode'), + :name => "True Memory Mode", :parameter => :true_memory_mode }, { :type => :bool, - :name => tr('Freeware Mode(!)'), + :name => "Freeware Mode(!)", :parameter => :oneshot_mode }, { :type => :bool, - :name => tr('World machine shader on Entity footprints'), + :name => "World machine shader on Entity footprints", :parameter => :twm_shader_footprint }, ], - tr("Controls") => [ + "Controls" => [ { :type => :bool, - :name => tr('Control LED lighting on gamepads'), + :name => 'Control LED lighting on gamepads', :parameter => :gamepad_led }, - { :type => :sep, :name => tr("Walk") }, + { :type => :sep, :name => "Walk" }, { :type => :key, - :name => tr("Walk Down"), + :name => "Walk Down", :parameter => :controls_walk_down, :bind => Input::DOWN }, { :type => :key, - :name => tr("Walk Left"), + :name => "Walk Left", :parameter => :controls_walk_left, :bind => Input::LEFT }, { :type => :key, - :name => tr("Walk Right"), + :name => "Walk Right", :parameter => :controls_walk_right, :bind => Input::RIGHT }, { :type => :key, - :name => tr("Walk Up"), + :name => "Walk Up", :parameter => :controls_walk_up, :bind => Input::UP }, { :type => :key, - :name => tr("Run"), + :name => "Run", :parameter => :controls_run, :bind => Input::RUN }, - { :type => :sep, :name => tr("Actions") }, + { :type => :sep, :name => "Actions" }, { :type => :key, - :name => tr("Action"), + :name => "Action", :parameter => :controls_action, :bind => Input::ACTION }, { :type => :key, - :name => tr("Deactivate"), + :name => "Deactivate", :parameter => :controls_deactivate, :bind => Input::DEACTIVATE }, { :type => :key, - :name => tr("Cancel"), + :name => "Cancel", :parameter => :controls_cancel, :bind => Input::CANCEL }, { :type => :key, - :name => tr("Menu"), + :name => "Menu", :parameter => :controls_menu, :bind => Input::MENU }, { :type => :key, - :name => tr("Items"), + :name => "Items", :parameter => :controls_items, :bind => Input::ITEMS }, { :type => :key, - :name => tr("Nav Left"), + :name => "Nav Left", :parameter => :controls_nav_left, :bind => Input::L }, { :type => :key, - :name => tr("Nav Right"), + :name => "Nav Right", :parameter => :controls_nav_right, :bind => Input::R }, - #{ :type => :sep, :name => tr("Other") }, + #{ :type => :sep, :name => "Other" }, { :type => :action, - :name => tr("Reset Controls"), + :name => "Reset Controls", :action => Settings.method(:reset_controls!) }, ], - tr("Advanced") => [ + "Advanced" => [ { :type => :bool, - :name => tr('Crashlog privacy'), + :name => "Crashlog privacy", :parameter => :crashlog_privacy }, ], - tr("Debug") => [ + "Debug" => [ { :type => :bool, - :name => tr('Debug mode(!)'), + :name => "Debug mode(!)", :parameter => :debug }, { :type => :bool, - :name => tr('Show debug character'), + :name => "Show debug character", :parameter => :debug_character }, { :type => :bool, - :name => tr('Draw debug text in main menu'), + :name => "Draw debug text in main menu", :parameter => :debug_text_scene_title }, { :type => :bool, - :name => tr('Show debug text'), + :name => "Show debug text", :parameter => :debug_text }, { :type => :bool, - :name => tr('Show picture names'), + :name => "Show picture names", :parameter => :debug_picture_names }, { :type => :bool, - :name => tr('Debug lightmap'), + :name => "Debug lightmap", :parameter => :debug_lightmap }, { :type => :sep }, { :type => :key, - :name => tr("Debug"), + :name => "Debug", :parameter => :controls_debug, :bind => Input::DEBUGACTION }, { :type => :sep }, { :type => :action, - :name => tr("Clear image cache"), + :name => "Clear image cache", :action => Proc.new { RPG::Cache.clear } } ], diff --git a/scripts/Data_Settings_Classes.rb b/scripts/Data_Settings_Classes.rb index e67cc3b..03c07e2 100644 --- a/scripts/Data_Settings_Classes.rb +++ b/scripts/Data_Settings_Classes.rb @@ -66,6 +66,9 @@ class Window_Settings @switch_panels_hint_left.opacity = @switch_panels_hint_right.opacity = 127 @switch_panels_hint_left.blend_type = @switch_panels_hint_right.blend_type = 1 + Language.register_text_sprite("settings_switch_panels_hint_left", @switch_panels_hint_left) + Language.register_text_sprite("settings_switch_panels_hint_right", @switch_panels_hint_right) + redraw_panels_hints @parameters = {} @@ -93,12 +96,14 @@ class Window_Settings @parameters[name] = [] screen_panel = Sprite.new(@viewport) - screen_panel.bitmap = Bitmap.new(name.length * 10 + SCREENS_PANELS_MARGIN, @screen_panel_selection_sprite.height) - screen_panel.bitmap.draw_text(SCREENS_PANELS_MARGIN / 2, 0, screen_panel.width, screen_panel.height, name, 1) + screen_panel.bitmap = Bitmap.new(@switch_panels_hint_left.bitmap.text_size(tr(name)).width + SCREENS_PANELS_MARGIN, @screen_panel_selection_sprite.height) + screen_panel.bitmap.draw_text(SCREENS_PANELS_MARGIN / 2, 0, screen_panel.width, screen_panel.height, tr(name), 1) screen_panel.y = @screen_panel_selection_sprite.y screen_panel.x = Graphics.width / 2 + @next_screen_panel_offset - (@screen_panels[0] ? @screen_panels[0].width / 2 : screen_panel.width / 2) @next_screen_panel_offset += screen_panel.width @screen_panels << screen_panel + + Language.register_text_sprite("settings_screen_panel #{name}", screen_panel) end def add_parameter(sreen_name, parameter) @@ -174,9 +179,18 @@ class Window_Settings parameter.update end end + redraw_panels_hints + @parameters.keys.each_with_index do |name, index| + screen_panel = @screen_panels[index] + screen_panel.bitmap.dispose + screen_panel.bitmap = Bitmap.new(@switch_panels_hint_left.bitmap.text_size(tr(name)).width + SCREENS_PANELS_MARGIN, @screen_panel_selection_sprite.height) + screen_panel.bitmap.draw_text(SCREENS_PANELS_MARGIN / 2, 0, screen_panel.width, screen_panel.height, tr(name), 1) + end end def redraw_panels_hints + @switch_panels_hint_right.bitmap.clear + @switch_panels_hint_left.bitmap.clear @switch_panels_hint_right.bitmap.draw_text(0, 0, @switch_panels_hint_right.bitmap.width, @switch_panels_hint_right.bitmap.height, "W→", 2) @switch_panels_hint_left.bitmap.draw_text(0, 0, @switch_panels_hint_left.bitmap.width, @switch_panels_hint_left.bitmap.height, "←Q") end @@ -308,6 +322,8 @@ class Window_Settings @sprite.bitmap = Bitmap.new(PARAMETER_WIDTH, PARAMETER_HEIGHT + 1) @sprite.bitmap.font.size = 20 + Language.register_text_sprite("settings_parameter #{screen_id}-#{position}", @sprite) + @value_width = PARAMETER_VALUE_WIDTH self.value = self.value # appling init settings redraw @@ -351,8 +367,8 @@ class Window_Settings def redraw() @sprite.bitmap.clear - @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width - @value_width, @sprite.bitmap.height, @name) - @sprite.bitmap.draw_text(@sprite.bitmap.width - @value_width, 0, @value_width, @sprite.bitmap.height, get_display_value, 2) + @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width - @value_width, @sprite.bitmap.height, tr(@name)) + @sprite.bitmap.draw_text(@sprite.bitmap.width - @value_width, 0, @value_width, @sprite.bitmap.height, tr(get_display_value), 2) if (@settings_content.need_draw_line(@screen_id, @position)) @sprite.bitmap.fill_rect(Rect.new(0, PARAMETER_HEIGHT - 1, PARAMETER_WIDTH, 2), Color.new(255, 255, 255, 24)) end @@ -386,9 +402,10 @@ class Window_Settings def redraw() @sprite.bitmap.clear @sprite.bitmap.fill_rect(Rect.new(0, PARAMETER_HEIGHT / 2 - 1, PARAMETER_WIDTH, 2), Color.new(255, 255, 255, 64)) - if (@name != "") - @sprite.bitmap.clear_rect(Rect.new((PARAMETER_WIDTH - @name.length * 10) / 2 - 8, 0, @name.length * 10 + 16, PARAMETER_HEIGHT)) - @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width, @sprite.bitmap.height, @name, 1) + if (@name && @name != "") + name = tr(@name) + @sprite.bitmap.clear_rect(Rect.new((PARAMETER_WIDTH - name.length * 10) / 2 - 8, 0, name.length * 10 + 16, PARAMETER_HEIGHT)) + @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width, @sprite.bitmap.height, name, 1) end end end @@ -410,7 +427,7 @@ class Window_Settings end def get_display_value - return self.value ? tr("ON") : tr("OFF") + return self.value ? "ON" : "OFF" end def value_left() @@ -495,7 +512,7 @@ class Window_Settings @sprite.bitmap.clear @sprite.bitmap.font.color = Color.new(255, 255, 255) - @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width - @value_width, @sprite.bitmap.height, @name) + @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width - @value_width, @sprite.bitmap.height, tr(@name)) percent = (self.value.to_f - @min_value.to_f) / @max_value.to_f width = (@value_width * percent).to_i @@ -561,7 +578,7 @@ class Window_Settings end def get_display_value - @texts&.[](self.value) || "Unknown value #{self.value}" + @texts&.[](self.value) || "#{tr("Unknown value")} #{self.value}" end def value_left() @@ -599,25 +616,25 @@ class Window_Settings key_bind = @key_binds[bind_id] if key_bind == nil - return tr("None") + return "None" end case key_bind.type when KeyBind::Type::Invalid - return tr("Invalid") + return "Invalid" when KeyBind::Type::Key - return tr(Input::key_name(key_bind.scancode)) + return Input::key_name(key_bind.scancode) when KeyBind::Type::CButton - return tr(Input::c_button_name(key_bind.button)) + return Input::c_button_name(key_bind.button) when KeyBind::Type::CAxis axis_name = Input::c_axis_name(key_bind.axis) dir_vert = axis_name.downcase.include?("y") dir_horiz = axis_name.downcase.include?("x") - return tr(axis_name + + return axis_name + if dir_horiz || dir_vert " " + if dir_vert @@ -625,16 +642,16 @@ class Window_Settings else (key_bind.dir == 1 ? "Right" : "Left") end - else "" end) + else "" end when KeyBind::Type::JButton return tr("Joystick Button") + " " + key_bind.button.to_s when KeyBind::Type::JAxis - return tr("Joystick Axis") + " " + key_bind.axis.to_s + " " + key_bind.dir == 1 ? "Positive" : "Negative" + return tr("Joystick Axis") + " " + key_bind.axis.to_s + " " + key_bind.dir == 1 ? tr("Positive") : tr("Negative") when KeyBind::Type::JHat - return tr("Joystick Hat") + " " + key_bind.hat.to_s + " " + key_bind.pos == 1 ? "Positive" : "Negative" + return tr("Joystick Hat") + " " + key_bind.hat.to_s + " " + key_bind.pos == 1 ? tr("Positive") : tr("Negative") end end @@ -652,11 +669,11 @@ class Window_Settings @sprite.bitmap.draw_text(@sprite.bitmap.width - PARAMETER_KEY_WIDTH * (3 - @selection) - SELECTED_KEYS_MARGIN, 0, SELECTED_KEYS_MARGIN, @sprite.bitmap.height, "←", 1) end - @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width - PARAMETER_KEY_WIDTH * 4, @sprite.bitmap.height, @name) + @sprite.bitmap.draw_text(0, 0, @sprite.bitmap.width - PARAMETER_KEY_WIDTH * 4, @sprite.bitmap.height, tr(@name)) for i in 0..3 offset = (@selected && i == @selection ? SELECTED_KEYS_MARGIN : 4) parameter_x = @sprite.bitmap.width - PARAMETER_KEY_WIDTH * (4 - i) + offset - @sprite.bitmap.draw_text(parameter_x, 0, PARAMETER_KEY_WIDTH - offset * 2, @sprite.bitmap.height, @waiting_for_key && i == @selection ? tr("Press a key") : get_display_value(i), 1) + @sprite.bitmap.draw_text(parameter_x, 0, PARAMETER_KEY_WIDTH - offset * 2, @sprite.bitmap.height, @waiting_for_key && i == @selection ? tr("Press a key") : tr(get_display_value(i)), 1) @sprite.bitmap.fill_rect(Rect.new(@sprite.bitmap.width - PARAMETER_KEY_WIDTH * (4 - i) - 1, 1, 2, @sprite.bitmap.height - 2), Color.new(255, 255, 255, 32)) end diff --git a/scripts/Data_Settings_Setters.rb b/scripts/Data_Settings_Setters.rb index 58cb5ba..4b6067a 100644 --- a/scripts/Data_Settings_Setters.rb +++ b/scripts/Data_Settings_Setters.rb @@ -54,8 +54,8 @@ module Settings end end def language(value) - # idk how to set language - # TODO: fix languages on new ruby versions + $persistent.lang = Language::LANGUAGES[value] + $scene&.redraw end def movement(value) diff --git a/scripts/Persistent.rb b/scripts/Persistent.rb index 41aa721..4d5c027 100644 --- a/scripts/Persistent.rb +++ b/scripts/Persistent.rb @@ -19,14 +19,14 @@ class LanguageCode end if parts.length == 1 - @lang = parts[0].downcase.to_sym + @lang = parts[0].downcase @region = nil @full = @lang elsif parts.length == 2 - @lang = parts[0].downcase.to_sym - @region = parts[1].upcase.to_sym + @lang = parts[0].downcase + @region = parts[1].upcase full_str = parts[0].downcase + '_' + parts[1].upcase - @full = full_str.to_sym + @full = full_str else raise "malformed language code: #{str}" end diff --git a/scripts/Scene_Map.rb b/scripts/Scene_Map.rb index 68858c3..fee0cdc 100644 --- a/scripts/Scene_Map.rb +++ b/scripts/Scene_Map.rb @@ -446,4 +446,10 @@ class Scene_Map def menu_open? @menu.visible || @item_menu.visible end + + def redraw + if @window_settings + @window_settings.redraw_all + end + end end diff --git a/scripts/Scene_Title.rb b/scripts/Scene_Title.rb index d5de7a4..480f4f8 100644 --- a/scripts/Scene_Title.rb +++ b/scripts/Scene_Title.rb @@ -73,14 +73,9 @@ class Scene_Title @menu = Sprite.new(@viewport) @menu.z += 1 @menu.bitmap = Bitmap.new(MENU_X, MENU_Y) - @menu.bitmap.draw_text(0, 0, MENU_X, ENTRY_HEIGHT - 1, tr("Start")) - @menu.bitmap.draw_text(0, ENTRY_HEIGHT, MENU_X, ENTRY_HEIGHT - 1, tr("Settings")) - @menu.bitmap.draw_text(0, ENTRY_HEIGHT * 2, MENU_X, ENTRY_HEIGHT - 1, tr("Exit")) - if $game_switches[160] && $game_switches[152] - @menu.bitmap.draw_text(0, ENTRY_HEIGHT * 3, 150, 24, tr("...")) - end @menu.x = Graphics.width - MENU_X @menu.y = Graphics.height - MENU_Y + redraw_menu @debug = Sprite.new(@viewport) @debug.x = 5 @@ -88,15 +83,10 @@ class Scene_Title @debug.z += 1 @debug.visible = Settings[:debug_text_scene_title] || false @debug.bitmap = Bitmap.new(200, ENTRY_HEIGHT * 6) - @debug.bitmap.draw_text(0, 0, 200, ENTRY_HEIGHT, tr("Ruby #{RUBY_VERSION}")) - @debug.bitmap.draw_text(0, ENTRY_HEIGHT, 200, ENTRY_HEIGHT, tr("SDL #{SDLVer}")) - @debug.bitmap.draw_text(0, ENTRY_HEIGHT * 2, 200, ENTRY_HEIGHT, tr("Sunshine #{SunshineVer}")) - @debug.bitmap.draw_text(0, ENTRY_HEIGHT * 3, 200, ENTRY_HEIGHT, tr("sec_#{Sunshine::SECURITYSTATE}")) - if ModLoader::IS_ENABLED - @debug.bitmap.draw_text(0, ENTRY_HEIGHT * 5, 200, ENTRY_HEIGHT, tr("Mods loaded: #{ModLoader::COUNT}")) - end + redraw_debug - Language.register_text_sprite(self.class.name + "_contents", @menu.bitmap) + Language.register_text_sprite(self.class.name + "_contents", @menu) + Language.register_text_sprite(self.class.name + "_debug", @debug) # Make cursor graphic @cursor = Sprite.new(@viewport) @@ -171,6 +161,35 @@ class Scene_Title # Run automatic change for BGM and BGS set with map $game_map.autoplay end + + def redraw_menu + if !@menu + return + end + + @menu.bitmap.clear + @menu.bitmap.draw_text(0, 0, MENU_X, ENTRY_HEIGHT - 1, tr("Start")) + @menu.bitmap.draw_text(0, ENTRY_HEIGHT, MENU_X, ENTRY_HEIGHT - 1, tr("Settings")) + @menu.bitmap.draw_text(0, ENTRY_HEIGHT * 2, MENU_X, ENTRY_HEIGHT - 1, tr("Exit")) + if $game_switches[160] && $game_switches[152] + @menu.bitmap.draw_text(0, ENTRY_HEIGHT * 3, 150, 24, tr("...")) + end + end + + def redraw_debug + if !@debug + return + end + + @debug.bitmap.clear + @debug.bitmap.draw_text(0, 0, 200, ENTRY_HEIGHT, tr("Ruby") + " " + RUBY_VERSION) + @debug.bitmap.draw_text(0, ENTRY_HEIGHT, 200, ENTRY_HEIGHT, tr("SDL") + " " + SDLVer) + @debug.bitmap.draw_text(0, ENTRY_HEIGHT * 2, 200, ENTRY_HEIGHT, tr("Sunshine") + " " + SunshineVer) + @debug.bitmap.draw_text(0, ENTRY_HEIGHT * 3, 200, ENTRY_HEIGHT, tr("sec_#{Sunshine::SECURITYSTATE}")) + if ModLoader::IS_ENABLED + @debug.bitmap.draw_text(0, ENTRY_HEIGHT * 5, 200, ENTRY_HEIGHT, tr("Mods loaded:") + " " + ModLoader::COUNT.to_s) + end + end #-------------------------------------------------------------------------- # * Frame Update #-------------------------------------------------------------------------- @@ -303,4 +322,10 @@ class Scene_Title $game_system.se_play($data_system.decision_se) @window_settings_title.open end + + def redraw + redraw_menu + redraw_debug + @window_settings_title&.redraw_all + end end diff --git a/scripts/Window_Settings.rb b/scripts/Window_Settings.rb index df2ac54..2d3f721 100644 --- a/scripts/Window_Settings.rb +++ b/scripts/Window_Settings.rb @@ -122,6 +122,12 @@ class Window_Settings @fade_in = true end + def redraw_all + @title.bitmap.clear + @title.bitmap.draw_text(0, 0, @title.bitmap.width, @title.bitmap.height, tr("Settings"), 1) + @content.redraw_all + end + def redraw_setting(screen, index) if @visible == false return diff --git a/scripts/i18n_Language.rb b/scripts/i18n_Language.rb index 18e93ea..983636e 100644 --- a/scripts/i18n_Language.rb +++ b/scripts/i18n_Language.rb @@ -12,9 +12,10 @@ class Language def set(lc) @data = nil @tr = nil - [lc.full.to_s, lc.lang.to_s].each do |name| + [lc.full, lc.lang].each do |name| path = "Languages/#{name}.po" - if FileTest.exist?(path) + puts path + if File.exist?(path) load_pot(path) loadFontMap Font.default_name = @languageFontMap[name] @@ -71,7 +72,7 @@ class Language @data = Hash.new lastLineWasMsgId = false lastLineWasMsgStr = false - if FileTest.exist?(path) + if File.exist?(path) File.readlines(path, encoding: "UTF-8").each do |line| if line.start_with?("msgid ") line = line[6..-1] @@ -138,7 +139,7 @@ class Language if !@fontMapLoaded @languageFontMap = Hash.new path = "Languages/language_fonts.ini" - if FileTest.exist?(path) + if File.exist?(path) File.readlines(path, encoding: "UTF-8").each do |line| parts = line.split("=", 2) #print(parts[0].inspect) @@ -170,6 +171,10 @@ class Language @text_sprites = Hash.new() end @text_sprites[key] = spr + # we dont want memory leak + @text_sprites.delete_if do |_, ispr| + ispr.disposed? + end end def reset_fonts(sprites) @@ -208,6 +213,10 @@ class TrString def length to_str.length end + + def +(other) + to_str + other + end end def tr(text)