Skip to content

Commit e882e42

Browse files
timothyqiubruvzg
andcommitted
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 <[email protected]>
1 parent ebb96e2 commit e882e42

File tree

11 files changed

+202
-61
lines changed

11 files changed

+202
-61
lines changed

core/io/translation_loader_po.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
112112
int p_start = config.find("Plural-Forms");
113113
if (p_start != -1) {
114114
int p_end = config.find_char('\n', p_start);
115-
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
115+
translation->set_plural_rules_override(config.substr(p_start, p_end - p_start));
116116
}
117117
} else {
118118
uint32_t str_start = 0;
@@ -228,7 +228,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
228228
int p_start = config.find("Plural-Forms");
229229
if (p_start != -1) {
230230
int p_end = config.find_char('\n', p_start);
231-
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
231+
translation->set_plural_rules_override(config.substr(p_start, p_end - p_start));
232232
plural_forms = translation->get_plural_forms();
233233
}
234234
}

core/string/locales.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,3 +1194,50 @@ static const char *script_list[][2] = {
11941194
{ "Zanabazar Square", "Zanb" },
11951195
{ nullptr, nullptr }
11961196
};
1197+
1198+
// Plural rules.
1199+
// Reference:
1200+
// - https://github.com/unicode-org/cldr/blob/main/common/supplemental/plurals.xml
1201+
static const char *plural_rules[][2] = {
1202+
{ "bm bo dz hnj id ig ii in ja jbo jv jw kde kea km ko lkt lo ms my nqo osa root sah ses sg su th to tpi vi wo yo yue zh", "nplurals=1; plural=0;" },
1203+
{ "am as bn doi fa gu hi kn pcm zu", "nplurals=2; plural=(n==0 || n==1);" },
1204+
{ "ff hy kab", "nplurals=2; plural=(n > 1);" },
1205+
{ "ast de en et fi fy gl ia io ji lij nl sc sv sw ur yi", "nplurals=2; plural=(n != 1);" },
1206+
{ "si", "nplurals=2; plural=(n > 1);" },
1207+
{ "ak bho csw guw ln mg nso pa ti wa", "nplurals=2; plural=(n > 1);" },
1208+
{ "tzm", "nplurals=2; plural=(n<=1 || (n>=11 && n<=99));" },
1209+
{ "af an asa az bal bem bez bg brx ce cgg chr ckb dv ee el eo eu fo fur gsw ha haw hu jgo jmc ka kaj kcg kk kkj kl ks ksb ku ky lb lg mas mgo ml mn mr nah nb nd ne nn nnh no nr ny nyn om or os pap ps rm rof rwk saq sd sdh seh sn so sq ss ssy st syr ta te teo tig tk tn tr ts ug uz ve vo vun wae xh xog", "nplurals=2; plural=(n != 1);" },
1210+
{ "da", "nplurals=2; plural=(n != 1);" },
1211+
{ "is", "nplurals=2; plural=(n%10==1 && n%100!=11);" },
1212+
{ "mk", "nplurals=2; plural=(n%10==1 && n%100!=11);" },
1213+
{ "ceb fil tl", "nplurals=2; plural=(n==1 || n==2 || n==3 || (n%10!=4 && n%10!=6 && n%10!=9));" },
1214+
{ "lv prg", "nplurals=3; plural=(n%10==0 || (n%100>=11 && n%100<=19) ? 0 : n%10==1 && n%100!=11 ? 1 : 2);" },
1215+
{ "lag", "nplurals=3; plural=(n==0 ? 0 : (n==0 || n==1) && n!=0 ? 1 : 2);" },
1216+
{ "blo", "nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2);" },
1217+
{ "ksh", "nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2);" },
1218+
{ "he iw", "nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);" },
1219+
{ "iu naq sat se sma smi smj smn sms", "nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);" },
1220+
{ "shi", "nplurals=3; plural=(n==0 || n==1 ? 0 : n>=2 && n<=10 ? 1 : 2);" },
1221+
{ "mo ro", "nplurals=3; plural=(n==1 ? 0 : n==0 || (n!=1 && n%100>=1 && n%100<=19) ? 1 : 2);" },
1222+
{ "bs hr sh sr", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);" },
1223+
{ "fr", "nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);" },
1224+
{ "pt", "nplurals=3; plural=((n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);" },
1225+
{ "ca it lld pt_PT scn vec", "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);" },
1226+
{ "es", "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);" },
1227+
{ "gd", "nplurals=4; plural=(n==1 || n==11 ? 0 : n==2 || n==12 ? 1 : (n>=3 && n<=10) || (n>=13 && n<=19) ? 2 : 3);" },
1228+
{ "sl", "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n%100<=4 ? 2 : 3);" },
1229+
{ "dsb hsb", "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n%100<=4 ? 2 : 3);" },
1230+
{ "cs sk", "nplurals=3; plural=(n==1 ? 0 : n>=2 && n<=4 ? 1 : 2);" },
1231+
{ "pl", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);" },
1232+
{ "be", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);" },
1233+
{ "lt", "nplurals=3; plural=(n%10==1 && (n%100<11 || n%100>19) ? 0 : n%10>=2 && n%10<=9 && (n%100<11 || n%100>19) ? 1 : 2);" },
1234+
{ "ru uk", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);" },
1235+
{ "br", "nplurals=5; plural=(n%10==1 && n%100!=11 && n%100!=71 && n%100!=91 ? 0 : n%10==2 && n%100!=12 && n%100!=72 && n%100!=92 ? 1 : ((n%10>=3 && n%10<=4) || n%10==9) && (n%100<10 || n%100>19) && (n%100<70 || n%100>79) && (n%100<90 || n%100>99) ? 2 : n!=0 && n%1000000==0 ? 3 : 4);" },
1236+
{ "mt", "nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n==0 || (n%100>=3 && n%100<=10) ? 2 : n%100>=11 && n%100<=19 ? 3 : 4);" },
1237+
{ "ga", "nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4);" },
1238+
{ "gv", "nplurals=4; plural=(n%10==1 ? 0 : n%10==2 ? 1 : n%100==0 || n%100==20 || n%100==40 || n%100==60 || n%100==80 ? 2 : 3);" },
1239+
{ "kw", "nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n%100==2 || n%100==22 || n%100==42 || n%100==62 || n%100==82 || (n%1000==0 && ((n%100000>=1000 && n%100000<=20000) || n%100000==40000 || n%100000==60000 || n%100000==80000)) || (n!=0 && n%1000000==100000) ? 2 : n%100==3 || n%100==23 || n%100==43 || n%100==63 || n%100==83 ? 3 : n!=1 && (n%100==1 || n%100==21 || n%100==41 || n%100==61 || n%100==81) ? 4 : 5);" },
1240+
{ "ar ars", "nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);" },
1241+
{ "cy", "nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n==3 ? 3 : n==6 ? 4 : 5);" },
1242+
{ nullptr, nullptr },
1243+
};

