Enable loading of po files in addons

This commit is contained in:
Celtic Minstrel 2016-11-14 20:01:41 -05:00
parent 6c32ccb3c3
commit fffe3d5d7a
2 changed files with 162 additions and 0 deletions

View file

@ -1724,6 +1724,16 @@
91B621F41B76BCB000B00E0F /* ucs4_iterator_base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ucs4_iterator_base.hpp; sourceTree = "<group>"; };
91B621F51B76BCB000B00E0F /* unicode_cast.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = unicode_cast.hpp; sourceTree = "<group>"; };
91B621F61B76BCB000B00E0F /* unicode_types.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = unicode_types.hpp; sourceTree = "<group>"; };
91BE78371DD8F5DE00528C21 /* catalog.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catalog.hpp; sourceTree = "<group>"; };
91BE78381DD8F5DE00528C21 /* catalog_metadata.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catalog_metadata.hpp; sourceTree = "<group>"; };
91BE78391DD8F5DE00528C21 /* default_plural_forms_compiler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = default_plural_forms_compiler.hpp; sourceTree = "<group>"; };
91BE783A1DD8F5DE00528C21 /* default_plural_forms_expressions.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = default_plural_forms_expressions.hpp; sourceTree = "<group>"; };
91BE783B1DD8F5DE00528C21 /* exceptions.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = exceptions.hpp; sourceTree = "<group>"; };
91BE783C1DD8F5DE00528C21 /* po_grammar.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = po_grammar.hpp; sourceTree = "<group>"; };
91BE783D1DD8F5DE00528C21 /* po_message.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = po_message.hpp; sourceTree = "<group>"; };
91BE783E1DD8F5DE00528C21 /* po_message_adapted.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = po_message_adapted.hpp; sourceTree = "<group>"; };
91BE783F1DD8F5DE00528C21 /* version.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = version.hpp; sourceTree = "<group>"; };
91BE78401DD8F5DE00528C21 /* spirit_po.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = spirit_po.hpp; sourceTree = "<group>"; };
91C548CE1D8866ED00FE6A7B /* campaignd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = campaignd; sourceTree = BUILT_PRODUCTS_DIR; };
91C548CF1D886AF000FE6A7B /* send_receive_wml_helpers.ipp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = send_receive_wml_helpers.ipp; sourceTree = "<group>"; };
91C548D01D886AF000FE6A7B /* server_base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = server_base.cpp; sourceTree = "<group>"; };
@ -3095,6 +3105,8 @@
B52EE8C5121359A600CFBDAB /* sound_music_track.hpp */,
B559999D0EC62181008DD061 /* soundsource.cpp */,
B559999C0EC62181008DD061 /* soundsource.hpp */,
91BE78361DD8F5DE00528C21 /* spirit_po */,
91BE78401DD8F5DE00528C21 /* spirit_po.hpp */,
B559999B0EC62181008DD061 /* statistics.cpp */,
B559999A0EC62181008DD061 /* statistics.hpp */,
B55999990EC62181008DD061 /* statistics_dialog.cpp */,
@ -3603,6 +3615,22 @@
path = xBRZ;
sourceTree = "<group>";
};
91BE78361DD8F5DE00528C21 /* spirit_po */ = {
isa = PBXGroup;
children = (
91BE78371DD8F5DE00528C21 /* catalog.hpp */,
91BE78381DD8F5DE00528C21 /* catalog_metadata.hpp */,
91BE78391DD8F5DE00528C21 /* default_plural_forms_compiler.hpp */,
91BE783A1DD8F5DE00528C21 /* default_plural_forms_expressions.hpp */,
91BE783B1DD8F5DE00528C21 /* exceptions.hpp */,
91BE783C1DD8F5DE00528C21 /* po_grammar.hpp */,
91BE783D1DD8F5DE00528C21 /* po_message.hpp */,
91BE783E1DD8F5DE00528C21 /* po_message_adapted.hpp */,
91BE783F1DD8F5DE00528C21 /* version.hpp */,
);
path = spirit_po;
sourceTree = "<group>";
};
91EF6BF01C9E217C00E2A733 /* utils */ = {
isa = PBXGroup;
children = (

View file

@ -15,14 +15,18 @@
#include "global.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "filesystem.hpp"
#include <iostream>
#include <fstream>
#include <locale>
#include <boost/locale.hpp>
// including boost/thread fixes linking of boost locale for msvc on boost 1.60
#include <boost/thread.hpp>
#include <set>
#include "spirit_po.hpp"
#define DBG_G LOG_STREAM(debug, lg::general())
#define LOG_G LOG_STREAM(info, lg::general())
#define WRN_G LOG_STREAM(warn, lg::general())
@ -66,6 +70,135 @@ namespace
std::string name_;
};
class wesnoth_message_format : public bl::message_format<char>
{
using po_catalog = spirit_po::catalog<>;
public:
wesnoth_message_format(std::locale base, const std::set<std::string>& domains, const std::set<std::string>& paths)
: base_loc_(base)
{
const bl::info& inf = std::use_facet<bl::info>(base);
if(inf.language() == "c") {
return;
}
std::string lang_name_short = inf.language();
std::string lang_name_long = lang_name_short;
if(!inf.country().empty()) {
lang_name_long += '_';
lang_name_long += inf.country();
}
if(!inf.variant().empty()) {
lang_name_long += '@';
lang_name_long += inf.variant();
lang_name_short += '@';
lang_name_short += inf.variant();
}
DBG_G << "Loading po files for language " << lang_name_long << '\n';
for(auto& domain : domains) {
DBG_G << "Searching for po files for domain " << domain << '\n';
std::string path;
for(auto base_path : paths) {
DBG_G << "Searching in dir " << base_path << '\n';
if(base_path[base_path.length()-1] != '/') {
base_path += '/';
}
base_path += domain;
base_path += '/';
path = base_path + lang_name_long + ".po";
DBG_G << " Trying path " << path << '\n';
if(filesystem::file_exists(path)) {
break;
}
path = base_path + lang_name_short + ".po";
DBG_G << " Trying path " << path << '\n';
if(filesystem::file_exists(path)) {
break;
}
}
if(!filesystem::file_exists(path)) {
continue;
}
std::ifstream po_file;
po_file.exceptions(std::ios::badbit);
LOG_G << "Loading language file from " << path << '\n';
try {
po_file.open(path);
const po_catalog& cat = po_catalog::from_istream(po_file);
extra_messages_.emplace(get_base().domain(domain), cat);
} catch(spirit_po::catalog_exception& e) {
throw_po_error(lang_name_long, domain, e.what());
} catch(std::ios::failure& e) {
throw_po_error(lang_name_long, domain, strerror(errno));
}
}
}
NORETURN static void throw_po_error(const std::string& lang, const std::string& dom, const std::string& detail) {
std::ostringstream err;
err << "Error opening language file for " << lang << ", textdomain " << dom
<< ":\n " << detail << '\n';
ERR_G << err.rdbuf() << std::flush;
throw game::error(err.str());
}
const char* get(int domain_id, const char* ctx, const char* id) const override
{
auto& base = get_base();
const char* msg = base.get(domain_id, ctx, id);
if(msg == nullptr) {
auto iter = extra_messages_.find(domain_id);
if(iter == extra_messages_.end()) {
return nullptr;
}
auto& catalog = iter->second;
const char* lookup = ctx ? catalog.pgettext(ctx, id) : catalog.gettext(id);
if(lookup != id) {
// (p)gettext returns the input pointer if the string was not found
msg = lookup;
}
}
return msg;
}
const char* get(int domain_id, const char* ctx, const char* sid, int n) const override
{
auto& base = get_base();
const char* msg = base.get(domain_id, ctx, sid, n);
if(msg == nullptr) {
auto iter = extra_messages_.find(domain_id);
if(iter == extra_messages_.end()) {
return nullptr;
}
auto& catalog = iter->second;
const char* lookup = ctx ? catalog.npgettext(ctx, sid, sid, n) : catalog.ngettext(sid, sid, n);
if(lookup != sid) {
// n(p)gettext returns one of the input pointers if the string was not found
msg = lookup;
}
}
return msg;
}
int domain(const std::string& domain) const override
{
auto& base = get_base();
return base.domain(domain);
}
const char* convert(const char* msg, std::string& buffer) const override
{
auto& base = get_base();
return base.convert(msg, buffer);
}
private:
const bl::message_format<char>& get_base() const
{
return std::use_facet<bl::message_format<char>>(base_loc_);
}
std::locale base_loc_;
std::map<int, po_catalog> extra_messages_;
};
struct translation_manager
{
translation_manager()
@ -158,6 +291,7 @@ namespace
{
LOG_G << "attempting to generate locale by name '" << current_language_ << "'\n";
current_locale_ = generator_.generate(current_language_);
current_locale_ = std::locale(current_locale_, new wesnoth_message_format(current_locale_, loaded_domains_, loaded_paths_));
const bl::info& info = std::use_facet<bl::info>(current_locale_);
LOG_G << "updated locale to '" << current_language_ << "' locale is now '" << current_locale_.name() << "' ( "
<< "name='" << info.name()