From f4ffd4110c996138bf2b0a652fbae9dbb08f0320 Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 9 Oct 2017 14:43:01 -0300 Subject: [PATCH 1/3] Start using fmt library This is the first step for a full i18n (#124). --- .gitmodules | 3 +++ docs/LICENSES.md | 28 ++++++++++++++++++++++++++++ src/app/CMakeLists.txt | 3 ++- src/app/i18n/strings.h | 4 +--- src/app/ui/home_view.cpp | 6 ++++-- third_party/CMakeLists.txt | 1 + third_party/fmt | 1 + 7 files changed, 40 insertions(+), 6 deletions(-) create mode 160000 third_party/fmt diff --git a/.gitmodules b/.gitmodules index 847afe966..49148de02 100644 --- a/.gitmodules +++ b/.gitmodules @@ -57,3 +57,6 @@ [submodule "third_party/giflib"] path = third_party/giflib url = https://github.com/aseprite/giflib.git +[submodule "third_party/fmt"] + path = third_party/fmt + url = https://github.com/aseprite/fmt.git diff --git a/docs/LICENSES.md b/docs/LICENSES.md index 87f95fe60..b5e9a9545 100644 --- a/docs/LICENSES.md +++ b/docs/LICENSES.md @@ -230,6 +230,34 @@ be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. ``` +# [fmt](https://github.com/fmtlib/fmt) + +``` +Copyright (c) 2012 - 2016, Victor Zverovich + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` + # [FreeType](http://www.freetype.org/) ``` diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index bbf406e48..d7915ddc4 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -554,7 +554,8 @@ target_link_libraries(app-lib ${FREETYPE_LIBRARIES} ${HARFBUZZ_LIBRARIES} json11 - archive_static) + archive_static + fmt) if(ENABLE_SCRIPTING) target_link_libraries(app-lib script-lib) diff --git a/src/app/i18n/strings.h b/src/app/i18n/strings.h index c85bc2a5b..b371f41b0 100644 --- a/src/app/i18n/strings.h +++ b/src/app/i18n/strings.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2016 David Capello +// Copyright (C) 2016-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -29,8 +29,6 @@ namespace app { std::unordered_map m_strings; }; - #define tr(id) (Strings::instance()->translate(id)) - } // namespace app #endif diff --git a/src/app/ui/home_view.cpp b/src/app/ui/home_view.cpp index 524480008..beddabc74 100644 --- a/src/app/ui/home_view.cpp +++ b/src/app/ui/home_view.cpp @@ -24,6 +24,7 @@ #include "app/ui_context.h" #include "base/bind.h" #include "base/exception.h" +#include "fmt/format.h" #include "ui/label.h" #include "ui/resize_event.h" #include "ui/system.h" @@ -143,7 +144,7 @@ void HomeView::onCheckingUpdates() void HomeView::onUpToDate() { - checkUpdate()->setText(PACKAGE " is up to date"); + checkUpdate()->setText(fmt::format("{0} is up to date", PACKAGE)); checkUpdate()->setVisible(true); layout(); @@ -151,7 +152,8 @@ void HomeView::onUpToDate() void HomeView::onNewUpdate(const std::string& url, const std::string& version) { - checkUpdate()->setText("New " PACKAGE " v" + version + " available!"); + checkUpdate()->setText(fmt::format("New {0} v{1} available!", + PACKAGE, version)); checkUpdate()->setUrl(url); checkUpdate()->setVisible(true); checkUpdate()->InitTheme.connect( diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 596ffa2e4..bda297fa6 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -88,6 +88,7 @@ endif() add_subdirectory(harfbuzz-cmake) add_subdirectory(simpleini) +add_subdirectory(fmt) # Add cmark without tests if(NOT USE_SHARED_CMARK) diff --git a/third_party/fmt b/third_party/fmt new file mode 160000 index 000000000..933a33a79 --- /dev/null +++ b/third_party/fmt @@ -0,0 +1 @@ +Subproject commit 933a33a7948c3d9bb984473b9b92832f52c590cd From 36a44e6691458750eac239ebb9dc668d3b864329 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 11 Oct 2017 18:02:38 -0300 Subject: [PATCH 2/3] Move menus+tools strings to en.ini file (#124) --- data/gui.xml | 623 +++++++++++++++----------------- data/strings/en.ini | 252 +++++++++++++ src/app/CMakeLists.txt | 5 +- src/app/app_menus.cpp | 30 +- src/app/app_menus.h | 3 + src/app/i18n/xml_translator.cpp | 44 +++ src/app/i18n/xml_translator.h | 31 ++ src/app/tools/tool_box.cpp | 29 +- src/app/tools/tool_box.h | 2 + src/app/tools/tool_group.h | 12 +- src/app/ui/app_menuitem.cpp | 3 +- src/app/ui/app_menuitem.h | 4 +- src/app/widget_loader.cpp | 29 +- src/app/widget_loader.h | 6 +- src/gen/check_strings.cpp | 64 +++- src/gen/check_strings.h | 5 +- src/gen/gen.cpp | 4 +- 17 files changed, 737 insertions(+), 409 deletions(-) create mode 100644 src/app/i18n/xml_translator.cpp create mode 100644 src/app/i18n/xml_translator.h diff --git a/data/gui.xml b/data/gui.xml index b4cdf08dc..5d1f53d35 100644 --- a/data/gui.xml +++ b/data/gui.xml @@ -554,80 +554,80 @@ - - - - + + + + - - - - - + + + + + - - - + + + - + - - - - + + + + - - - - - + + + + + - - + + - + - + - + - + - - - + + + - + - + - + @@ -636,379 +636,379 @@ - - + + - - - - - - - - - - + + + + + + - + + + + + - - + + - - - - + + + + - + - + - + - + - - - - + + + + - + - + - + - + - - + + - - - - - + + + + + - - + + - - - + + + - - - + + + - + - - + + - + - - + + - + - + - + - - + + - - + + - - - - - + + + + + - + - + - + - + - - - - - + + + + + - - - + + + - + - + - - + + - - + + - - - - - - - - + + + + + + + + - + - - - - + + + + - - + + - + - + - + - + - - + + - + - - - - + + + + - + - - + + - + - + - + - + - + - + - + - - + + - - - + + + - - + + - - + + - - + + - - - + + + - - - + + + - - + + - - + + - - + + - + - - + + - - - + + + - + - + - - - + + + - + - - - + + + - - - + + + - - - + + + - + - + - + - + - + - + - + - - - + + + - + - + - - - + + + - - + + - - + + - - + + - - + + - + @@ -1016,270 +1016,221 @@ - - + - * - Left-button: replace/add to current selection. * - Right-button: remove from current selection. - - - + tracepolicy="last" /> - * - Left-button: Replace/add to current selection. * - Right-button: Remove from current selection. - - - + tracepolicy="last" /> - * - Left-button: Replace/add to current selection. * - Right-button: Remove from current selection. - - - + tracepolicy="accumulate" /> - * - Left-button: Replace/add to current selection. * - Right-button: Remove from current selection. - - - + tracepolicy="last" /> - * - Left-button: Replace/add to current selection. * - Right-button: Remove from current selection. - - + tracepolicy="accumulate" /> - + + tracepolicy="accumulate" /> + tracepolicy="overlap" /> - + - * - Left-button: Erase with the background color in `Background' layer - or transparent color in any other layer. * - Right-button: Replace foreground with background color. - - + default_brush_size="8" /> - + + pointshape="pixel" /> - + + controller="freehand" /> + controller="freehand" /> - + + controller="freehand" /> + tracepolicy="last" /> - + + tracepolicy="accumulate" /> + tracepolicy="last" /> - + + tracepolicy="last" /> + tracepolicy="last" /> - + + tracepolicy="last" /> + tracepolicy="last" /> + tracepolicy="last" /> + tracepolicy="last" /> - + + tracepolicy="accumulate" /> + tracepolicy="last" /> - + + default_brush_size="16" /> + default_brush_size="16" /> diff --git a/data/strings/en.ini b/data/strings/en.ini index a45f1a806..83c6ba96f 100644 --- a/data/strings/en.ini +++ b/data/strings/en.ini @@ -22,6 +22,21 @@ extras = Extras: shade = Shade pixel_perfect = Pixel-Perfect +[cel_movement_popup_menu] +move = &Move +copy = &Copy +cancel = Cancel + +[cel_popup_menu] +clear = &Clear +unlink = &Unlink +link_cels = &Link Cels + +[document_tab_popup_menu] +duplicate_view = Duplicate &View +open_with_os = &Open with OS +open_in_folder = Open in &Folder + [canvas_size] title = Canvas Size size = Size: @@ -185,6 +200,14 @@ partial_tiles = Include partial tiles at bottom/right edges import = &Import cancel = &Cancel +[ink_popup_menu] +simple_ink = Simple Ink +alpha_compositing = Alpha Compositing +copy_color = Copy Alpha+Color +lock_alpha = Lock Alpha +shading = Shading +same_in_all_tools = Same in all Tools + [jpeg_options] title = JPEG Options quality = Quality: @@ -203,6 +226,160 @@ name = Name: mode = Mode: opacity = Opacity: +[main_menu] +file = &File +file_new = &New... +file_open = &Open... +file_open_recent = Open &Recent +file_save = &Save +file_save_as = Save &As... +file_export = Expor&t... +file_close = &Close +file_close_all = Close All +file_import_sprite_sheet = &Import Sprite Sheet +file_export_sprite_sheet = &Export Sprite Sheet +file_repeat_last_export = Repeat &Last Export +file_exit = E&xit +edit = &Edit +edit_undo = &Undo +edit_redo = &Redo +edit_undo_history = Undo Histor&y +edit_cut = Cu&t +edit_copy = &Copy +edit_copy_merged = Copy Mer&ged +edit_paste = &Paste +edit_clear = C&lear +edit_rotate = R&otate +edit_rotate_180 = &180 +edit_rotate_90cw = &90 CW +edit_rotate_90ccw = 90 &CCW +edit_flip_horizontal = Flip &Horizontal +edit_flip_vertical = Flip &Vertical +edit_transform = Transfor&m +edit_shift = &Shift +edit_shift_left = &Left +edit_shift_right = &Right +edit_shift_up = &Up +edit_shift_down = &Down +edit_new_brush = New &Brush +edit_new_sprite_from_selection = &New Sprite from Selection +edit_replace_color = R&eplace Color... +edit_invert_color = &Invert... +edit_adjustments = Ad&justments +edit_adjustments_brightness_contrast = &Brightness/Contrast... +edit_adjustments_hue_saturation = &Hue/Saturation... +edit_adjustments_color_curve = &Color Curve... +edit_fx = F&X +edit_fx_convolution_matrix = Convolution &Matrix... +edit_fx_despeckle = &Despeckle (Median Filter)... +edit_insert_text = Insert Text +edit_keyboard_shortcuts = &Keyboard Shortcuts... +edit_preferences = Pre&ferences... +sprite = &Sprite +sprite_properties = &Properties... +sprite_color_mode = Color &Mode +sprite_color_mode_rgb = &RGB Color +sprite_color_mode_grayscale = &Grayscale +sprite_color_mode_indexed = &Indexed +sprite_color_mode_more_options = &More Options +sprite_duplicate = &Duplicate... +sprite_sprite_size = &Sprite Size... +sprite_canvas_size = &Canvas Size... +sprite_rotate_canvas = &Rotate Canvas +sprite_rotate = R&otate +sprite_rotate_180 = &180 +sprite_rotate_90cw = &90 CW +sprite_rotate_90ccw = 90 &CCW +sprite_flip_canvas_horizontal = Flip Canvas &Horizontal +sprite_flip_canvas_vertical = Flip Canvas &Vertical +sprite_crop = Cr&op +sprite_trim = &Trim +layer = &Layer +layer_properties = &Properties... +layer_visible = &Visible +layer_lock_layers = Loc&k Layers +layer_open_group = &Open Group +layer_new_layer = &New Layer +layer_new_group = New &Group +layer_delete_layer = Delete Laye&r +layer_background_from_layer = &Background from Layer +layer_layer_from_background = &Layer from Background +layer_duplicate = &Duplicate +layer_merge_down = &Merge Down +layer_flatten = &Flatten +layer_add_reference_layer = Add R&eference Layer +frame = F&rame +frame_properties = Frame &Properties... +frame_cel_properties = &Cel Properties... +frame_new_frame = &New Frame +frame_new_empty_frame = New &Empty Frame +frame_duplicate_cels = &Duplicate Cel(s) +frame_duplicate_linked_cels = Duplicate &Linked Cel(s) +frame_delete_frame = Delete F&rame +frame_tags = &Tags +frame_tags_tag_properties = Tag &Properties... +frame_tags_new_tag = New &Tag +frame_tags_delete_tag = &Delete Tag +frame_jump_to = &Jump to +frame_jump_to_first_frame = &First Frame +frame_jump_to_previous_frame = &Previous Frame +frame_jump_to_next_frame = &Next Frame +frame_jump_to_last_frame = &Last Frame +frame_go_to_frame = &Go to Frame +frame_play_animation = &Play Animation +frame_constant_frame_rate = Constant Frame Rate +frame_reverse_frames = Re&verse Frames +select = Selec&t +select_all = &All +select_deselect = &Deselect +select_reselect = &Reselect +select_inverse = &Inverse +select_color_range = &Color Range +select_modify = &Modify +select_modify_border = &Border +select_modify_expand = &Expand +select_modify_contract = &Contract +select_load_from_file = &Load from MSK file +select_save_to_file = &Save to MSK file +view = &View +view_duplicate_view = Duplicate &View +view_show_extras = &Extras +view_show = &Show +view_show_layer_edges = &Layer Edges +view_show_selection_edges = &Selection Edges +view_show_grid = &Grid +view_show_auto_guides = &Auto Guides +view_show_slices = Sl&ices +view_show_pixel_grid = &Pixel Grid +view_show_brush_preview = &Brush Preview +view_grid = &Grid +view_grid_settings = Gri&d Settings +view_grid_selection_as_grid = Select&ion as Grid +view_grid_snap_to_grid = &Snap to Grid +view_tiled_mode = Tiled &Mode +view_tiled_mode_none = &None +view_tiled_mode_both = Tiled in &Both Axes +view_tiled_mode_x = Tiled in &X Axis +view_tiled_mode_y = Tiled in &Y Axis +view_symmetry_options = S&ymmetry Options +view_set_loop_section = Set &Loop Section +view_show_onion_skin = Show &Onion Skin +view_timeline = &Timeline +view_preview = Previe&w +view_full_screen_mode = &Full Screen Mode +view_full_screen_preview = F&ull Screen Preview +view_home = &Home +view_refresh = &Refresh && Reload Skin +help = &Help +help_readme = Readme +help_quick_reference = Quick &Reference +help_documentation = Documentation +help_tutorial = Tutorial +help_release_notes = Release Notes +help_twitter = Twitter +help_donate = &Donate +help_about = &About + [modify_selection] title = Modify Selection circle = Circle Brush @@ -434,6 +611,22 @@ alpha_channel = Create entries with alpha component load = &Load open_folder = Open &Folder +[palette_popup_menu] +edit_palette = Edit &Palette +palette_size = Palette Si&ze +small_size = &Small Size +medium_size = &Medium Size +large_size = &Large Size +color_tint_shade_tone = Color Tint/Shade/Tone +color_spectrum = Color Spectrum +rgb_color_wheel = RGB Color Wheel +ryb_color_wheel = RYB Color Wheel +load_palette = L&oad Palette +save_palette = S&ave Palette +load_default_palette = Load Default Palette +save_as_default_palette = Save as Default Palette +create_palette_from_current_sprite = Create Palette from Current Sprite + [palette_size] title = Palette Size number_of_colors = Number of colors: @@ -478,6 +671,10 @@ open_dmp_file = Open the following file to debug your compilation: do_it_later = Do it later delete_file = Delete file, I've already sent it +[slice_popup_menu] +properties = Slice &Properties... +delete = &Delete Slice + [slice_properties] title = Slice Properties name = Slice Name: @@ -528,6 +725,9 @@ percentage = Percentage: interpolation = Interpolation: method = Method: +[tab_popup_menu] +close = &Close + [timeline_conf] position = Position: left = &Left @@ -556,6 +756,58 @@ END in_front = In front of sprite in_front_toolip = For all kind of layers (background and transparents) +[tools] +rectangular_marquee = Rectangular Marquee Tool +rectangular_marquee_tooltip = <<${output_fn} + COMMAND ${CMAKE_BINARY_DIR}/bin/gen --widgets-dir "${CMAKE_SOURCE_DIR}/data/widgets/" --strings-dir "${CMAKE_SOURCE_DIR}/data/strings/" --gui-file "${CMAKE_SOURCE_DIR}/data/gui.xml" >${output_fn} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS gen ${widget_files} ${string_files}) + DEPENDS gen ${widget_files} ${string_files} "${CMAKE_SOURCE_DIR}/data/gui.xml") list(APPEND generated_files ${output_fn}) # Directory where generated files by "gen" utility will stay. @@ -389,6 +389,7 @@ add_library(app-lib font_path.cpp gui_xml.cpp i18n/strings.cpp + i18n/xml_translator.cpp ini_file.cpp job.cpp launcher.cpp diff --git a/src/app/app_menus.cpp b/src/app/app_menus.cpp index 40503ef7f..958bc1989 100644 --- a/src/app/app_menus.cpp +++ b/src/app/app_menus.cpp @@ -16,6 +16,7 @@ #include "app/commands/params.h" #include "app/console.h" #include "app/gui_xml.h" +#include "app/i18n/strings.h" #include "app/recent_files.h" #include "app/resource_finder.h" #include "app/tools/tool_box.h" @@ -327,16 +328,16 @@ void AppMenus::reload() LOG("MENU: Main menu loaded.\n"); - m_tabPopupMenu.reset(loadMenuById(handle, "tab_popup")); - m_documentTabPopupMenu.reset(loadMenuById(handle, "document_tab_popup")); - m_layerPopupMenu.reset(loadMenuById(handle, "layer_popup")); - m_framePopupMenu.reset(loadMenuById(handle, "frame_popup")); - m_celPopupMenu.reset(loadMenuById(handle, "cel_popup")); - m_celMovementPopupMenu.reset(loadMenuById(handle, "cel_movement_popup")); - m_frameTagPopupMenu.reset(loadMenuById(handle, "frame_tag_popup")); - m_slicePopupMenu.reset(loadMenuById(handle, "slice_popup")); - m_palettePopupMenu.reset(loadMenuById(handle, "palette_popup")); - m_inkPopupMenu.reset(loadMenuById(handle, "ink_popup")); + m_tabPopupMenu.reset(loadMenuById(handle, "tab_popup_menu")); + m_documentTabPopupMenu.reset(loadMenuById(handle, "document_tab_popup_menu")); + m_layerPopupMenu.reset(loadMenuById(handle, "layer_popup_menu")); + m_framePopupMenu.reset(loadMenuById(handle, "frame_popup_menu")); + m_celPopupMenu.reset(loadMenuById(handle, "cel_popup_menu")); + m_celMovementPopupMenu.reset(loadMenuById(handle, "cel_movement_popup_menu")); + m_frameTagPopupMenu.reset(loadMenuById(handle, "frame_tag_popup_menu")); + m_slicePopupMenu.reset(loadMenuById(handle, "slice_popup_menu")); + m_palettePopupMenu.reset(loadMenuById(handle, "palette_popup_menu")); + m_inkPopupMenu.reset(loadMenuById(handle, "ink_popup_menu")); createNativeMenus(); @@ -439,10 +440,12 @@ Menu* AppMenus::loadMenuById(TiXmlHandle& handle, const char* id) .FirstChild("menus") .FirstChild("menu").ToElement(); while (xmlMenu) { - const char* menu_id = xmlMenu->Attribute("id"); + const char* menuId = xmlMenu->Attribute("id"); - if (menu_id && strcmp(menu_id, id) == 0) + if (menuId && strcmp(menuId, id) == 0) { + m_xmlTranslator.setStringIdPrefix(menuId); return convertXmlelemToMenu(xmlMenu); + } xmlMenu = xmlMenu->NextSiblingElement(); } @@ -496,7 +499,8 @@ Widget* AppMenus::convertXmlelemToMenuitem(TiXmlElement* elem) } // Create the item - AppMenuItem* menuitem = new AppMenuItem(elem->Attribute("text"), command, params); + AppMenuItem* menuitem = new AppMenuItem(m_xmlTranslator(elem, "text"), + command, params); if (!menuitem) return nullptr; diff --git a/src/app/app_menus.h b/src/app/app_menus.h index 624f8078a..90bd4cdc2 100644 --- a/src/app/app_menus.h +++ b/src/app/app_menus.h @@ -8,6 +8,8 @@ #define APP_APP_MENUS_H_INCLUDED #pragma once +#include "app/i18n/xml_translator.h" +#include "app/widget_type_mismatch.h" #include "base/disable_copying.h" #include "base/unique_ptr.h" #include "obs/connection.h" @@ -88,6 +90,7 @@ namespace app { obs::scoped_connection m_recentFilesConn; std::vector m_menus; she::Menu* m_osMenu; + XmlTranslator m_xmlTranslator; }; she::Shortcut get_os_shortcut_from_key(Key* key); diff --git a/src/app/i18n/xml_translator.cpp b/src/app/i18n/xml_translator.cpp new file mode 100644 index 000000000..487d26aa5 --- /dev/null +++ b/src/app/i18n/xml_translator.cpp @@ -0,0 +1,44 @@ +// Aseprite +// Copyright (C) 2017 David Capello +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/i18n/xml_translator.h" + +#include "app/i18n/strings.h" +#include "tinyxml.h" + +namespace app { + +std::string XmlTranslator::operator()(const TiXmlElement* elem, + const char* attrName) +{ + const char* value = elem->Attribute(attrName); + if (!value) + return std::string(); + else if (value[0] == '@') { + if (value[1] == '.') + return Strings::instance()->translate((m_stringIdPrefix + (value+1)).c_str()); + else + return Strings::instance()->translate(value+1); + } + else + return std::string(value); +} + +void XmlTranslator::clearStringIdPrefix() +{ + m_stringIdPrefix.clear(); +} + +void XmlTranslator::setStringIdPrefix(const char* prefix) +{ + m_stringIdPrefix = prefix; +} + +} // namespace app diff --git a/src/app/i18n/xml_translator.h b/src/app/i18n/xml_translator.h new file mode 100644 index 000000000..1415b75f0 --- /dev/null +++ b/src/app/i18n/xml_translator.h @@ -0,0 +1,31 @@ +// Aseprite +// Copyright (C) 2017 David Capello +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + +#ifndef APP_I18N_XML_TRANSLATOR_INCLUDED +#define APP_I18N_XML_TRANSLATOR_INCLUDED +#pragma once + +#include + +class TiXmlElement; + +namespace app { + + class XmlTranslator { + public: + std::string operator()(const TiXmlElement* elem, + const char* attrName); + + void clearStringIdPrefix(); + void setStringIdPrefix(const char* prefix); + + private: + std::string m_stringIdPrefix; + }; + +} // namespace app + +#endif diff --git a/src/app/tools/tool_box.cpp b/src/app/tools/tool_box.cpp index c2be5672f..45d8ab142 100644 --- a/src/app/tools/tool_box.cpp +++ b/src/app/tools/tool_box.cpp @@ -11,6 +11,7 @@ #include "app/tools/tool_box.h" #include "app/gui_xml.h" +#include "app/i18n/strings.h" #include "app/tools/controller.h" #include "app/tools/ink.h" #include "app/tools/intertwine.h" @@ -92,6 +93,8 @@ const char* WellKnownPointShapes::Spray = "spray"; ToolBox::ToolBox() { + m_xmlTranslator.setStringIdPrefix("tools"); + m_inks[WellKnownInks::Selection] = new SelectionInk(); m_inks[WellKnownInks::Paint] = new PaintInk(PaintInk::Simple); m_inks[WellKnownInks::PaintFg] = new PaintInk(PaintInk::WithFg); @@ -194,29 +197,27 @@ void ToolBox::loadTools() // For each group TiXmlElement* xmlGroup = handle.FirstChild("gui").FirstChild("tools").FirstChild("group").ToElement(); while (xmlGroup) { - const char* group_id = xmlGroup->Attribute("id"); - const char* group_text = xmlGroup->Attribute("text"); - - if (!group_id || !group_text) + const char* groupId = xmlGroup->Attribute("id"); + if (!groupId) throw base::Exception("The configuration file has a without 'id' or 'text' attributes."); - LOG(VERBOSE) << "TOOL: Group " << group_id << "\n"; + LOG(VERBOSE) << "TOOL: Group " << groupId << "\n"; - ToolGroup* tool_group = new ToolGroup(group_id, group_text); + ToolGroup* toolGroup = new ToolGroup(groupId); // For each tool TiXmlNode* xmlToolNode = xmlGroup->FirstChild("tool"); TiXmlElement* xmlTool = xmlToolNode ? xmlToolNode->ToElement(): NULL; while (xmlTool) { - const char* tool_id = xmlTool->Attribute("id"); - const char* tool_text = xmlTool->Attribute("text"); - const char* tool_tips = xmlTool->FirstChild("tooltip") ? ((TiXmlElement*)xmlTool->FirstChild("tooltip"))->GetText(): ""; - const char* default_brush_size = xmlTool->Attribute("default_brush_size"); + const char* toolId = xmlTool->Attribute("id"); + std::string toolText = m_xmlTranslator(xmlTool, "text"); + std::string toolTips = m_xmlTranslator(xmlTool, "tooltip"); + const char* defaultBrushSize = xmlTool->Attribute("default_brush_size"); - Tool* tool = new Tool(tool_group, tool_id, tool_text, tool_tips, - default_brush_size ? strtol(default_brush_size, NULL, 10): 1); + Tool* tool = new Tool(toolGroup, toolId, toolText, toolTips, + defaultBrushSize ? strtol(defaultBrushSize, NULL, 10): 1); - LOG(VERBOSE) << "TOOL: Tool " << tool_id << " in group " << group_id << " found\n"; + LOG(VERBOSE) << "TOOL: Tool " << toolId << " in group " << groupId << " found\n"; loadToolProperties(xmlTool, tool, 0, "left"); loadToolProperties(xmlTool, tool, 1, "right"); @@ -226,7 +227,7 @@ void ToolBox::loadTools() xmlTool = xmlTool->NextSiblingElement(); } - m_groups.push_back(tool_group); + m_groups.push_back(toolGroup); xmlGroup = xmlGroup->NextSiblingElement(); } diff --git a/src/app/tools/tool_box.h b/src/app/tools/tool_box.h index f40f687d2..bcae2ea15 100644 --- a/src/app/tools/tool_box.h +++ b/src/app/tools/tool_box.h @@ -12,6 +12,7 @@ #include #include +#include "app/i18n/xml_translator.h" #include "app/tools/tool.h" #include "base/unique_ptr.h" @@ -117,6 +118,7 @@ namespace app { ToolGroupList m_groups; ToolList m_tools; + XmlTranslator m_xmlTranslator; }; } // namespace tools diff --git a/src/app/tools/tool_group.h b/src/app/tools/tool_group.h index ba05a223e..c44c4b448 100644 --- a/src/app/tools/tool_group.h +++ b/src/app/tools/tool_group.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -16,16 +16,12 @@ namespace app { // A group of tools. class ToolGroup { public: - ToolGroup(const char* name, - const char* label) : m_name(name) - , m_label(label) { } + ToolGroup(const char* id) : m_id(id) { } - const std::string& getName() const { return m_name; } - const std::string& getLabel() const { return m_label; } + const std::string& id() const { return m_id; } private: - std::string m_name; - std::string m_label; + std::string m_id; }; } // namespace tools diff --git a/src/app/ui/app_menuitem.cpp b/src/app/ui/app_menuitem.cpp index 9dfd3b5e9..9154cf1fd 100644 --- a/src/app/ui/app_menuitem.cpp +++ b/src/app/ui/app_menuitem.cpp @@ -34,7 +34,8 @@ using namespace ui; // static Params AppMenuItem::s_contextParams; -AppMenuItem::AppMenuItem(const char* text, Command* command, const Params& params) +AppMenuItem::AppMenuItem(const std::string& text, + Command* command, const Params& params) : MenuItem(text) , m_key(nullptr) , m_command(command) diff --git a/src/app/ui/app_menuitem.h b/src/app/ui/app_menuitem.h index 93df0e33a..9fcfd085a 100644 --- a/src/app/ui/app_menuitem.h +++ b/src/app/ui/app_menuitem.h @@ -34,7 +34,9 @@ namespace app { app::KeyContext keyContext = app::KeyContext::Any; }; - AppMenuItem(const char* text, Command* command = nullptr, const Params& params = Params()); + AppMenuItem(const std::string& text, + Command* command = nullptr, + const Params& params = Params()); ~AppMenuItem(); Key* key() { return m_key; } diff --git a/src/app/widget_loader.cpp b/src/app/widget_loader.cpp index 02b53ea1c..05d5fb21a 100644 --- a/src/app/widget_loader.cpp +++ b/src/app/widget_loader.cpp @@ -89,7 +89,7 @@ Widget* WidgetLoader::loadWidgetFromXmlFile( ui::Widget* widget) { m_tooltipManager = NULL; - m_stringIdPrefix = widgetId; + m_xmlTranslator.setStringIdPrefix(widgetId.c_str()); XmlDocumentRef doc(open_xml(xmlFilename)); TiXmlHandle handle(doc.get()); @@ -362,7 +362,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget (middle ? MIDDLE: (bottom ? BOTTOM: TOP)); if (!widget) { - widget = new Separator(textAttr(elem, "text"), align); + widget = new Separator(m_xmlTranslator(elem, "text"), align); } else widget->setAlign(widget->align() | align); @@ -397,7 +397,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget if (desktop) widget = new Window(Window::DesktopWindow); else if (elem->Attribute("text")) - widget = new Window(Window::WithTitleBar, textAttr(elem, "text")); + widget = new Window(Window::WithTitleBar, m_xmlTranslator(elem, "text")); else widget = new Window(Window::WithoutTitleBar); } @@ -419,7 +419,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget } else if (elem_name == "dropdownbutton") { if (!widget) { - widget = new DropDownButton(textAttr(elem, "text").c_str()); + widget = new DropDownButton(m_xmlTranslator(elem, "text").c_str()); } } else if (elem_name == "buttonset") { @@ -453,7 +453,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget } if (text) - item->setText(textAttr(elem, "text")); + item->setText(m_xmlTranslator(elem, "text")); buttonset->addItem(item, hspan, vspan); fillWidgetWithXmlElementAttributes(elem, root, item); @@ -527,7 +527,7 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, widget->setId(id); if (elem->Attribute("text")) - widget->setText(textAttr(elem, "text")); + widget->setText(m_xmlTranslator(elem, "text")); if (elem->Attribute("tooltip") && root) { if (!m_tooltipManager) { @@ -543,7 +543,7 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, else if (strcmp(tooltip_dir, "right") == 0) dir = RIGHT; } - m_tooltipManager->addTooltipFor(widget, textAttr(elem, "tooltip"), dir); + m_tooltipManager->addTooltipFor(widget, m_xmlTranslator(elem, "tooltip"), dir); } if (selected) @@ -723,19 +723,4 @@ static int int_attr(const TiXmlElement* elem, const char* attribute_name, int de return (value ? strtol(value, NULL, 10): default_value); } -std::string WidgetLoader::textAttr(const TiXmlElement* elem, const char* attrName) -{ - const char* value = elem->Attribute(attrName); - if (!value) - return std::string(); - else if (value[0] == '@') { - if (value[1] == '.') - return Strings::instance()->translate((m_stringIdPrefix + (value+1)).c_str()); - else - return Strings::instance()->translate(value+1); - } - else - return std::string(value); -} - } // namespace app diff --git a/src/app/widget_loader.h b/src/app/widget_loader.h index 1134e4b60..d471d9584 100644 --- a/src/app/widget_loader.h +++ b/src/app/widget_loader.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -8,6 +8,7 @@ #define APP_WIDGET_LOADER_H_INCLUDED #pragma once +#include "app/i18n/xml_translator.h" #include "app/widget_type_mismatch.h" #include @@ -64,13 +65,12 @@ namespace app { ui::Widget* convertXmlElementToWidget(const TiXmlElement* elem, ui::Widget* root, ui::Widget* parent, ui::Widget* widget); void fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, ui::Widget* root, ui::Widget* widget); void fillWidgetWithXmlElementAttributesWithChildren(const TiXmlElement* elem, ui::Widget* root, ui::Widget* widget); - std::string textAttr(const TiXmlElement* elem, const char* attrName); typedef std::map TypeCreatorsMap; TypeCreatorsMap m_typeCreators; ui::TooltipManager* m_tooltipManager; - std::string m_stringIdPrefix; + XmlTranslator m_xmlTranslator; }; } // namespace app diff --git a/src/gen/check_strings.cpp b/src/gen/check_strings.cpp index e7002bda5..d934fd9b9 100644 --- a/src/gen/check_strings.cpp +++ b/src/gen/check_strings.cpp @@ -1,5 +1,5 @@ // Aseprite Code Generator -// Copyright (c) 2016 David Capello +// Copyright (c) 2016-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -41,15 +41,15 @@ static std::string find_first_id(TiXmlElement* elem) return ""; } -static void collect_widgets_with_strings(TiXmlElement* elem, XmlElements& widgets) +static void collect_elements_with_strings(TiXmlElement* elem, XmlElements& elems) { TiXmlElement* child = elem->FirstChildElement(); while (child) { const char* text = child->Attribute("text"); const char* tooltip = child->Attribute("tooltip"); if (text || tooltip) - widgets.push_back(child); - collect_widgets_with_strings(child, widgets); + elems.push_back(child); + collect_elements_with_strings(child, elems); child = child->NextSiblingElement(); } } @@ -117,7 +117,7 @@ public: m_prefixId = find_first_id(doc->RootElement()); - collect_widgets_with_strings(doc->RootElement(), widgets); + collect_elements_with_strings(doc->RootElement(), widgets); for (TiXmlElement* elem : widgets) { checkString(elem, elem->Attribute("text")); checkString(elem, elem->Attribute("tooltip")); @@ -125,6 +125,56 @@ public: } } + void checkStringsOnGuiFile(const std::string& fullFn) { + base::FileHandle inputFile(base::open_file(fullFn, "rb")); + base::UniquePtr doc(new TiXmlDocument()); + doc->SetValue(fullFn.c_str()); + if (!doc->LoadFile(inputFile.get())) { + std::cerr << doc->Value() << ":" + << doc->ErrorRow() << ":" + << doc->ErrorCol() << ": " + << "error " << doc->ErrorId() << ": " + << doc->ErrorDesc() << "\n"; + + throw std::runtime_error("invalid input file"); + } + + TiXmlHandle handle(doc); + + // For each menu + TiXmlElement* xmlMenu = handle + .FirstChild("gui") + .FirstChild("menus") + .FirstChild("menu").ToElement(); + while (xmlMenu) { + const char* menuId = xmlMenu->Attribute("id"); + if (menuId) { + m_prefixId = menuId; + XmlElements menus; + collect_elements_with_strings(xmlMenu, menus); + for (TiXmlElement* elem : menus) + checkString(elem, elem->Attribute("text")); + } + xmlMenu = xmlMenu->NextSiblingElement(); + } + + // For each tool + m_prefixId = "tools"; + TiXmlElement* xmlGroup = handle + .FirstChild("gui") + .FirstChild("tools") + .FirstChild("group").ToElement(); + while (xmlGroup) { + XmlElements tools; + collect_elements_with_strings(xmlGroup, tools); + for (TiXmlElement* elem : tools) { + checkString(elem, elem->Attribute("text")); + checkString(elem, elem->Attribute("tooltip")); + } + xmlGroup = xmlGroup->NextSiblingElement(); + } + } + void checkString(TiXmlElement* elem, const char* text) { if (!text) return; // Do nothing @@ -172,9 +222,11 @@ private: }; void check_strings(const std::string& widgetsDir, - const std::string& stringsDir) + const std::string& stringsDir, + const std::string& guiFile) { CheckStrings cs; cs.loadStrings(stringsDir); cs.checkStringsOnWidgets(widgetsDir); + cs.checkStringsOnGuiFile(guiFile); } diff --git a/src/gen/check_strings.h b/src/gen/check_strings.h index e143d8a8d..7796a29ea 100644 --- a/src/gen/check_strings.h +++ b/src/gen/check_strings.h @@ -1,5 +1,5 @@ // Aseprite Code Generator -// Copyright (c) 2016 David Capello +// Copyright (c) 2016-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -11,6 +11,7 @@ #include void check_strings(const std::string& widgetsDir, - const std::string& stringsDir); + const std::string& stringsDir, + const std::string& guiFile); #endif diff --git a/src/gen/gen.cpp b/src/gen/gen.cpp index a34977160..c7d47a3ba 100644 --- a/src/gen/gen.cpp +++ b/src/gen/gen.cpp @@ -31,6 +31,7 @@ static void run(int argc, const char* argv[]) PO::Option& strings = po.add("strings"); PO::Option& widgetsDir = po.add("widgets-dir").requiresValue(""); PO::Option& stringsDir = po.add("strings-dir").requiresValue(""); + PO::Option& guiFile = po.add("gui-file").requiresValue(""); po.parse(argc, argv); // Try to load the XML file @@ -75,7 +76,8 @@ static void run(int argc, const char* argv[]) else if (po.enabled(widgetsDir) && po.enabled(stringsDir)) { check_strings(po.value_of(widgetsDir), - po.value_of(stringsDir)); + po.value_of(stringsDir), + po.value_of(guiFile)); } } From a9b183729fd207d9441c682280b5a5a7e6867196 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 17 Oct 2017 18:00:45 -0300 Subject: [PATCH 3/3] Translate all alerts to the en.ini file (#124) --- data/strings/en.ini | 131 +++++++++++++++++++ src/app/commands/cmd_export_sprite_sheet.cpp | 8 +- src/app/commands/cmd_import_sprite_sheet.cpp | 8 +- src/app/commands/cmd_keyboard_shortcuts.cpp | 16 +-- src/app/commands/cmd_load_mask.cpp | 9 +- src/app/commands/cmd_load_palette.cpp | 4 +- src/app/commands/cmd_options.cpp | 50 ++++--- src/app/commands/cmd_paste_text.cpp | 4 +- src/app/commands/cmd_remove_layer.cpp | 7 +- src/app/commands/cmd_save_file.cpp | 6 +- src/app/commands/cmd_save_mask.cpp | 4 +- src/app/commands/cmd_save_palette.cpp | 4 +- src/app/commands/filters/filter_worker.cpp | 6 +- src/app/file/ase_format.cpp | 20 +-- src/app/file/file.cpp | 66 +++++----- src/app/i18n/strings.cpp | 4 +- src/app/i18n/strings.h | 8 +- src/app/job.cpp | 7 +- src/app/launcher.cpp | 8 +- src/app/send_crash.cpp | 8 +- src/app/ui/color_bar.cpp | 7 +- src/app/ui/data_recovery_view.cpp | 9 +- src/app/ui/document_view.cpp | 14 +- src/app/ui/editor/tool_loop_impl.cpp | 13 +- src/app/ui/file_selector.cpp | 29 ++-- src/app/ui/timeline/timeline.cpp | 4 +- src/app/util/clipboard_native.cpp | 7 +- src/gen/strings_class.cpp | 4 +- src/ui/alert.cpp | 30 ++--- src/ui/alert.h | 6 +- 30 files changed, 320 insertions(+), 181 deletions(-) diff --git a/data/strings/en.ini b/data/strings/en.ini index 83c6ba96f..8782f3095 100644 --- a/data/strings/en.ini +++ b/data/strings/en.ini @@ -6,6 +6,137 @@ title = Warning - Important description = You are going to enter in "Advanced Mode". dont_show_again = Don't show this again +[alerts] +applying_filter = FX< Save As menu option in that case. +||&OK +END +cannot_open_file = Problem< {2}% +<< UI Scaling: {3}% -> {4}% +<accels()[index]; - if (Alert::show( - "Warning" - "<disableAccel(accel); @@ -586,10 +587,7 @@ private: } void onReset() { - if (Alert::show("Warning" - "<reset(); layout(); } diff --git a/src/app/commands/cmd_load_mask.cpp b/src/app/commands/cmd_load_mask.cpp index 71f8ff1e4..fbfc2380a 100644 --- a/src/app/commands/cmd_load_mask.cpp +++ b/src/app/commands/cmd_load_mask.cpp @@ -13,11 +13,13 @@ #include "app/commands/params.h" #include "app/context_access.h" #include "app/file_selector.h" +#include "app/i18n/strings.h" #include "app/modules/gui.h" #include "app/transaction.h" #include "app/util/msk_file.h" #include "doc/mask.h" #include "doc/sprite.h" +#include "fmt/format.h" #include "ui/alert.h" namespace app { @@ -68,9 +70,10 @@ void LoadMaskCommand::onExecute(Context* context) } base::UniquePtr mask(load_msk_file(m_filename.c_str())); - if (!mask) - throw base::Exception("Error loading .msk file: %s", - static_cast(m_filename.c_str())); + if (!mask) { + ui::Alert::show(fmt::format(Strings::alerts_error_loading_file(), m_filename)); + return; + } { ContextWriter writer(reader); diff --git a/src/app/commands/cmd_load_palette.cpp b/src/app/commands/cmd_load_palette.cpp index ff584d13b..071d033f8 100644 --- a/src/app/commands/cmd_load_palette.cpp +++ b/src/app/commands/cmd_load_palette.cpp @@ -14,10 +14,12 @@ #include "app/context.h" #include "app/file/palette_file.h" #include "app/file_selector.h" +#include "app/i18n/strings.h" #include "app/modules/palettes.h" #include "base/fs.h" #include "base/unique_ptr.h" #include "doc/palette.h" +#include "fmt/format.h" #include "ui/alert.h" namespace app { @@ -80,7 +82,7 @@ void LoadPaletteCommand::onExecute(Context* context) base::UniquePtr palette(load_palette(filename.c_str())); if (!palette) { if (context->isUIAvailable()) - Alert::show("Error<isSelected()); m_pref.general.dataRecoveryPeriod(newPeriod); - warnings += "<<- Automatically save recovery data every"; + warnings += "<<- " + Strings::alerts_restart_by_preferences_save_recovery_data_period(); } m_pref.editor.zoomFromCenterWithWheel(zoomFromCenterWithWheel()->isSelected()); @@ -463,9 +465,9 @@ public: m_pref.save(); if (!warnings.empty()) { - ui::Alert::show(PACKAGE - "< %d%%" - "<< UI Scaling: %d%% -> %d%%" - "< 0 ? newScreenScale: m_pref.general.screenScale()), - 100 * m_pref.general.uiScale(), - 100 * (newUIScale > 0 ? newUIScale: m_pref.general.uiScale())); + fmt::format( + Strings::alerts_update_screen_ui_scaling_with_theme_values(), + themeName, + 100 * m_pref.general.screenScale(), + 100 * (newScreenScale > 0 ? newScreenScale: m_pref.general.screenScale()), + 100 * m_pref.general.uiScale(), + 100 * (newUIScale > 0 ? newUIScale: m_pref.general.uiScale()))); if (result == 1) { // Preferred UI Scaling factor @@ -865,14 +863,13 @@ private: // Uninstall? if (ui::Alert::show( - "Update Extension" - "<name().c_str(), - (isDowngrade ? "downgrade": "upgrade"), - ext->version().c_str(), - info.version.c_str()) != 1) + fmt::format( + Strings::alerts_update_extension(), + ext->name(), + (isDowngrade ? Strings::alerts_update_extension_downgrade(): + Strings::alerts_update_extension_upgrade()), + ext->version(), + info.version)) != 1) return; // Uninstall old version @@ -917,10 +914,9 @@ private: return; if (ui::Alert::show( - "Warning" - "<text().c_str()) != 1) + fmt::format( + Strings::alerts_uninstall_extension_warning(), + item->text())) != 1) return; try { diff --git a/src/app/commands/cmd_paste_text.cpp b/src/app/commands/cmd_paste_text.cpp index 560450c2a..3af916037 100644 --- a/src/app/commands/cmd_paste_text.cpp +++ b/src/app/commands/cmd_paste_text.cpp @@ -195,9 +195,7 @@ void PasteTextCommand::onExecute(Context* ctx) } } catch (const std::exception& ex) { - ui::Alert::show(PACKAGE - "<<%s" - "||&OK", ex.what()); + Console::showException(ex); } } diff --git a/src/app/commands/cmd_remove_layer.cpp b/src/app/commands/cmd_remove_layer.cpp index 881acdc53..2280e8849 100644 --- a/src/app/commands/cmd_remove_layer.cpp +++ b/src/app/commands/cmd_remove_layer.cpp @@ -12,9 +12,10 @@ #include "app/commands/command.h" #include "app/context_access.h" #include "app/document_api.h" +#include "app/i18n/strings.h" #include "app/modules/gui.h" -#include "app/ui/status_bar.h" #include "app/transaction.h" +#include "app/ui/status_bar.h" #include "doc/layer.h" #include "doc/sprite.h" #include "ui/alert.h" @@ -69,7 +70,7 @@ void RemoveLayerCommand::onExecute(Context* context) } if (deletedTopLevelLayers == sprite->root()->layersCount()) { - ui::Alert::show("Error<allLayersCount() == 1) { - ui::Alert::show("Error< Save menu option in that case." - "||&OK"); + ui::Alert::show(Strings::alerts_cannot_file_overwrite_on_export()); goto again; } diff --git a/src/app/commands/cmd_save_mask.cpp b/src/app/commands/cmd_save_mask.cpp index 6a6628614..53e07f3a5 100644 --- a/src/app/commands/cmd_save_mask.cpp +++ b/src/app/commands/cmd_save_mask.cpp @@ -11,10 +11,12 @@ #include "app/commands/command.h" #include "app/context_access.h" #include "app/file_selector.h" +#include "app/i18n/strings.h" #include "app/util/msk_file.h" #include "base/fs.h" #include "doc/mask.h" #include "doc/sprite.h" +#include "fmt/format.h" #include "ui/alert.h" namespace app { @@ -55,7 +57,7 @@ void SaveMaskCommand::onExecute(Context* context) std::string filename = selFilename.front(); if (save_msk_file(document->mask(), filename.c_str()) != 0) - ui::Alert::show("Error<addProgress(); m_timer.Tick.connect(&FilterWorker::onMonitoringTick, this); diff --git a/src/app/file/ase_format.cpp b/src/app/file/ase_format.cpp index cb28e5ca2..c1baaaa6b 100644 --- a/src/app/file/ase_format.cpp +++ b/src/app/file/ase_format.cpp @@ -20,6 +20,7 @@ #include "base/fs.h" #include "doc/doc.h" #include "fixmath/fixmath.h" +#include "fmt/format.h" #include "ui/alert.h" #include "zlib.h" @@ -403,14 +404,17 @@ bool AseFormat::onPostLoad(FileOp* fop) if (flat && ase_has_groups(group)) { if (fop->context() && fop->context()->isUIAvailable() && - ui::Alert::show("Warning" - "<filename()).c_str(), - PACKAGE, ver.c_str()) != 1) { + ui::Alert::show( + fmt::format( + // This message is not translated because is used only in the old v1.1 only + "Warning" + "<filename()), + PACKAGE, ver)) != 1) { return false; } ase_ungroup_all(group); diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp index 8be82f128..5b07b2586 100644 --- a/src/app/file/file.cpp +++ b/src/app/file/file.cpp @@ -19,6 +19,7 @@ #include "app/file/format_options.h" #include "app/file/split_filename.h" #include "app/filename_formatter.h" +#include "app/i18n/strings.h" #include "app/modules/gui.h" #include "app/modules/palettes.h" #include "app/ui/status_bar.h" @@ -29,6 +30,7 @@ #include "base/string.h" #include "dio/detect_format.h" #include "doc/doc.h" +#include "fmt/format.h" #include "render/quantization.h" #include "render/render.h" #include "ui/alert.h" @@ -327,6 +329,13 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context, // Get the extension of the filename (in lower case) LOG("FILE: Saving document \"%s\"\n", filename.c_str()); + // Check for read-only attribute + if (base::has_readonly_attr(filename)) { + fop->setError("Error saving \"%s\" file, it's read-only", + filename.c_str()); + return fop.release(); + } + // Get the format through the extension of the filename fop->m_format = FileFormatsManager::instance()->getFileFormat( dio::detect_format_by_file_extension(filename)); @@ -347,32 +356,32 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context, case IMAGE_RGB: if (!(fop->m_format->support(FILE_SUPPORT_RGB))) { - warnings += "<<- RGB format"; + warnings += "<<- " + Strings::alerts_file_format_rgb_mode(); fatal = true; } if (!(fop->m_format->support(FILE_SUPPORT_RGBA)) && fop->m_document->sprite()->needAlpha()) { - warnings += "<<- Alpha channel"; + warnings += "<<- " + Strings::alerts_file_format_alpha_channel(); } break; case IMAGE_GRAYSCALE: if (!(fop->m_format->support(FILE_SUPPORT_GRAY))) { - warnings += "<<- Grayscale format"; + warnings += "<<- " + Strings::alerts_file_format_grayscale_mode(); fatal = true; } if (!(fop->m_format->support(FILE_SUPPORT_GRAYA)) && fop->m_document->sprite()->needAlpha()) { - warnings += "<<- Alpha channel"; + warnings += "<<- " + Strings::alerts_file_format_alpha_channel(); } break; case IMAGE_INDEXED: if (!(fop->m_format->support(FILE_SUPPORT_INDEXED))) { - warnings += "<<- Indexed format"; + warnings += "<<- " + Strings::alerts_file_format_indexed_mode(); fatal = true; } break; @@ -382,14 +391,14 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context, if (fop->m_roi.frames() > 1) { if (!fop->m_format->support(FILE_SUPPORT_FRAMES) && !fop->m_format->support(FILE_SUPPORT_SEQUENCES)) { - warnings += "<<- Frames"; + warnings += "<<- " + Strings::alerts_file_format_frames(); } } // Layers support if (fop->m_document->sprite()->root()->layersCount() > 1) { if (!(fop->m_format->support(FILE_SUPPORT_LAYERS))) { - warnings += "<<- Layers"; + warnings += "<<- " + Strings::alerts_file_format_layers(); } } @@ -397,14 +406,14 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context, if (fop->m_document->sprite()->getPalettes().size() > 1) { if (!fop->m_format->support(FILE_SUPPORT_PALETTES) && !fop->m_format->support(FILE_SUPPORT_SEQUENCES)) { - warnings += "<<- Palette changes between frames"; + warnings += "<<- " + Strings::alerts_file_format_palette_changes(); } } // Check frames support if (!fop->m_document->sprite()->frameTags().empty()) { if (!fop->m_format->support(FILE_SUPPORT_FRAME_TAGS)) { - warnings += "<<- Frame tags"; + warnings += "<<- " + Strings::alerts_file_format_frame_tags(); } } @@ -438,26 +447,12 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context, if (!warnings.empty()) { // Interative if (context && context->isUIAvailable()) { - warnings += "<m_format->name(), - warnings.c_str(), - fop->m_format->name(), - buttons.c_str()); + int ret = ui::Alert::show( + fmt::format( + (fatal ? Strings::alerts_file_format_doesnt_support_error(): + Strings::alerts_file_format_doesnt_support_warning()), + fop->m_format->name(), + warnings)); // Operation can't be done (by fatal error) or the user cancel // the operation @@ -510,13 +505,12 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context, if (context && context->isUIAvailable() && fop->m_seq.filename_list.size() > 1 && - ui::Alert::show("Notice" - "<m_seq.filename_list.size()), - base::get_file_name(fop->m_seq.filename_list[0]).c_str(), - base::get_file_name(fop->m_seq.filename_list[1]).c_str()) != 1) { + ui::Alert::show( + fmt::format( + Strings::alerts_export_animation_in_sequence(), + int(fop->m_seq.filename_list.size()), + base::get_file_name(fop->m_seq.filename_list[0]), + base::get_file_name(fop->m_seq.filename_list[1]))) != 1) { return nullptr; } } diff --git a/src/app/i18n/strings.cpp b/src/app/i18n/strings.cpp index 23bebc0e4..8663d6291 100644 --- a/src/app/i18n/strings.cpp +++ b/src/app/i18n/strings.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2016 David Capello +// Copyright (C) 2016, 2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -61,7 +61,7 @@ Strings::Strings() } } -const std::string& Strings::translate(const char* id) +const std::string& Strings::translate(const char* id) const { auto it = m_strings.find(id); if (it != m_strings.end()) diff --git a/src/app/i18n/strings.h b/src/app/i18n/strings.h index b371f41b0..52c9ff182 100644 --- a/src/app/i18n/strings.h +++ b/src/app/i18n/strings.h @@ -16,17 +16,17 @@ namespace app { - // Singleton class to load and access "strings/english.txt" file. - class Strings { + // Singleton class to load and access "strings/en.ini" file. + class Strings : public app::gen::Strings { public: static Strings* instance(); - const std::string& translate(const char* id); + const std::string& translate(const char* id) const; private: Strings(); - std::unordered_map m_strings; + mutable std::unordered_map m_strings; }; } // namespace app diff --git a/src/app/job.cpp b/src/app/job.cpp index 2038ca0f9..8c4a77b49 100644 --- a/src/app/job.cpp +++ b/src/app/job.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -12,9 +12,11 @@ #include "app/app.h" #include "app/console.h" +#include "app/i18n/strings.h" #include "base/mutex.h" #include "base/scoped_lock.h" #include "base/thread.h" +#include "fmt/format.h" #include "ui/alert.h" #include "ui/widget.h" #include "ui/window.h" @@ -43,7 +45,8 @@ Job::Job(const char* jobName) m_mutex = new base::mutex(); if (App::instance()->isGui()) { - m_alert_window = ui::Alert::create("%s<addProgress(); m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window.get())); diff --git a/src/app/launcher.cpp b/src/app/launcher.cpp index b93a289c6..8ca6a75fb 100644 --- a/src/app/launcher.cpp +++ b/src/app/launcher.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2015, 2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -10,8 +10,10 @@ #include "app/launcher.h" +#include "app/i18n/strings.h" #include "base/exception.h" #include "base/launcher.h" +#include "fmt/format.h" #include "ui/alert.h" namespace app { @@ -25,13 +27,13 @@ void open_url(const std::string& url) void open_file(const std::string& file) { if (!base::launcher::open_file(file)) - ui::Alert::show("Problem<isModified()) { // ask what want to do the user with the changes in the sprite - int ret = Alert::show("Warning" - "<name().c_str(), - quitting ? "quitting": "closing"); + int ret = Alert::show( + fmt::format( + Strings::alerts_save_sprite_changes(), + m_document->name(), + (quitting ? Strings::alerts_save_sprite_changes_quitting(): + Strings::alerts_save_sprite_changes_closing()))); if (ret == 1) { // "save": save the changes diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp index 8374d0014..9bab2a410 100644 --- a/src/app/ui/editor/tool_loop_impl.cpp +++ b/src/app/ui/editor/tool_loop_impl.cpp @@ -20,6 +20,7 @@ #include "app/context.h" #include "app/context_access.h" #include "app/document_undo.h" +#include "app/i18n/strings.h" #include "app/modules/gui.h" #include "app/modules/palettes.h" #include "app/pref/preferences.h" @@ -48,6 +49,7 @@ #include "doc/remap.h" #include "doc/slice.h" #include "doc/sprite.h" +#include "fmt/format.h" #include "render/dithering.h" #include "render/render.h" #include "ui/ui.h" @@ -591,10 +593,7 @@ tools::ToolLoop* create_tool_loop( app::Color bg = colorbar->getBgColor(); if (!fg.isValid() || !bg.isValid()) { - Alert::show(PACKAGE - "< |" + " \\ : * ? \" < > |" #endif - "||&OK"); + ; + + ui::Alert::show( + fmt::format( + Strings::alerts_invalid_chars_in_filename(), + invalid_chars)); // show the window again setVisible(true); @@ -646,8 +652,10 @@ again: } if (m_type == FileSelectorType::Save && base::is_file(buf)) { - int ret = Alert::show("Warning<(this)->translate(\"" << textId << "\"); }\n"; + std::cout << " static const std::string& " << to_cpp(textId) << "() { return T::instance()->translate(\"" << textId << "\"); }\n"; textId.erase(section.size()+1); } diff --git a/src/ui/alert.cpp b/src/ui/alert.cpp index 47ea9767f..dfd7cdf89 100644 --- a/src/ui/alert.cpp +++ b/src/ui/alert.cpp @@ -45,7 +45,6 @@ #include "ui/slider.h" #include "ui/theme.h" -#include #include namespace ui { @@ -73,13 +72,10 @@ void Alert::setProgress(double progress) m_progress->setValue(int(MID(0.0, progress * 100.0, 100.0))); } -AlertPtr Alert::create(const char* format, ...) +// static +AlertPtr Alert::create(const std::string& _msg) { - // Process arguments - std::va_list ap; - va_start(ap, format); - std::string msg = base::string_vprintf(format, ap); - va_end(ap); + std::string msg(_msg); // Create the alert window AlertPtr window(new Alert()); @@ -88,13 +84,9 @@ AlertPtr Alert::create(const char* format, ...) } // static -int Alert::show(const char* format, ...) +int Alert::show(const std::string& _msg) { - // Process arguments - std::va_list ap; - va_start(ap, format); - std::string msg = base::string_vprintf(format, ap); - va_end(ap); + std::string msg(_msg); // Create the alert window AlertPtr window(new Alert()); @@ -134,7 +126,14 @@ void Alert::processString(std::string& buf) // Process buffer c = 0; beg = 0; - for (; ; c++) { + for (;;) { + // Ignore characters + if (buf[c] == '\n' || + buf[c] == '\r') { + buf.erase(c, 1); + continue; + } + if ((!buf[c]) || ((buf[c] == buf[c+1]) && ((buf[c] == '<') || @@ -185,9 +184,10 @@ void Alert::processString(std::string& buf) case '-': separator=true; break; case '|': button=true; break; } - c++; + ++c; } } + ++c; } auto box1 = new Box(VERTICAL); diff --git a/src/ui/alert.h b/src/ui/alert.h index 37c30709f..99bb6ebe0 100644 --- a/src/ui/alert.h +++ b/src/ui/alert.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -31,8 +31,8 @@ namespace ui { int show(); - static AlertPtr create(const char* format, ...); - static int show(const char* format, ...); + static AlertPtr create(const std::string& msg); + static int show(const std::string& msg); private: void processString(std::string& buf);