core/string/translation.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "translation.h"
3232

3333
#include "core/os/thread.h"
34+
#include "core/string/plural_rules.h"
3435
#include "core/string/translation_server.h"
3536

3637
Dictionary Translation::_get_messages() const {
@@ -73,6 +74,11 @@ void Translation::_set_messages(const Dictionary &p_messages) {
7374

7475
void Translation::set_locale(const String &p_locale) {
7576
locale = TranslationServer::get_singleton()->standardize_locale(p_locale);
77+
78+
if (plural_rules_cache && plural_rules_override.is_empty()) {
79+
memdelete(plural_rules_cache);
80+
plural_rules_cache = nullptr;
81+
}
7682
}
7783

7884
void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
@@ -131,6 +137,44 @@ int Translation::get_message_count() const {
131137
return translation_map.size();
132138
}
133139

140+
PluralRules *Translation::_get_plural_rules() const {
141+
if (plural_rules_cache) {
142+
return plural_rules_cache;
143+
}
144+
145+
if (!plural_rules_override.is_empty()) {
146+
plural_rules_cache = PluralRules::parse(plural_rules_override);
147+
}
148+
149+
if (!plural_rules_cache) {
150+
// Locale's default plural rules.
151+
const String &default_rule = TranslationServer::get_singleton()->get_plural_rules(locale);
152+
if (!default_rule.is_empty()) {
153+
plural_rules_cache = PluralRules::parse(default_rule);
154+
}
155+
156+
// Use English plural rules as a fallback.
157+
if (!plural_rules_cache) {
158+
plural_rules_cache = PluralRules::parse("nplurals=2; plural=(n != 1);");
159+
}
160+
}
161+
162+
DEV_ASSERT(plural_rules_cache != nullptr);
163+
return plural_rules_cache;
164+
}
165+
166+
void Translation::set_plural_rules_override(const String &p_rules) {
167+
plural_rules_override = p_rules;
168+
if (plural_rules_cache) {
169+
memdelete(plural_rules_cache);
170+
plural_rules_cache = nullptr;
171+
}
172+
}
173+
174+
String Translation::get_plural_rules_override() const {
175+
return plural_rules_override;
176+
}
177+
134178
void Translation::_bind_methods() {
135179
ClassDB::bind_method(D_METHOD("set_locale", "locale"), &Translation::set_locale);
136180
ClassDB::bind_method(D_METHOD("get_locale"), &Translation::get_locale);
@@ -144,10 +188,20 @@ void Translation::_bind_methods() {
144188
ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count);
145189
ClassDB::bind_method(D_METHOD("_set_messages", "messages"), &Translation::_set_messages);
146190
ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages);
191+
ClassDB::bind_method(D_METHOD("set_plural_rules_override", "rules"), &Translation::set_plural_rules_override);
192+
ClassDB::bind_method(D_METHOD("get_plural_rules_override"), &Translation::get_plural_rules_override);
147193

148194
GDVIRTUAL_BIND(_get_plural_message, "src_message", "src_plural_message", "n", "context");
149195
GDVIRTUAL_BIND(_get_message, "src_message", "context");
150196

151197
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");
152198
ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale");
199+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "plural_rules_override"), "set_plural_rules_override", "get_plural_rules_override");
200+
}
201+
202+
Translation::~Translation() {
203+
if (plural_rules_cache) {
204+
memdelete(plural_rules_cache);
205+
plural_rules_cache = nullptr;
206+
}
153207
}

