Add support for gettext plurals to Lua API

This commit is contained in:
Celtic Minstrel 2016-08-29 16:42:41 -04:00
parent 116ff67c6e
commit f866039e81
4 changed files with 125 additions and 11 deletions

View file

@ -67,11 +67,10 @@ local function generate_objectives(cfg)
local turn_limit = wesnoth.game_config.last_turn
if turn_limit >= current_turn then
if turn_limit - current_turn + 1 > 1 then
turn_counter = "<span foreground='white'><small> " .. string.format(tostring(_"(%d turns left)"), turn_limit - current_turn + 1) .. "</small></span>"
else
turn_counter = "<span foreground='white'><small> " .. _"(this turn left)" .. "</small></span>"
end
local turn_count = turn_limit - current_turn + 1
turn_counter = _("(this turn left)", "(%d turns left)", turn_count)
turn_counter = tostring(turn_counter):format(turn_count)
turn_counter = "<span foreground='white'><small> " .. turn_counter .. "</small></span>"
end
end

View file

@ -68,7 +68,13 @@ static int impl_gettext(lua_State *L)
char const *m = luaL_checkstring(L, 2);
char const *d = static_cast<char *>(lua_touserdata(L, 1));
// Hidden metamethod, so d has to be a string. Use it to create a t_string.
luaW_pushtstring(L, t_string(m, d));
if(lua_isstring(L, 3)) {
const char* pl = luaL_checkstring(L, 3);
int count = luaL_checkinteger(L, 4);
luaW_pushtstring(L, t_string(m, pl, count, d));
} else {
luaW_pushtstring(L, t_string(m, d));
}
return 1;
}

View file

