mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-18 11:42:47 +00:00
Add displayName property for language extensions (fix #3964)
The default language (en.ini) has a new "display_name" property, but probably we should remove it and transform the English language in an extension (just as the default Aseprite theme).
This commit is contained in:
parent
35e64ad2f3
commit
00b75a76a8
@ -2,6 +2,13 @@
|
|||||||
# Copyright (C) 2018-2023 Igara Studio S.A.
|
# Copyright (C) 2018-2023 Igara Studio S.A.
|
||||||
# Copyright (C) 2016-2018 David Capello
|
# Copyright (C) 2016-2018 David Capello
|
||||||
|
|
||||||
|
# Don't translate this string, in extensions you have to use the
|
||||||
|
# "displayName" property of your "languages" item, see:
|
||||||
|
#
|
||||||
|
# https://aseprite.org/docs/extensions/languages/
|
||||||
|
#
|
||||||
|
display_name = English
|
||||||
|
|
||||||
[advanced_mode]
|
[advanced_mode]
|
||||||
title = Warning - Important
|
title = Warning - Important
|
||||||
description = You are going to enter in "Advanced Mode".
|
description = You are going to enter in "Advanced Mode".
|
||||||
|
@ -164,6 +164,19 @@ class OptionsWindow : public app::gen::Options {
|
|||||||
std::string m_name;
|
std::string m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LangItem : public ListItem {
|
||||||
|
public:
|
||||||
|
LangItem(const LangInfo& langInfo)
|
||||||
|
: ListItem(langInfo.displayName)
|
||||||
|
, m_langInfo(langInfo) {
|
||||||
|
}
|
||||||
|
const std::string& langId() const {
|
||||||
|
return m_langInfo.id;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
LangInfo m_langInfo;
|
||||||
|
};
|
||||||
|
|
||||||
class ExtensionItem : public ListItem {
|
class ExtensionItem : public ListItem {
|
||||||
public:
|
public:
|
||||||
ExtensionItem(Extension* extension)
|
ExtensionItem(Extension* extension)
|
||||||
@ -641,8 +654,9 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Update language
|
// Update language
|
||||||
Strings::instance()->setCurrentLanguage(
|
if (auto item = dynamic_cast<const LangItem*>(language()->getSelectedItem())) {
|
||||||
language()->getItemText(language()->getSelectedItemIndex()));
|
Strings::instance()->setCurrentLanguage(item->langId());
|
||||||
|
}
|
||||||
|
|
||||||
m_globPref.timeline.firstFrame(firstFrame()->textInt());
|
m_globPref.timeline.firstFrame(firstFrame()->textInt());
|
||||||
m_pref.general.showFullPath(showFullPath()->isSelected());
|
m_pref.general.showFullPath(showFullPath()->isSelected());
|
||||||
@ -1285,11 +1299,12 @@ private:
|
|||||||
if (language()->getItemCount() > 0)
|
if (language()->getItemCount() > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Select current language by lang ID
|
||||||
Strings* strings = Strings::instance();
|
Strings* strings = Strings::instance();
|
||||||
std::string curLang = strings->currentLanguage();
|
std::string curLang = strings->currentLanguage();
|
||||||
for (const std::string& lang : strings->availableLanguages()) {
|
for (const LangInfo& lang : strings->availableLanguages()) {
|
||||||
int i = language()->addItem(lang);
|
int i = language()->addItem(new LangItem(lang));
|
||||||
if (lang == curLang)
|
if (lang.id == curLang)
|
||||||
language()->setSelectedItemIndex(i);
|
language()->setSelectedItemIndex(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,13 +271,17 @@ void Extension::addKeys(const std::string& id, const std::string& path)
|
|||||||
updateCategory(Category::Keys);
|
updateCategory(Category::Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Extension::addLanguage(const std::string& id, const std::string& path)
|
void Extension::addLanguage(const std::string& id,
|
||||||
|
const std::string& path,
|
||||||
|
const std::string& displayName)
|
||||||
{
|
{
|
||||||
m_languages[id] = path;
|
m_languages[id] = LangInfo(id, path, displayName);
|
||||||
updateCategory(Category::Languages);
|
updateCategory(Category::Languages);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Extension::addTheme(const std::string& id, const std::string& path, const std::string& variant)
|
void Extension::addTheme(const std::string& id,
|
||||||
|
const std::string& path,
|
||||||
|
const std::string& variant)
|
||||||
{
|
{
|
||||||
m_themes[id] = ThemeInfo(path, variant);
|
m_themes[id] = ThemeInfo(path, variant);
|
||||||
updateCategory(Category::Themes);
|
updateCategory(Category::Themes);
|
||||||
@ -871,7 +875,7 @@ std::string Extensions::languagePath(const std::string& langId)
|
|||||||
|
|
||||||
auto it = ext->languages().find(langId);
|
auto it = ext->languages().find(langId);
|
||||||
if (it != ext->languages().end())
|
if (it != ext->languages().end())
|
||||||
return it->second;
|
return it->second.path;
|
||||||
}
|
}
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
@ -1147,15 +1151,19 @@ Extension* Extensions::loadExtension(const std::string& path,
|
|||||||
for (const auto& lang : languages.array_items()) {
|
for (const auto& lang : languages.array_items()) {
|
||||||
std::string langId = lang["id"].string_value();
|
std::string langId = lang["id"].string_value();
|
||||||
std::string langPath = lang["path"].string_value();
|
std::string langPath = lang["path"].string_value();
|
||||||
|
std::string langDisplayName = lang["displayName"].string_value();
|
||||||
|
|
||||||
// The path must be always relative to the extension
|
// The path must be always relative to the extension
|
||||||
langPath = base::join_path(path, langPath);
|
langPath = base::join_path(path, langPath);
|
||||||
|
|
||||||
LOG("EXT: New language id=%s path=%s\n",
|
LOG("EXT: New language id=%s path=%s displayName=%s\n",
|
||||||
langId.c_str(),
|
langId.c_str(),
|
||||||
langPath.c_str());
|
langPath.c_str(),
|
||||||
|
langDisplayName.c_str());
|
||||||
|
|
||||||
extension->addLanguage(langId, langPath);
|
extension->addLanguage(langId,
|
||||||
|
langPath,
|
||||||
|
langDisplayName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define APP_EXTENSIONS_H_INCLUDED
|
#define APP_EXTENSIONS_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/i18n/lang_info.h"
|
||||||
#include "obs/signal.h"
|
#include "obs/signal.h"
|
||||||
#include "render/dithering_matrix.h"
|
#include "render/dithering_matrix.h"
|
||||||
|
|
||||||
@ -80,6 +81,7 @@ namespace app {
|
|||||||
, variant(variant) { }
|
, variant(variant) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Languages = std::map<std::string, LangInfo>;
|
||||||
using Themes = std::map<std::string, ThemeInfo>;
|
using Themes = std::map<std::string, ThemeInfo>;
|
||||||
using DitheringMatrices = std::map<std::string, DitheringMatrixInfo>;
|
using DitheringMatrices = std::map<std::string, DitheringMatrixInfo>;
|
||||||
|
|
||||||
@ -101,13 +103,17 @@ namespace app {
|
|||||||
const Category category() const { return m_category; }
|
const Category category() const { return m_category; }
|
||||||
|
|
||||||
const ExtensionItems& keys() const { return m_keys; }
|
const ExtensionItems& keys() const { return m_keys; }
|
||||||
const ExtensionItems& languages() const { return m_languages; }
|
const Languages& languages() const { return m_languages; }
|
||||||
const Themes& themes() const { return m_themes; }
|
const Themes& themes() const { return m_themes; }
|
||||||
const ExtensionItems& palettes() const { return m_palettes; }
|
const ExtensionItems& palettes() const { return m_palettes; }
|
||||||
|
|
||||||
void addKeys(const std::string& id, const std::string& path);
|
void addKeys(const std::string& id, const std::string& path);
|
||||||
void addLanguage(const std::string& id, const std::string& path);
|
void addLanguage(const std::string& id,
|
||||||
void addTheme(const std::string& id, const std::string& path, const std::string& variant);
|
const std::string& path,
|
||||||
|
const std::string& displayName);
|
||||||
|
void addTheme(const std::string& id,
|
||||||
|
const std::string& path,
|
||||||
|
const std::string& variant);
|
||||||
void addPalette(const std::string& id, const std::string& path);
|
void addPalette(const std::string& id, const std::string& path);
|
||||||
void addDitheringMatrix(const std::string& id,
|
void addDitheringMatrix(const std::string& id,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
@ -152,7 +158,7 @@ namespace app {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
ExtensionItems m_keys;
|
ExtensionItems m_keys;
|
||||||
ExtensionItems m_languages;
|
Languages m_languages;
|
||||||
Themes m_themes;
|
Themes m_themes;
|
||||||
ExtensionItems m_palettes;
|
ExtensionItems m_palettes;
|
||||||
DitheringMatrices m_ditheringMatrices;
|
DitheringMatrices m_ditheringMatrices;
|
||||||
|
36
src/app/i18n/lang_info.h
Normal file
36
src/app/i18n/lang_info.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2023 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_I18N_LANG_INFO_INCLUDED
|
||||||
|
#define APP_I18N_LANG_INFO_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
struct LangInfo {
|
||||||
|
std::string id;
|
||||||
|
std::string path;
|
||||||
|
std::string displayName;
|
||||||
|
|
||||||
|
LangInfo() = default;
|
||||||
|
LangInfo(const std::string& id,
|
||||||
|
const std::string& path,
|
||||||
|
const std::string& displayName)
|
||||||
|
: id(id)
|
||||||
|
, path(path)
|
||||||
|
, displayName(displayName.empty() ? id: displayName) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const LangInfo& other) const {
|
||||||
|
return id < other.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
|
||||||
|
#endif
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2016-2018 David Capello
|
// Copyright (C) 2016-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -19,6 +20,8 @@
|
|||||||
#include "base/fs.h"
|
#include "base/fs.h"
|
||||||
#include "cfg/cfg.h"
|
#include "cfg/cfg.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
static Strings* singleton = nullptr;
|
static Strings* singleton = nullptr;
|
||||||
@ -46,20 +49,29 @@ Strings::Strings(Preferences& pref,
|
|||||||
loadLanguage(currentLanguage());
|
loadLanguage(currentLanguage());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::string> Strings::availableLanguages() const
|
std::set<LangInfo> Strings::availableLanguages() const
|
||||||
{
|
{
|
||||||
std::set<std::string> result;
|
std::set<LangInfo> result;
|
||||||
|
|
||||||
// Add languages in data/strings/
|
// Add languages in data/strings/
|
||||||
ResourceFinder rf;
|
ResourceFinder rf;
|
||||||
rf.includeDataDir("strings");
|
rf.includeDataDir("strings");
|
||||||
while (rf.next()) {
|
while (rf.next()) {
|
||||||
if (!base::is_directory(rf.filename()))
|
const std::string stringsPath = rf.filename();
|
||||||
|
if (!base::is_directory(stringsPath))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const auto& fn : base::list_files(rf.filename())) {
|
for (const auto& fn : base::list_files(stringsPath)) {
|
||||||
const std::string langId = base::get_file_title(fn);
|
const std::string langId = base::get_file_title(fn);
|
||||||
result.insert(langId);
|
std::string path = base::join_path(stringsPath, fn);
|
||||||
|
std::string displayName = langId;
|
||||||
|
|
||||||
|
// Load display name
|
||||||
|
cfg::CfgFile cfg;
|
||||||
|
if (cfg.load(path))
|
||||||
|
displayName = cfg.getValue("", "display_name", displayName.c_str());
|
||||||
|
|
||||||
|
result.insert(LangInfo(langId, path, displayName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,12 +79,17 @@ std::set<std::string> Strings::availableLanguages() const
|
|||||||
for (const auto& ext : m_exts) {
|
for (const auto& ext : m_exts) {
|
||||||
if (ext->isEnabled() &&
|
if (ext->isEnabled() &&
|
||||||
ext->hasLanguages()) {
|
ext->hasLanguages()) {
|
||||||
for (const auto& langId : ext->languages())
|
for (const auto& lang : ext->languages())
|
||||||
result.insert(langId.first);
|
result.insert(lang.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(result.find(kDefLanguage) != result.end());
|
// Check that the default language exists.
|
||||||
|
ASSERT(std::find_if(result.begin(), result.end(),
|
||||||
|
[](const LangInfo& li){
|
||||||
|
return li.id == kDefLanguage;
|
||||||
|
}) != result.end());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2016-2018 David Capello
|
// Copyright (C) 2016-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -8,14 +9,14 @@
|
|||||||
#define APP_I18N_STRINGS_INCLUDED
|
#define APP_I18N_STRINGS_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/i18n/lang_info.h"
|
||||||
|
#include "obs/signal.h"
|
||||||
|
#include "strings.ini.h"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "obs/signal.h"
|
|
||||||
|
|
||||||
#include "strings.ini.h"
|
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
class Preferences;
|
class Preferences;
|
||||||
@ -30,7 +31,7 @@ namespace app {
|
|||||||
|
|
||||||
const std::string& translate(const char* id) const;
|
const std::string& translate(const char* id) const;
|
||||||
|
|
||||||
std::set<std::string> availableLanguages() const;
|
std::set<LangInfo> availableLanguages() const;
|
||||||
std::string currentLanguage() const;
|
std::string currentLanguage() const;
|
||||||
void setCurrentLanguage(const std::string& langId);
|
void setCurrentLanguage(const std::string& langId);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Config Library
|
// Aseprite Config Library
|
||||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2014-2017 David Capello
|
// Copyright (C) 2014-2017 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -88,7 +88,7 @@ public:
|
|||||||
m_ini.Delete(section, nullptr, true);
|
m_ini.Delete(section, nullptr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(const std::string& filename) {
|
bool load(const std::string& filename) {
|
||||||
m_filename = filename;
|
m_filename = filename;
|
||||||
|
|
||||||
base::FileHandle file(base::open_file(m_filename, "rb"));
|
base::FileHandle file(base::open_file(m_filename, "rb"));
|
||||||
@ -98,8 +98,10 @@ public:
|
|||||||
if (err != SI_OK) {
|
if (err != SI_OK) {
|
||||||
LOG(ERROR, "CFG: Error %d loading configuration from %s\n",
|
LOG(ERROR, "CFG: Error %d loading configuration from %s\n",
|
||||||
(int)err, m_filename.c_str());
|
(int)err, m_filename.c_str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void save() {
|
void save() {
|
||||||
@ -193,9 +195,9 @@ void CfgFile::deleteSection(const char* section)
|
|||||||
m_impl->deleteSection(section);
|
m_impl->deleteSection(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgFile::load(const std::string& filename)
|
bool CfgFile::load(const std::string& filename)
|
||||||
{
|
{
|
||||||
m_impl->load(filename);
|
return m_impl->load(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgFile::save()
|
void CfgFile::save()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Config Library
|
// Aseprite Config Library
|
||||||
// Copyright (C) 2019 Igara Studio S.A.
|
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2014-2016 David Capello
|
// Copyright (C) 2014-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -37,7 +37,7 @@ namespace cfg {
|
|||||||
void deleteValue(const char* section, const char* name);
|
void deleteValue(const char* section, const char* name);
|
||||||
void deleteSection(const char* section);
|
void deleteSection(const char* section);
|
||||||
|
|
||||||
void load(const std::string& filename);
|
bool load(const std::string& filename);
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user