Initial version of a new app::Strings to translate strings

Added a simple en.ini file to translate "New Sprite" dialog.
This commit is contained in:
David Capello 2016-12-17 01:01:57 -03:00
parent dfa90d15c8
commit 42b48cfed2
9 changed files with 212 additions and 33 deletions

View File

@ -93,6 +93,7 @@
<global>
<section id="general">
<option id="language" type="std::string" default="&quot;en&quot;" />
<option id="screen_scale" type="int" default="0" />
<option id="ui_scale" type="int" default="1" migrate="experimental.ui_scale" />
<option id="gpu_acceleration" type="bool" default="false" />

30
data/strings/en.ini Normal file
View File

@ -0,0 +1,30 @@
[new_sprite]
title = New Sprite
size = Size:
width = Width:
height = Height:
background = Background:
transparent = &Transparent
white = &White
black = &Black
color_mode = Color Mode:
rgba = &RGBA
grayscale = &Grayscale
indexed = &Indexed
rgba_tooltip = <<<END
Each pixel has Red, Green, Blue, and Alpha components
(32 bits per pixel)
END
grayscale_tooltip = <<<END
Each pixel has a Gray value and Alpha
(16 bits per pixel)
END
indexed_tooltip = <<<END
Each pixel is a reference to the palette
(8 bits per pixel)
END
advanced_options = Advanced Options
pixel_ratio = Pixel Ratio:
square_pixels = Square Pixels (1:1)
double_wide = Double-wide Pixels (2:1)
double_high = Double-high Pixels (1:2)

View File

@ -1,39 +1,39 @@
<!-- ASEPRITE -->
<!-- Copyright (C) 2001-2016 by David Capello -->
<gui>
<window text="New Sprite" id="new_sprite">
<window text="@new_sprite.title" id="new_sprite">
<box vertical="true">
<separator text="Size:" left="true" horizontal="true" />
<separator text="@new_sprite.size" left="true" horizontal="true" />
<grid columns="2">
<label text="Width:" />
<label text="@new_sprite.width" />
<entry id="width" maxsize="8" magnet="true" cell_align="horizontal" suffix="px" />
<label text="Height:" />
<label text="@new_sprite.height" />
<entry id="height" maxsize="8" cell_align="horizontal" suffix="px" />
</grid>
<separator text="Color Mode:" left="true" horizontal="true" />
<separator text="@new_sprite.color_mode" left="true" horizontal="true" />
<buttonset columns="3" id="color_mode">
<item text="&amp;RGBA" icon="icon_rgb" tooltip="Each pixel has Red, Green, Blue, and Alpha components&#10;(32 bits per pixel)" tooltip_dir="bottom" />
<item text="&amp;Grayscale" icon="icon_grayscale" tooltip="Each pixel has a Gray value and Alpha&#10;(16 bits per pixel)" tooltip_dir="bottom" />
<item text="&amp;Indexed" icon="icon_indexed" tooltip="Each pixel is a reference to the palette&#10;(8 bits per pixel)" tooltip_dir="bottom" />
<item text="@new_sprite.rgba" icon="icon_rgb" tooltip="@new_sprite.rgba_tooltip" tooltip_dir="bottom" />
<item text="@new_sprite.grayscale" icon="icon_grayscale" tooltip="@new_sprite.grayscale_tooltip" tooltip_dir="bottom" />
<item text="@new_sprite.indexed" icon="icon_indexed" tooltip="@new_sprite.indexed_tooltip" tooltip_dir="bottom" />
</buttonset>
<separator text="Background:" left="true" horizontal="true" />
<separator text="@new_sprite.background" left="true" horizontal="true" />
<buttonset columns="3" id="bg_color">
<item text="&amp;Transparent" icon="icon_transparent" />
<item text="&amp;White" icon="icon_white" />
<item text="&amp;Black" icon="icon_black" />
<item text="@new_sprite.transparent" icon="icon_transparent" />
<item text="@new_sprite.white" icon="icon_white" />
<item text="@new_sprite.black" icon="icon_black" />
</buttonset>
<check id="advanced_check" text="Advanced Options" />
<check id="advanced_check" text="@new_sprite.advanced_options" />
<vbox id="advanced">
<grid columns="2">
<label text="Pixel Ratio:" />
<label text="@new_sprite.pixel_ratio" />
<combobox id="pixel_ratio" cell_align="horizontal">
<listitem text="Square Pixels (1:1)" value="1:1" />
<listitem text="Double-wide Pixels (2:1)" value="2:1" />
<listitem text="Double-high Pixels (1:2)" value="1:2" />
<listitem text="@new_sprite.square_pixels" value="1:1" />
<listitem text="@new_sprite.double_wide" value="2:1" />
<listitem text="@new_sprite.double_high" value="1:2" />
</combobox>
</grid>
</vbox>