@ -45,6 +45,7 @@ namespace {
const char UNTRANSLATABLE_PART = 0x02;
const char TEXTDOMAIN_SEPARATOR = 0x03;
const char ID_TRANSLATABLE_PART = 0x04;
const char PLURAL_PART = 0x05;
std::vector<std::string> id_to_textdomain;
std::map<std::string, unsigned int> textdomain_to_id;
@ -70,18 +71,19 @@ t_string_base::walker::walker(const t_string_base& string) :
}
}
static std::string mark = std::string(TRANSLATABLE_PART, 1) + UNTRANSLATABLE_PART +
ID_TRANSLATABLE_PART + PLURAL_PART;
void t_string_base::walker::update()
{
unsigned int id;
static std::string mark = std::string(TRANSLATABLE_PART, 1) + UNTRANSLATABLE_PART +
ID_TRANSLATABLE_PART;
if(begin_ == string_.size())
return;
switch(string_[begin_]) {
case TRANSLATABLE_PART: {
// Format: [TRANSLATABLE_PART]textdomain[TEXTDOMAIN_SEPARATOR]msgid[...]
std::string::size_type textdomain_end =
string_.find(TEXTDOMAIN_SEPARATOR, begin_ + 1);
@ -102,6 +104,7 @@ void t_string_base::walker::update()
break;
}
case ID_TRANSLATABLE_PART:
// Format: [ID_TRANSLATABLE_PART][2-byte textdomain ID]msgid[...]
if(begin_ + 3 >= string_.size()) {
ERR_CF << "Error: invalid string: " << string_ << std::endl;
begin_ = string_.size();
@ -139,12 +142,64 @@ void t_string_base::walker::update()
begin_ += 1;
break;
case PLURAL_PART:
begin_ = string_.find_first_of(mark, end_ + 5);
if(begin_ == PLURAL_PART) {
ERR_CF << "Error: invalid string: " << string_ << std::endl;
begin_ = string_.size();
return;
}
update();
break;
default:
end_ = string_.size();
translatable_ = false;
textdomain_ = "";
break;
}
if(translatable_ && string_[end_] == PLURAL_PART) {
// Format: [PLURAL_PART][4-byte count]msgid_plural[...]
if(end_ + 5 >= string_.size()) {
ERR_CF << "Error: invalid string: " << string_ << std::endl;
begin_ = string_.size();
return;
}
std::string::size_type real_end = string_.find_first_of(mark, end_ + 6);
if(real_end < string_.size() && string_[real_end] == PLURAL_PART) {
ERR_CF << "Error: invalid string: " << string_ << std::endl;
begin_ = string_.size();
return;
}
countable_ = true;
count_ = *reinterpret_cast<const int32_t*>(string_.data() + end_ + 1);
} else {
countable_ = false;
count_ = 0;
}
}
std::string::const_iterator t_string_base::walker::plural_begin() const
{
if(!countable_) {
return begin();
}
return end() + 5;
}
std::string::const_iterator t_string_base::walker::plural_end() const
{
if(!countable_) {
return end();
}
std::string::size_type pl_end = string_.find_first_of(mark, end_ + 5);
if(pl_end == std::string::npos) {
pl_end = string_.size();
}
return string_.begin() + pl_end;
}
t_string_base::t_string_base() :
@ -207,6 +262,43 @@ t_string_base::t_string_base(const std::string& string, const std::string& textd
value_ += string;
}
t_string_base::t_string_base(const std::string& sing, const std::string& pl, int count, const std::string& textdomain) :
value_(1, ID_TRANSLATABLE_PART),
translated_value_(),
translation_timestamp_(0),
translatable_(true),
last_untranslatable_(false)
{
if (sing.empty() && pl.empty()) {
value_.clear();
translatable_ = false;
return;
}
std::map<std::string, unsigned int>::const_iterator idi = textdomain_to_id.find(textdomain);
unsigned int id;
if(idi == textdomain_to_id.end()) {
id = id_to_textdomain.size();
textdomain_to_id[textdomain] = id;
id_to_textdomain.push_back(textdomain);
} else {
id = idi->second;
}
value_ += char(id & 0xff);
value_ += char(id >> 8);
value_ += sing;
value_ += PLURAL_PART;
char count_val[4];
*reinterpret_cast<int32_t*>(count_val) = count;
for(char c : count_val) {
value_ += c;
}
value_ += pl;
}
t_string_base::t_string_base(const char* string) :
value_(string),
translated_value_(),
@ -444,7 +536,12 @@ const std::string& t_string_base::str() const
std::string part(w.begin(), w.end());
if(w.translatable()) {
translated_value_ += translation::dsgettext(w.textdomain().c_str(), part.c_str());
if(w.countable()) {
std::string plural(w.plural_begin(), w.plural_end());
translated_value_ += translation::dsngettext(w.textdomain().c_str(), part.c_str(), plural.c_str(), w.count());
} else {
translated_value_ += translation::dsgettext(w.textdomain().c_str(), part.c_str());
}
} else {
translated_value_ += part;
}
@ -482,6 +579,11 @@ t_string::t_string(const std::string &o, const std::string &textdomain) : val_(n
{
}
t_string::t_string(const std::string &s, const std::string& pl, int c, const std::string &textdomain)
: val_(new base(s, pl, c, textdomain))
{
}
t_string &t_string::operator=(const t_string &o)
{
val_ = o.val_;

View file

@ -34,9 +34,13 @@ public:
bool eos() const { return begin_ == string_.size(); }
bool last() const { return end_ == string_.size(); }
bool translatable() const { return translatable_; }
bool countable() const { return countable_; }
int count() const { return count_; }
const std::string& textdomain() const { return textdomain_; }
std::string::const_iterator begin() const { return string_.begin() + begin_; }
std::string::const_iterator end() const { return string_.begin() + end_; }
std::string::const_iterator plural_begin() const;
std::string::const_iterator plural_end() const;
private:
void update();
@ -44,7 +48,8 @@ public:
std::string::size_type begin_;
std::string::size_type end_;
std::string textdomain_;
bool translatable_;
bool translatable_, countable_;
int count_;
};
friend class walker;
@ -56,6 +61,7 @@ public:
t_string_base(const t_string_base&);
t_string_base(const std::string& string);
t_string_base(const std::string& string, const std::string& textdomain);
t_string_base(const std::string& sing, const std::string& pl, int count, const std::string& textdomain);
t_string_base(const char* string);
static t_string_base from_serialized(const std::string& string);
@ -129,6 +135,7 @@ public:
t_string(const char *);
t_string(const std::string &);
t_string(const std::string &str, const std::string &textdomain);
t_string(const std::string& sing, const std::string& pl, int count, const std::string& textdomain);
t_string &operator=(const char *o);