Skip to content

Commit

Permalink
Update add-on i18n for tinygettext (CB #4815 / GH #6453)
Browse files Browse the repository at this point in the history
Co-authored-by: Benedikt Straub <[email protected]>
Co-committed-by: Benedikt Straub <[email protected]>
  • Loading branch information
Noordfrees authored and The Widelands Bunny Bot committed May 20, 2024
1 parent 0e0afa2 commit 0551a17
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 36 deletions.
15 changes: 11 additions & 4 deletions src/base/i18n.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ const std::string& get_addon_locale_dir() {
return canonical_addon_locale_dir;
}

static inline std::string textdomain_cache_key(const std::string& domain, const std::string& ldir) {
return ldir + "/" + domain;
}

GenericTextdomain::GenericTextdomain() : lock_(MutexLock::ID::kI18N) {
}
GenericTextdomain::~GenericTextdomain() {
Expand All @@ -243,12 +247,15 @@ GenericTextdomain::~GenericTextdomain() {
Textdomain::Textdomain(const std::string& name) {
grab_textdomain(name, get_localedir());
}
AddOnTextdomain::AddOnTextdomain(std::string addon, const int i18n_version) {
addon += '.';
addon += std::to_string(i18n_version);
AddOnTextdomain::AddOnTextdomain(const std::string& addon) {
grab_textdomain(addon, get_addon_locale_dir());
}

void clear_addon_translations_cache(const std::string& addon) {
MutexLock m(MutexLock::ID::kI18N);
g_dictionary_cache.erase(textdomain_cache_key(addon, get_addon_locale_dir()));
}

/**
* Grab a given TextDomain. If a new one is grabbed, it is pushed on the stack.
* On release, it is dropped and the previous one is re-grabbed instead.
Expand All @@ -259,7 +266,7 @@ AddOnTextdomain::AddOnTextdomain(std::string addon, const int i18n_version) {
*/
void grab_textdomain(const std::string& domain, const std::string& ldir) {
log_i18n_if_desired_("textdomain", (domain + " @ " + ldir).c_str());
textdomains.emplace_back(new TextdomainStackEntry(ldir + "/" + domain));
textdomains.emplace_back(new TextdomainStackEntry(textdomain_cache_key(domain, ldir)));
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/base/i18n.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const std::string& npgettext_wrapper(const std::string& msgctxt,

void grab_textdomain(const std::string& domain, const std::string& ldir);
void release_textdomain();
void clear_addon_translations_cache(const std::string& addon);

void init_locale();
void set_locale(const std::string&);
Expand Down Expand Up @@ -113,7 +114,7 @@ struct Textdomain : GenericTextdomain {
};
struct AddOnTextdomain : GenericTextdomain {
// For strings defined in an add-on
explicit AddOnTextdomain(std::string addon, int i18n_version);
explicit AddOnTextdomain(const std::string& addon);
};

enum class ConcatenateWith { AND, OR, AMPERSAND, COMMA };
Expand Down
10 changes: 5 additions & 5 deletions src/logic/addons.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ const AddOnInfo* find_addon(const std::string& name) {
}

i18n::GenericTextdomain* create_textdomain_for_addon(std::string addon, const std::string& dflt) {
if (const AddOnInfo* a = find_addon(addon)) {
return new i18n::AddOnTextdomain(addon, a->i18n_version);
if (find_addon(addon) != nullptr) {
return new i18n::AddOnTextdomain(addon);
}
return dflt.empty() ? nullptr : new i18n::Textdomain(dflt);
}
Expand Down Expand Up @@ -486,15 +486,15 @@ std::shared_ptr<AddOnInfo> preload_addon(const std::string& name) {
i->min_wl_version = s.get_string("min_wl_version", "");
i->max_wl_version = s.get_string("max_wl_version", "");
i->descname = [i]() {
i18n::AddOnTextdomain td(i->internal_name, i->i18n_version);
i18n::AddOnTextdomain td(i->internal_name);
return i18n::translate(i->unlocalized_descname);
};
i->description = [i]() {
i18n::AddOnTextdomain td(i->internal_name, i->i18n_version);
i18n::AddOnTextdomain td(i->internal_name);
return i18n::translate(i->unlocalized_description);
};
i->author = [i]() {
i18n::AddOnTextdomain td(i->internal_name, i->i18n_version);
i18n::AddOnTextdomain td(i->internal_name);
return i18n::translate(i->unlocalized_author);
};
i->icon = g_image_cache->get(fs->file_exists(kAddOnIconFile) ?
Expand Down
2 changes: 1 addition & 1 deletion src/network/net_addons.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ constexpr unsigned kCurrentProtocolVersion = 7;
static const std::string kCmdList = "2:CMD_LIST";
static const std::string kCmdInfo = "2:CMD_INFO";
static const std::string kCmdDownload = "1:CMD_DOWNLOAD";
static const std::string kCmdI18N = "1:CMD_I18N";
static const std::string kCmdI18N = "2:CMD_I18N";
static const std::string kCmdScreenshot = "1:CMD_SCREENSHOT";
static const std::string kCmdVote = "1:CMD_VOTE";
static const std::string kCmdGetVote = "1:CMD_GET_VOTE";
Expand Down
6 changes: 3 additions & 3 deletions src/network/net_addons.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ struct NetAddons {
// from the server and downloads it to the given canonical location.
void download_addon(const std::string& name, const std::string& save_as, const CallbackFn&);

// Requests the MO files for the given add-on (cool_feature.wad) from the server and
// Requests the PO files for the given add-on (cool_feature.wad) from the server and
// downloads them into the given temporary location (e.g. ~/.widelands/temp/some_dir).
// The filename of the created MO files is guaranteed to be in the format
// "nds.mo.tmp" (where 'nds' is the language's abbreviation).
// The filename of the created PO files is guaranteed to be in the format
// "nds.po.tmp" (where 'nds' is the language's abbreviation).
void download_i18n(const std::string& name,
const std::string& directory,
const CallbackFn& progress,
Expand Down
32 changes: 10 additions & 22 deletions src/ui_fsmenu/addons/manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1334,36 +1334,23 @@ bool AddOnsCtrl::is_remote(const std::string& name) const {
}

static void install_translation(const std::string& temp_locale_path,
const std::string& addon_name,
const int i18n_version) {
const std::string& addon_name) {
assert(g_fs->file_exists(temp_locale_path));

// NOTE:
// gettext expects a directory structure such as
// "~/.widelands/addons_i18n/nds/LC_MESSAGES/addon_name.wad.VERSION.mo"
// where "nds" is the language abbreviation, VERSION the add-on's i18n version,
// and "addon_name.wad" the add-on's name.
// If we use a different structure, gettext will not find the translations!

const std::string temp_filename =
FileSystem::fs_filename(temp_locale_path.c_str()); // nds.mo.tmp
FileSystem::fs_filename(temp_locale_path.c_str()); // nds.po.tmp
const std::string locale = temp_filename.substr(0, temp_filename.find('.')); // nds

const std::string new_locale_dir = kAddOnLocaleDir + FileSystem::file_separator() + locale +
FileSystem::file_separator() +
"LC_MESSAGES"; // addons_i18n/nds/LC_MESSAGES
const std::string new_locale_dir =
kAddOnLocaleDir + FileSystem::file_separator() + addon_name; // addons_i18n/name.wad
g_fs->ensure_directory_exists(new_locale_dir);

const std::string new_locale_path = new_locale_dir + FileSystem::file_separator() + addon_name +
'.' + std::to_string(i18n_version) + ".mo";
const std::string new_locale_path = new_locale_dir + FileSystem::file_separator() + locale +
".po"; // addons_i18n/name.wad/nds.po

assert(!g_fs->is_directory(new_locale_path));
// Delete outdated translations if present.
for (const std::string& mo : g_fs->list_directory(new_locale_dir)) {
if (strncmp(FileSystem::fs_filename(mo.c_str()), addon_name.c_str(), addon_name.size()) ==
0) {
g_fs->fs_unlink(mo);
}
if (g_fs->file_exists(new_locale_path)) {
g_fs->fs_unlink(new_locale_path);
}
assert(!g_fs->file_exists(new_locale_path));

Expand Down Expand Up @@ -1561,8 +1548,9 @@ void AddOnsCtrl::install_or_upgrade(std::shared_ptr<AddOns::AddOnInfo> remote,
});

for (const std::string& n : g_fs->list_directory(temp_dir)) {
install_translation(n, remote->internal_name, remote->i18n_version);
install_translation(n, remote->internal_name);
}
i18n::clear_addon_translations_cache(remote->internal_name);
for (auto& pair : AddOns::g_addons) {
if (pair.first->internal_name == remote->internal_name) {
pair.first->i18n_version = remote->i18n_version;
Expand Down

0 comments on commit 0551a17

Please sign in to comment.