core/string/translation.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include "core/io/resource.h"
3434
#include "core/object/gdvirtual.gen.inc"
3535

36+
class PluralRules;
37+
3638
class Translation : public Resource {
3739
GDCLASS(Translation, Resource);
3840
OBJ_SAVE_TYPE(Translation);
@@ -41,13 +43,18 @@ class Translation : public Resource {
4143
String locale = "en";
4244
HashMap<StringName, StringName> translation_map;
4345

46+
mutable PluralRules *plural_rules_cache = nullptr;
47+
String plural_rules_override;
48+
4449
virtual Vector<String> _get_message_list() const;
4550
virtual Dictionary _get_messages() const;
4651
virtual void _set_messages(const Dictionary &p_messages);
4752

4853
protected:
4954
static void _bind_methods();
5055

56+
PluralRules *_get_plural_rules() const;
57+
5158
GDVIRTUAL2RC(StringName, _get_message, StringName, StringName);
5259
GDVIRTUAL4RC(StringName, _get_plural_message, StringName, StringName, int, StringName);
5360

@@ -64,5 +71,8 @@ class Translation : public Resource {
6471
virtual int get_message_count() const;
6572
virtual Vector<String> get_translated_message_list() const;
6673

67-
Translation() {}
74+
void set_plural_rules_override(const String &p_rules);
75+
String get_plural_rules_override() const;
76+
77+
~Translation();
6878
};

core/string/translation_po.cpp

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,6 @@ Vector<String> TranslationPO::_get_message_list() const {
131131
return v;
132132
}
133133

134-
void TranslationPO::set_plural_rule(const String &p_plural_rule) {
135-
if (plural_rules) {
136-
memdelete(plural_rules);
137-
}
138-
plural_rules = PluralRules::parse(p_plural_rule);
139-
}
140-
141134
void TranslationPO::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
142135
HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context];
143136

@@ -150,8 +143,7 @@ void TranslationPO::add_message(const StringName &p_src_text, const StringName &
150143
}
151144

152145
void TranslationPO::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) {
153-
ERR_FAIL_NULL_MSG(plural_rules, "Plural rules are not set. Please call set_plural_rule() before calling add_plural_message().");
154-
ERR_FAIL_COND_MSG(p_plural_xlated_texts.size() != plural_rules->get_nplurals(), vformat("Trying to add plural texts that don't match the required number of plural forms for locale \"%s\".", get_locale()));
146+
ERR_FAIL_COND_MSG(p_plural_xlated_texts.size() != _get_plural_rules()->get_nplurals(), vformat("Trying to add plural texts that don't match the required number of plural forms for locale \"%s\".", get_locale()));
155147

156148
HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context];
157149

