Add default plural rules

This makes the PO loader correctly handle the situation where the optional
`Plural-Forms` header field does not exist.

The `Translation` class and its subclasses always have access to valid plural
rules via `_get_plural_rules()`. Plural rules are prioritized:

1. `Translation.plural_rules_override`
2. `TranslationServer.get_plural_rules(locale)`
3. The English plural rules: `nplurals=2; plurals=(n != 1)`

Co-Authored-By: Pāvels Nadtočajevs <7645683+bruvzg@users.noreply.github.com>
This commit is contained in:
Haoyu Qiu 2025-07-21 17:59:14 +08:00
parent ebb96e2303
commit e882e42e1b
11 changed files with 202 additions and 61 deletions

View file

@ -112,22 +112,38 @@ TEST_CASE("[TranslationPO] Messages with context") {
}
TEST_CASE("[TranslationPO] Plural messages") {
Ref<TranslationPO> translation = memnew(TranslationPO);
translation->set_locale("fr");
translation->set_plural_rule("Plural-Forms: nplurals=2; plural=(n >= 2);");
CHECK(translation->get_plural_forms() == 2);
{
Ref<TranslationPO> translation = memnew(TranslationPO);
translation->set_locale("fr");
CHECK(translation->get_plural_forms() == 3);
CHECK(translation->get_plural_rule() == "(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2)");
}
PackedStringArray plurals;
plurals.push_back("Il y a %d pomme");
plurals.push_back("Il y a %d pommes");
translation->add_plural_message("There are %d apples", plurals);
ERR_PRINT_OFF;
// This is invalid, as the number passed to `get_plural_message()` may not be negative.
CHECK(vformat(translation->get_plural_message("There are %d apples", "", -1), -1) == "");
ERR_PRINT_ON;
CHECK(vformat(translation->get_plural_message("There are %d apples", "", 0), 0) == "Il y a 0 pomme");
CHECK(vformat(translation->get_plural_message("There are %d apples", "", 1), 1) == "Il y a 1 pomme");
CHECK(vformat(translation->get_plural_message("There are %d apples", "", 2), 2) == "Il y a 2 pommes");
{
Ref<TranslationPO> translation = memnew(TranslationPO);
translation->set_locale("invalid");
CHECK(translation->get_plural_forms() == 2);
CHECK(translation->get_plural_rule() == "(n != 1)");
}
{
Ref<TranslationPO> translation = memnew(TranslationPO);
translation->set_plural_rules_override("Plural-Forms: nplurals=2; plural=(n >= 2);");
CHECK(translation->get_plural_forms() == 2);
CHECK(translation->get_plural_rule() == "(n >= 2)");
PackedStringArray plurals;
plurals.push_back("Il y a %d pomme");
plurals.push_back("Il y a %d pommes");
translation->add_plural_message("There are %d apples", plurals);
ERR_PRINT_OFF;
// This is invalid, as the number passed to `get_plural_message()` may not be negative.
CHECK(vformat(translation->get_plural_message("There are %d apples", "", -1), -1) == "");
ERR_PRINT_ON;
CHECK(vformat(translation->get_plural_message("There are %d apples", "", 0), 0) == "Il y a 0 pomme");
CHECK(vformat(translation->get_plural_message("There are %d apples", "", 1), 1) == "Il y a 1 pomme");
CHECK(vformat(translation->get_plural_message("There are %d apples", "", 2), 2) == "Il y a 2 pommes");
}
}
TEST_CASE("[TranslationPO] Plural rules parsing") {