View File

@ -334,6 +334,7 @@ add_library(app-lib
filename_formatter.cpp
flatten.cpp
gui_xml.cpp
i18n/strings.cpp
ini_file.cpp
job.cpp
launcher.cpp

69
src/app/i18n/strings.cpp Normal file
View File

@ -0,0 +1,69 @@
// Aseprite
// Copyright (C) 2016 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/strings.h"
#include "app/pref/preferences.h"
#include "app/resource_finder.h"
#include "app/xml_document.h"
#include "app/xml_exception.h"
#include "base/fs.h"
#include "cfg/cfg.h"
namespace app {
// static
Strings* Strings::instance()
{
static Strings* singleton = nullptr;
if (!singleton)
singleton = new Strings();
return singleton;
}
Strings::Strings()
{
const std::string lang = Preferences::instance().general.language();
LOG("I18N: Loading strings/%s.ini file\n", lang.c_str());
ResourceFinder rf;
rf.includeDataDir(("strings/" + lang + ".ini").c_str());
if (!rf.findFirst())
throw base::Exception("strings/" + lang + ".txt was not found");
cfg::CfgFile cfg;
cfg.load(rf.filename());
std::vector<std::string> sections;
std::vector<std::string> keys;
cfg.getAllSections(sections);
for (const auto& section : sections) {
keys.clear();
cfg.getAllKeys(section.c_str(), keys);
std::string textId = section;
textId.push_back('.');
for (auto key : keys) {
textId.append(key);
m_strings[textId] = cfg.getValue(section.c_str(), key.c_str(), "");
//TRACE("I18N: Reading string %s -> %s\n", textId.c_str(), m_strings[textId].c_str());
textId.erase(section.size()+1);
}
}
}
const std::string& Strings::translate(const char* id)
{
return m_strings[id];
}
} // namespace app

34
src/app/i18n/strings.h Normal file
View File

@ -0,0 +1,34 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_I18N_STRINGS_INCLUDED
#define APP_I18N_STRINGS_INCLUDED
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
namespace app {
// Singleton class to load and access "strings/english.txt" file.
class Strings {
public:
static Strings* instance();
const std::string& translate(const char* id);
private:
Strings();
std::unordered_map<std::string, std::string> m_strings;
};
#define tr(id) (Strings::instance()->translate(id))
} // namespace app
#endif

View File

@ -11,6 +11,7 @@
#include "app/widget_loader.h"
#include "app/app.h"
#include "app/i18n/strings.h"
#include "app/modules/gui.h"
#include "app/resource_finder.h"
#include "app/ui/button_set.h"
@ -43,6 +44,7 @@ using namespace app::skin;
static int convert_align_value_to_flags(const char *value);
static int int_attr(const TiXmlElement* elem, const char* attribute_name, int default_value);
static std::string text_attr(const TiXmlElement* elem, const char* attribute_name);
WidgetLoader::WidgetLoader()
: m_tooltipManager(NULL)
@ -380,8 +382,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
(middle ? MIDDLE: (bottom ? BOTTOM: TOP));
if (!widget) {
const char* text = elem->Attribute("text");
widget = new Separator(text ? text: "", align);
widget = new Separator(text_attr(elem, "text"), align);
}
else
widget->setAlign(widget->align() | align);
@ -411,13 +412,12 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
}
else if (elem_name == "window") {
if (!widget) {
const char* text = elem->Attribute("text");
bool desktop = bool_attr_is_true(elem, "desktop");
if (desktop)
widget = new Window(Window::DesktopWindow);
else if (text)
widget = new Window(Window::WithTitleBar, text);
else if (elem->Attribute("text"))
widget = new Window(Window::WithTitleBar, text_attr(elem, "text"));
else
widget = new Window(Window::WithoutTitleBar);
}
@ -428,8 +428,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
}
else if (elem_name == "dropdownbutton") {
if (!widget) {
const char* text = elem->Attribute("text");
widget = new DropDownButton(text);
widget = new DropDownButton(text_attr(elem, "text").c_str());
}
}
else if (elem_name == "buttonset") {
@ -463,7 +462,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
}
if (text)
item->setText(text);
item->setText(text_attr(elem, "text"));
buttonset->addItem(item, hspan, vspan);
fillWidgetWithXmlElementAttributes(elem, root, item);
@ -505,8 +504,6 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem, Widget* root, Widget* widget)
{
const char* id = elem->Attribute("id");
const char* text = elem->Attribute("text");
const char* tooltip = elem->Attribute("tooltip");
const char* tooltip_dir = elem->Attribute("tooltip_dir");
bool selected = bool_attr_is_true(elem, "selected");
bool disabled = bool_attr_is_true(elem, "disabled");
@ -534,13 +531,13 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem,
if (!maxheight) maxheight = height;
}
if (id != NULL)
if (id)
widget->setId(id);
if (text)
widget->setText(text);
if (elem->Attribute("text"))
widget->setText(text_attr(elem, "text"));
if (tooltip && root) {
if (elem->Attribute("tooltip") && root) {
if (!m_tooltipManager) {
m_tooltipManager = new ui::TooltipManager();
root->addChild(m_tooltipManager);
@ -553,7 +550,8 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem,
else if (strcmp(tooltip_dir, "left") == 0) dir = LEFT;
else if (strcmp(tooltip_dir, "right") == 0) dir = RIGHT;
}
m_tooltipManager->addTooltipFor(widget, tooltip, dir);
m_tooltipManager->addTooltipFor(widget, text_attr(elem, "tooltip"), dir);
}
if (selected)
@ -703,4 +701,15 @@ static int int_attr(const TiXmlElement* elem, const char* attribute_name, int de
return (value ? strtol(value, NULL, 10): default_value);
}
static std::string text_attr(const TiXmlElement* elem, const char* attribute_name)
{
const char* value = elem->Attribute(attribute_name);
if (!value)
return std::string();
else if (*value == '@')
return Strings::instance()->translate(value+1);
else
return std::string(value);
}
} // namespace app