@@ -166,11 +158,11 @@ void TranslationPO::add_plural_message(const StringName &p_src_text, const Vecto
166158
}
167159

168160
int TranslationPO::get_plural_forms() const {
169-
return plural_rules ? plural_rules->get_nplurals() : 0;
161+
return _get_plural_rules()->get_nplurals();
170162
}
171163

172164
String TranslationPO::get_plural_rule() const {
173-
return plural_rules ? plural_rules->get_plural() : String();
165+
return _get_plural_rules()->get_plural();
174166
}
175167

176168
StringName TranslationPO::get_message(const StringName &p_src_text, const StringName &p_context) const {
@@ -184,14 +176,13 @@ StringName TranslationPO::get_message(const StringName &p_src_text, const String
184176

185177
StringName TranslationPO::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
186178
ERR_FAIL_COND_V_MSG(p_n < 0, StringName(), "N passed into translation to get a plural message should not be negative. For negative numbers, use singular translation please. Search \"gettext PO Plural Forms\" online for the documentation on translating negative numbers.");
187-
ERR_FAIL_NULL_V_MSG(plural_rules, StringName(), "Plural rules are not set. Please call set_plural_rule() before calling get_plural_message().");
188179

189180
if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) {
190181
return StringName();
191182
}
192183
ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), vformat("Source text \"%s\" is registered but doesn't have a translation. Please report this bug.", String(p_src_text)));
193184

194-
int plural_index = plural_rules->evaluate(p_n);
185+
int plural_index = _get_plural_rules()->evaluate(p_n);
195186
ERR_FAIL_COND_V_MSG(plural_index < 0 || translation_map[p_context][p_src_text].size() < plural_index + 1, StringName(), "Plural index returned or number of plural translations is not valid. Please report this bug.");
196187

197188
return translation_map[p_context][p_src_text][plural_index];
@@ -234,10 +225,3 @@ void TranslationPO::_bind_methods() {
234225
ClassDB::bind_method(D_METHOD("get_plural_forms"), &TranslationPO::get_plural_forms);
235226
ClassDB::bind_method(D_METHOD("get_plural_rule"), &TranslationPO::get_plural_rule);
236227
}
237-
238-
TranslationPO::~TranslationPO() {
239-
if (plural_rules) {
240-
memdelete(plural_rules);
241-
plural_rules = nullptr;
242-
}
243-
}

core/string/translation_po.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434

3535
#include "core/string/translation.h"
3636

