diff --git a/src/gettext_plural_form.cpp b/src/gettext_plural_form.cpp index 6a5322421..89dd721a4 100644 --- a/src/gettext_plural_form.cpp +++ b/src/gettext_plural_form.cpp @@ -90,16 +90,16 @@ class TernaryOperation: public GettextPluralForm }; typedef std::pair ParserResult; -typedef ParserResult (*Parser)(const size_t, const std::wstring_view &); +typedef ParserResult (*Parser)(const size_t, std::wstring_view); -static ParserResult parse_expr(const size_t nplurals, const std::wstring_view &str); +static ParserResult parse_expr(const size_t nplurals, std::wstring_view str); template typename Operator> static ParserResult reduce_ltr(const size_t nplurals, const ParserResult &res, const wchar_t* pattern) { if (!str_starts_with(res.second, pattern)) return ParserResult(nullptr, res.second); - auto next = Parser(nplurals, res.second.substr(std::char_traits::length(pattern))); + auto next = Parser(nplurals, trim(res.second.substr(std::char_traits::length(pattern)))); if (!next.first) return next; next.first = GettextPluralForm::Ptr(new BinaryOperation(res.first, next.first)); @@ -123,7 +123,7 @@ static ParserResult reduce_ltr(const size_t nplurals, const ParserResult &res, c } template typename Operator, template typename... Operators> -static ParserResult parse_ltr(const size_t nplurals, const std::wstring_view &str, const wchar_t** patterns) +static ParserResult parse_ltr(const size_t nplurals, std::wstring_view str, const wchar_t** patterns) { auto &&pres = Parser(nplurals, str); if (!pres.first) @@ -139,7 +139,7 @@ static ParserResult parse_ltr(const size_t nplurals, const std::wstring_view &st return pres; } -static ParserResult parse_atomic(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_atomic(const size_t nplurals, std::wstring_view str) { if (str.empty()) return ParserResult(nullptr, str); @@ -151,7 +151,7 @@ static ParserResult parse_atomic(const size_t nplurals, const std::wstring_view return ParserResult(new ConstValue(nplurals, val), trim(str.substr(endp-str.data()))); } -static ParserResult parse_parenthesized(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_parenthesized(const size_t nplurals, std::wstring_view str) { if (str.empty()) return ParserResult(nullptr, str); @@ -167,7 +167,7 @@ static ParserResult parse_parenthesized(const size_t nplurals, const std::wstrin return result; } -static ParserResult parse_negation(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_negation(const size_t nplurals, std::wstring_view str) { if (str.empty()) return ParserResult(nullptr, str); @@ -179,43 +179,43 @@ static ParserResult parse_negation(const size_t nplurals, const std::wstring_vie return result; } -static ParserResult parse_multiplicative(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_multiplicative(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"*", L"/", L"%" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_additive(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_additive(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"+", L"-" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_comparison(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_comparison(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"<=", L">=", L"<", L">" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_equality(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_equality(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"==", L"!=" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_conjunction(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_conjunction(const size_t nplurals, std::wstring_view str) { static const wchar_t *and_pattern[] = { L"&&" }; return parse_ltr(nplurals, str, and_pattern); } -static ParserResult parse_disjunction(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_disjunction(const size_t nplurals, std::wstring_view str) { static const wchar_t *or_pattern[] = { L"||" }; return parse_ltr(nplurals, str, or_pattern); } -static ParserResult parse_ternary(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_ternary(const size_t nplurals, std::wstring_view str) { auto pres = parse_disjunction(nplurals, str); if (pres.second.empty() || pres.second[0] != '?') // no ? : @@ -229,12 +229,12 @@ static ParserResult parse_ternary(const size_t nplurals, const std::wstring_view return ParserResult(new TernaryOperation(cond, val, pres.first), pres.second); } -static ParserResult parse_expr(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_expr(const size_t nplurals, std::wstring_view str) { return parse_ternary(nplurals, trim(str)); } -GettextPluralForm::Ptr GettextPluralForm::parse(const size_t nplurals, const std::wstring_view &str) +GettextPluralForm::Ptr GettextPluralForm::parse(const size_t nplurals, std::wstring_view str) { if (nplurals == 0) return nullptr; @@ -244,7 +244,7 @@ GettextPluralForm::Ptr GettextPluralForm::parse(const size_t nplurals, const std return result.first; } -GettextPluralForm::Ptr GettextPluralForm::parseHeaderLine(const std::wstring_view &str) +GettextPluralForm::Ptr GettextPluralForm::parseHeaderLine(std::wstring_view str) { if (!str_starts_with(str, L"Plural-Forms: nplurals=") || !str_ends_with(str, L";")) return nullptr; diff --git a/src/gettext_plural_form.h b/src/gettext_plural_form.h index d73718965..1d3195e9a 100644 --- a/src/gettext_plural_form.h +++ b/src/gettext_plural_form.h @@ -24,8 +24,8 @@ public: } virtual ~GettextPluralForm() {}; - static GettextPluralForm::Ptr parse(const size_t nplurals, const std::wstring_view &str); - static GettextPluralForm::Ptr parseHeaderLine(const std::wstring_view &str); + static GettextPluralForm::Ptr parse(const size_t nplurals, std::wstring_view str); + static GettextPluralForm::Ptr parseHeaderLine(std::wstring_view str); protected: GettextPluralForm(size_t nplurals): nplurals(nplurals) {}; private: diff --git a/src/unittest/test_translations.cpp b/src/unittest/test_translations.cpp index 37fc78ee4..5bab3e15c 100644 --- a/src/unittest/test_translations.cpp +++ b/src/unittest/test_translations.cpp @@ -25,12 +25,41 @@ TEST_CASE("test translations") { SECTION("Plural-Forms function for translations") { - auto form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; plural= (n-1+1)<=1 ? n||1?0:1 : 1?(!!2):2;"); - REQUIRE(form); - REQUIRE(form->size() == 3); +#define REQUIRE_FORM_SIZE(x) {REQUIRE(form); REQUIRE(form->size() == (x));} + // Test cases from https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html + auto form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=2; plural=n != 1;"); + REQUIRE_FORM_SIZE(2); + CHECK((*form)(0) == 1); + CHECK((*form)(1) == 0); + CHECK((*form)(2) == 1); + + form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;"); + REQUIRE_FORM_SIZE(3); + CHECK((*form)(0) == 2); + CHECK((*form)(1) == 0); + CHECK((*form)(102) == 1); + CHECK((*form)(111) == 1); + + form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; " + "plural=n%10==1 && n%100!=11 ? 0 : " + "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"); + REQUIRE_FORM_SIZE(3); + CHECK((*form)(0) == 2); + CHECK((*form)(1) == 0); + CHECK((*form)(102) == 1); + CHECK((*form)(104) == 1); + CHECK((*form)(111) == 2); + CHECK((*form)(112) == 2); + CHECK((*form)(121) == 0); + CHECK((*form)(122) == 1); + + // Edge cases + form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; plural= (n-1+1)<=1 ? n||1?0:1 : 1?(!!2):2;"); + REQUIRE_FORM_SIZE(3); CHECK((*form)(0) == 0); CHECK((*form)(1) == 0); CHECK((*form)(2) == 1); +#undef REQUIRE_FORM_SIZE } SECTION("PO file parser")