View File

@ -16,6 +16,8 @@
#include <cstdlib>
#include <iostream>
#include <list>
#include "SimpleIni.h"
namespace cfg {
@ -26,6 +28,24 @@ public:
return m_filename;
}
void getAllSections(std::vector<std::string>& sections) const {
std::list<CSimpleIniA::Entry> sectionsList;
m_ini.GetAllSections(sectionsList);
sections.reserve(sectionsList.size());
for (const auto& section : sectionsList)
sections.push_back(section.pItem);
}
void getAllKeys(const char* section, std::vector<std::string>& keys) const {
std::list<CSimpleIniA::Entry> keysList;
if (!m_ini.GetAllKeys(section, keysList))
return;
keys.reserve(keysList.size());
for (const auto& k : keysList)
keys.push_back(k.pItem);
}
const char* getValue(const char* section, const char* name, const char* defaultValue) const {
return m_ini.GetValue(section, name, defaultValue);
}
@ -67,6 +87,7 @@ public:
base::FileHandle file(base::open_file(m_filename, "rb"));
if (file) {
m_ini.SetMultiLine();
SI_Error err = m_ini.LoadFile(file.get());
if (err != SI_OK) {
LOG(ERROR) << "CFG: Error " << err << " loading configuration from " << m_filename << "\n";
@ -104,6 +125,16 @@ const std::string& CfgFile::filename() const
return m_impl->filename();
}
void CfgFile::getAllSections(std::vector<std::string>& sections) const
{
m_impl->getAllSections(sections);
}
void CfgFile::getAllKeys(const char* section, std::vector<std::string>& keys) const
{
m_impl->getAllKeys(section, keys);
}
const char* CfgFile::getValue(const char* section, const char* name, const char* defaultValue) const
{
return m_impl->getValue(section, name, defaultValue);

View File

@ -1,5 +1,5 @@
// Aseprite Config Library
// Copyright (c) 2014 David Capello
// Copyright (c) 2014-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -9,6 +9,7 @@
#pragma once
#include <string>
#include <vector>
namespace cfg {
@ -19,6 +20,9 @@ namespace cfg {
const std::string& filename() const;
void getAllSections(std::vector<std::string>& sections) const;
void getAllKeys(const char* section, std::vector<std::string>& keys) const;
const char* getValue(const char* section, const char* name, const char* defaultValue) const;
bool getBoolValue(const char* section, const char* name, bool defaultValue);
int getIntValue(const char* section, const char* name, int defaultValue);