37-
class PluralRules;
38-
3937
class TranslationPO : public Translation {
4038
GDCLASS(TranslationPO, Translation);
4139

@@ -46,8 +44,6 @@ class TranslationPO : public Translation {
4644
// Strings without context have "" as first key.
4745
HashMap<StringName, HashMap<StringName, Vector<StringName>>> translation_map;
4846

49-
PluralRules *plural_rules = nullptr;
50-
5147
Vector<String> _get_message_list() const override;
5248
Dictionary _get_messages() const override;
5349
void _set_messages(const Dictionary &p_messages) override;
@@ -65,13 +61,10 @@ class TranslationPO : public Translation {
6561
StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override;
6662
void erase_message(const StringName &p_src_text, const StringName &p_context = "") override;
6763

68-
void set_plural_rule(const String &p_plural_rule);
6964
int get_plural_forms() const;
7065
String get_plural_rule() const;
7166

7267
#ifdef DEBUG_TRANSLATION_PO
7368
void print_translation_map();
7469
#endif
75-
76-
~TranslationPO();
7770
};

core/string/translation_server.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,6 @@
3636
#include "core/os/os.h"
3737
#include "core/string/locales.h"
3838

39-
Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
40-
41-
HashMap<String, String> TranslationServer::language_map;
42-
HashMap<String, String> TranslationServer::script_map;
43-
HashMap<String, String> TranslationServer::locale_rename_map;
44-
HashMap<String, String> TranslationServer::country_name_map;
45-
HashMap<String, String> TranslationServer::variant_map;
46-
HashMap<String, String> TranslationServer::country_rename_map;
47-
4839
void TranslationServer::init_locale_info() {
4940
// Init locale info.
5041
language_map.clear();
@@ -113,6 +104,18 @@ void TranslationServer::init_locale_info() {
113104
}
114105
idx++;
115106
}
107+
108+
// Init plural rules.
109+
plural_rules_map.clear();
110+
idx = 0;
111+
while (plural_rules[idx][0] != nullptr) {
112+
const Vector<String> rule_locs = String(plural_rules[idx][0]).split(" ");
113+
const String rule = String(plural_rules[idx][1]);
114+
for (const String &l : rule_locs) {
115+
plural_rules_map[l] = rule;
116+
}
117+
idx++;
118+
}
116119
}
117120

118121
TranslationServer::Locale::operator String() const {
@@ -305,6 +308,26 @@ String TranslationServer::get_locale_name(const String &p_locale) const {
305308
return name;
306309
}
307310

311+
String TranslationServer::get_plural_rules(const String &p_locale) const {
312+
const String *rule = plural_rules_map.getptr(p_locale);
313+
if (rule) {
314+
return *rule;
315+
}
316+
317+
Locale l = Locale(*this, p_locale, false);
318+
if (!l.country.is_empty()) {
319+
rule = plural_rules_map.getptr(l.language + "_" + l.country);
320+
if (rule) {
321+
return *rule;
322+
}
323+
}
324+
rule = plural_rules_map.getptr(l.language);
325+
if (rule) {
326+
return *rule;
327+
}
328+
return String();
329+
}
330+
308331
Vector<String> TranslationServer::get_all_languages() const {
309332
Vector<String> languages;
310333

@@ -584,6 +607,7 @@ void TranslationServer::_bind_methods() {
584607
ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name);
585608

586609
ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
610+
ClassDB::bind_method(D_METHOD("get_plural_rules", "locale"), &TranslationServer::get_plural_rules);
587611

588612
ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName()));
589613
ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName()));

core/string/translation_server.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class TranslationServer : public Object {
6262
String default_country;
6363
HashSet<String> supported_countries;
6464
};
65-
static Vector<LocaleScriptInfo> locale_script_info;
65+
static inline Vector<LocaleScriptInfo> locale_script_info;
6666

6767
struct Locale {
6868
String language;
@@ -82,12 +82,13 @@ class TranslationServer : public Object {
8282
Locale(const TranslationServer &p_server, const String &p_locale, bool p_add_defaults);
8383
};
8484

85-
static HashMap<String, String> language_map;
86-
static HashMap<String, String> script_map;
87-
static HashMap<String, String> locale_rename_map;
88-
static HashMap<String, String> country_name_map;
89-
static HashMap<String, String> country_rename_map;
90-
static HashMap<String, String> variant_map;
85+
static inline HashMap<String, String> language_map;
86+
static inline HashMap<String, String> script_map;
87+
static inline HashMap<String, String> locale_rename_map;
88+
static inline HashMap<String, String> country_name_map;
89+
static inline HashMap<String, String> country_rename_map;
90+
static inline HashMap<String, String> variant_map;
91+
static inline HashMap<String, String> plural_rules_map;
9192

9293
void init_locale_info();
9394

@@ -113,6 +114,7 @@ class TranslationServer : public Object {
113114
String get_country_name(const String &p_country) const;
114115

115116
String get_locale_name(const String &p_locale) const;
117+
String get_plural_rules(const String &p_locale) const;
116118

117119
PackedStringArray get_loaded_locales() const;
118120

0 commit comments

Comments
 (0)