1
0
mirror of https://github.com/godotengine/godot.git synced 2025-12-02 16:48:55 +00:00

Add methods for querying loaded Translation instances

This commit is contained in:
Haoyu Qiu
2025-11-09 16:17:04 +08:00
parent b79fe2e020
commit ec860ffe4a
9 changed files with 257 additions and 82 deletions

View File

@@ -32,6 +32,7 @@
#include "core/string/translation.h" #include "core/string/translation.h"
#include "core/string/translation_server.h" #include "core/string/translation_server.h"
#include "core/variant/typed_array.h"
struct _character_accent_pair { struct _character_accent_pair {
const char32_t character; const char32_t character;
@@ -252,27 +253,7 @@ PackedStringArray TranslationDomain::get_loaded_locales() const {
return locales; return locales;
} }
bool TranslationDomain::has_translation_for_locale(const String &p_locale) const { #ifndef DISABLE_DEPRECATED
for (const Ref<Translation> &E : translations) {
if (E->get_locale() == p_locale) {
return true;
}
}
return false;
}
// Translation objects that could potentially be used for the given locale.
HashSet<Ref<Translation>> TranslationDomain::get_potential_translations(const String &p_locale) const {
HashSet<Ref<Translation>> res;
for (const Ref<Translation> &E : translations) {
if (TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale()) > 0) {
res.insert(E);
}
}
return res;
}
Ref<Translation> TranslationDomain::get_translation_object(const String &p_locale) const { Ref<Translation> TranslationDomain::get_translation_object(const String &p_locale) const {
Ref<Translation> res; Ref<Translation> res;
int best_score = 0; int best_score = 0;
@@ -289,6 +270,7 @@ Ref<Translation> TranslationDomain::get_translation_object(const String &p_local
} }
return res; return res;
} }
#endif
void TranslationDomain::add_translation(const Ref<Translation> &p_translation) { void TranslationDomain::add_translation(const Ref<Translation> &p_translation) {
ERR_FAIL_COND_MSG(p_translation.is_null(), "Invalid translation provided."); ERR_FAIL_COND_MSG(p_translation.is_null(), "Invalid translation provided.");
@@ -303,6 +285,69 @@ void TranslationDomain::clear() {
translations.clear(); translations.clear();
} }
const HashSet<Ref<Translation>> TranslationDomain::get_translations() const {
return translations;
}
HashSet<Ref<Translation>> TranslationDomain::find_translations(const String &p_locale, bool p_exact) const {
HashSet<Ref<Translation>> res;
if (p_exact) {
for (const Ref<Translation> &E : translations) {
if (E->get_locale() == p_locale) {
res.insert(E);
}
}
} else {
for (const Ref<Translation> &E : translations) {
if (TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale()) > 0) {
res.insert(E);
}
}
}
return res;
}
bool TranslationDomain::has_translation(const Ref<Translation> &p_translation) const {
return translations.has(p_translation);
}
bool TranslationDomain::has_translation_for_locale(const String &p_locale, bool p_exact) const {
if (p_exact) {
for (const Ref<Translation> &E : translations) {
if (E->get_locale() == p_locale) {
return true;
}
}
} else {
for (const Ref<Translation> &E : translations) {
if (TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale()) > 0) {
return true;
}
}
}
return false;
}
TypedArray<Translation> TranslationDomain::get_translations_bind() const {
TypedArray<Translation> res;
res.reserve(translations.size());
for (const Ref<Translation> &E : translations) {
res.push_back(E);
}
return res;
}
TypedArray<Translation> TranslationDomain::find_translations_bind(const String &p_locale, bool p_exact) const {
const HashSet<Ref<Translation>> &found = find_translations(p_locale, p_exact);
TypedArray<Translation> res;
res.reserve(found.size());
for (const Ref<Translation> &E : found) {
res.push_back(E);
}
return res;
}
StringName TranslationDomain::translate(const StringName &p_message, const StringName &p_context) const { StringName TranslationDomain::translate(const StringName &p_message, const StringName &p_context) const {
if (!enabled) { if (!enabled) {
return p_message; return p_message;
@@ -459,10 +504,17 @@ StringName TranslationDomain::pseudolocalize(const StringName &p_message) const
} }
void TranslationDomain::_bind_methods() { void TranslationDomain::_bind_methods() {
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationDomain::get_translation_object); ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationDomain::get_translation_object);
#endif
ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationDomain::add_translation); ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationDomain::add_translation);
ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationDomain::remove_translation); ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationDomain::remove_translation);
ClassDB::bind_method(D_METHOD("clear"), &TranslationDomain::clear); ClassDB::bind_method(D_METHOD("clear"), &TranslationDomain::clear);
ClassDB::bind_method(D_METHOD("get_translations"), &TranslationDomain::get_translations_bind);
ClassDB::bind_method(D_METHOD("has_translation_for_locale", "locale", "exact"), &TranslationDomain::has_translation_for_locale);
ClassDB::bind_method(D_METHOD("has_translation", "translation"), &TranslationDomain::has_translation);
ClassDB::bind_method(D_METHOD("find_translations", "locale", "exact"), &TranslationDomain::find_translations_bind);
ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationDomain::translate, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationDomain::translate, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("translate_plural", "message", "message_plural", "n", "context"), &TranslationDomain::translate_plural, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("translate_plural", "message", "message_plural", "n", "context"), &TranslationDomain::translate_plural, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("get_locale_override"), &TranslationDomain::get_locale_override); ClassDB::bind_method(D_METHOD("get_locale_override"), &TranslationDomain::get_locale_override);

View File

@@ -71,16 +71,25 @@ public:
StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const; StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const;
StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const; StringName get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const;
PackedStringArray get_loaded_locales() const; PackedStringArray get_loaded_locales() const;
bool has_translation_for_locale(const String &p_locale) const;
HashSet<Ref<Translation>> get_potential_translations(const String &p_locale) const;
public: public:
// These two methods are public for easier TranslationServer bindings.
TypedArray<Translation> get_translations_bind() const;
TypedArray<Translation> find_translations_bind(const String &p_locale, bool p_exact) const;
#ifndef DISABLE_DEPRECATED
Ref<Translation> get_translation_object(const String &p_locale) const; Ref<Translation> get_translation_object(const String &p_locale) const;
#endif
void add_translation(const Ref<Translation> &p_translation); void add_translation(const Ref<Translation> &p_translation);
void remove_translation(const Ref<Translation> &p_translation); void remove_translation(const Ref<Translation> &p_translation);
void clear(); void clear();
bool has_translation(const Ref<Translation> &p_translation) const;
const HashSet<Ref<Translation>> get_translations() const;
HashSet<Ref<Translation>> find_translations(const String &p_locale, bool p_exact) const;
bool has_translation_for_locale(const String &p_locale, bool p_exact) const;
StringName translate(const StringName &p_message, const StringName &p_context) const; StringName translate(const StringName &p_message, const StringName &p_context) const;
StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const; StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const;

View File

@@ -36,6 +36,7 @@
#include "core/os/main_loop.h" #include "core/os/main_loop.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/string/locales.h" #include "core/string/locales.h"
#include "core/variant/typed_array.h"
void TranslationServer::init_locale_info() { void TranslationServer::init_locale_info() {
// Init locale info. // Init locale info.
@@ -503,9 +504,27 @@ void TranslationServer::remove_translation(const Ref<Translation> &p_translation
main_domain->remove_translation(p_translation); main_domain->remove_translation(p_translation);
} }
#ifndef DISABLE_DEPRECATED
Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) { Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
return main_domain->get_translation_object(p_locale); return main_domain->get_translation_object(p_locale);
} }
#endif
TypedArray<Translation> TranslationServer::get_translations() const {
return main_domain->get_translations_bind();
}
TypedArray<Translation> TranslationServer::find_translations(const String &p_locale, bool p_exact) const {
return main_domain->find_translations_bind(p_locale, p_exact);
}
bool TranslationServer::has_translation(const Ref<Translation> &p_translation) const {
return main_domain->has_translation(p_translation);
}
bool TranslationServer::has_translation_for_locale(const String &p_locale, bool p_exact) const {
return main_domain->has_translation_for_locale(p_locale, p_exact);
}
void TranslationServer::clear() { void TranslationServer::clear() {
main_domain->clear(); main_domain->clear();
@@ -576,21 +595,27 @@ void TranslationServer::setup() {
String TranslationServer::get_tool_locale() { String TranslationServer::get_tool_locale() {
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) { if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
if (editor_domain->has_translation_for_locale(locale)) { if (editor_domain->has_translation_for_locale(locale, true)) {
return locale; return locale;
} }
return "en"; return "en";
} else { }
#else
{
#endif #endif
// Look for best matching loaded translation.
Ref<Translation> t = main_domain->get_translation_object(locale); Ref<Translation> res;
if (t.is_null()) { int best_score = 0;
return fallback;
for (const Ref<Translation> &E : main_domain->get_translations()) {
int score = TranslationServer::get_singleton()->compare_locales(locale, E->get_locale());
if (score > 0 && score >= best_score) {
res = E;
best_score = score;
if (score == 10) {
return locale; // Exact match.
} }
return t->get_locale();
} }
}
return res.is_valid() ? res->get_locale() : fallback;
} }
bool TranslationServer::is_pseudolocalization_enabled() const { bool TranslationServer::is_pseudolocalization_enabled() const {
@@ -676,7 +701,15 @@ void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation); ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation); ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object); ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
#endif
ClassDB::bind_method(D_METHOD("get_translations"), &TranslationServer::get_translations);
ClassDB::bind_method(D_METHOD("find_translations", "locale", "exact"), &TranslationServer::find_translations);
ClassDB::bind_method(D_METHOD("has_translation_for_locale", "locale", "exact"), &TranslationServer::has_translation_for_locale);
ClassDB::bind_method(D_METHOD("has_translation", "translation"), &TranslationServer::has_translation);
ClassDB::bind_method(D_METHOD("has_domain", "domain"), &TranslationServer::has_domain); ClassDB::bind_method(D_METHOD("has_domain", "domain"), &TranslationServer::has_domain);
ClassDB::bind_method(D_METHOD("get_or_add_domain", "domain"), &TranslationServer::get_or_add_domain); ClassDB::bind_method(D_METHOD("get_or_add_domain", "domain"), &TranslationServer::get_or_add_domain);

View File

@@ -110,7 +110,15 @@ public:
String get_locale() const; String get_locale() const;
void set_fallback_locale(const String &p_locale); void set_fallback_locale(const String &p_locale);
String get_fallback_locale() const; String get_fallback_locale() const;
#ifndef DISABLE_DEPRECATED
Ref<Translation> get_translation_object(const String &p_locale); Ref<Translation> get_translation_object(const String &p_locale);
#endif
bool has_translation(const Ref<Translation> &p_translation) const;
TypedArray<Translation> get_translations() const;
TypedArray<Translation> find_translations(const String &p_locale, bool p_exact) const;
bool has_translation_for_locale(const String &p_locale, bool p_exact) const;
Vector<String> get_all_languages() const; Vector<String> get_all_languages() const;
String get_language_name(const String &p_language) const; String get_language_name(const String &p_language) const;

View File

@@ -23,19 +23,48 @@
Removes all translations. Removes all translations.
</description> </description>
</method> </method>
<method name="find_translations" qualifiers="const">
<return type="Translation[]" />
<param index="0" name="locale" type="String" />
<param index="1" name="exact" type="bool" />
<description>
Returns the [Translation] instances that match [param locale] (see [method TranslationServer.compare_locales]). If [param exact] is [code]true[/code], only instances whose locale exactly equals [param locale] will be returned.
</description>
</method>
<method name="get_locale_override" qualifiers="const"> <method name="get_locale_override" qualifiers="const">
<return type="String" /> <return type="String" />
<description> <description>
Returns the locale override of the domain. Returns an empty string if locale override is disabled. Returns the locale override of the domain. Returns an empty string if locale override is disabled.
</description> </description>
</method> </method>
<method name="get_translation_object" qualifiers="const"> <method name="get_translation_object" qualifiers="const" deprecated="Use [method find_translations] instead.">
<return type="Translation" /> <return type="Translation" />
<param index="0" name="locale" type="String" /> <param index="0" name="locale" type="String" />
<description> <description>
Returns the [Translation] instance that best matches [param locale]. Returns [code]null[/code] if there are no matches. Returns the [Translation] instance that best matches [param locale]. Returns [code]null[/code] if there are no matches.
</description> </description>
</method> </method>
<method name="get_translations" qualifiers="const">
<return type="Translation[]" />
<description>
Returns all available [Translation] instances as added by [method add_translation].
</description>
</method>
<method name="has_translation" qualifiers="const">
<return type="bool" />
<param index="0" name="translation" type="Translation" />
<description>
Returns [code]true[/code] if this translation domain contains the given [param translation].
</description>
</method>
<method name="has_translation_for_locale" qualifiers="const">
<return type="bool" />
<param index="0" name="locale" type="String" />
<param index="1" name="exact" type="bool" />
<description>
Returns [code]true[/code] if there are any [Translation] instances that match [param locale] (see [method TranslationServer.compare_locales]). If [param exact] is [code]true[/code], only instances whose locale exactly equals [param locale] are considered.
</description>
</method>
<method name="pseudolocalize" qualifiers="const"> <method name="pseudolocalize" qualifiers="const">
<return type="StringName" /> <return type="StringName" />
<param index="0" name="message" type="StringName" /> <param index="0" name="message" type="StringName" />

View File

@@ -33,6 +33,14 @@
Compares two locales and returns a similarity score between [code]0[/code] (no match) and [code]10[/code] (full match). Compares two locales and returns a similarity score between [code]0[/code] (no match) and [code]10[/code] (full match).
</description> </description>
</method> </method>
<method name="find_translations" qualifiers="const">
<return type="Translation[]" />
<param index="0" name="locale" type="String" />
<param index="1" name="exact" type="bool" />
<description>
Returns the [Translation] instances in the main translation domain that match [param locale] (see [method compare_locales]). If [param exact] is [code]true[/code], only instances whose locale exactly equals [param locale] will be returned.
</description>
</method>
<method name="format_number" qualifiers="const"> <method name="format_number" qualifiers="const">
<return type="String" /> <return type="String" />
<param index="0" name="number" type="String" /> <param index="0" name="number" type="String" />
@@ -128,13 +136,19 @@
[b]Note:[/b] When called from an exported project returns the same value as [method get_locale]. [b]Note:[/b] When called from an exported project returns the same value as [method get_locale].
</description> </description>
</method> </method>
<method name="get_translation_object"> <method name="get_translation_object" deprecated="Use [method find_translations] instead.">
<return type="Translation" /> <return type="Translation" />
<param index="0" name="locale" type="String" /> <param index="0" name="locale" type="String" />
<description> <description>
Returns the [Translation] instance that best matches [param locale] in the main translation domain. Returns [code]null[/code] if there are no matches. Returns the [Translation] instance that best matches [param locale] in the main translation domain. Returns [code]null[/code] if there are no matches.
</description> </description>
</method> </method>
<method name="get_translations" qualifiers="const">
<return type="Translation[]" />
<description>
Returns all available [Translation] instances in the main translation domain as added by [method add_translation].
</description>
</method>
<method name="has_domain" qualifiers="const"> <method name="has_domain" qualifiers="const">
<return type="bool" /> <return type="bool" />
<param index="0" name="domain" type="StringName" /> <param index="0" name="domain" type="StringName" />
@@ -142,6 +156,21 @@
Returns [code]true[/code] if a translation domain with the specified name exists. Returns [code]true[/code] if a translation domain with the specified name exists.
</description> </description>
</method> </method>
<method name="has_translation" qualifiers="const">
<return type="bool" />
<param index="0" name="translation" type="Translation" />
<description>
Returns [code]true[/code] if the main translation domain contains the given [param translation].
</description>
</method>
<method name="has_translation_for_locale" qualifiers="const">
<return type="bool" />
<param index="0" name="locale" type="String" />
<param index="1" name="exact" type="bool" />
<description>
Returns [code]true[/code] if there are any [Translation] instances in the main translation domain that match [param locale] (see [method compare_locales]). If [param exact] is [code]true[/code], only instances whose locale exactly equals [param locale] are considered.
</description>
</method>
<method name="parse_number" qualifiers="const"> <method name="parse_number" qualifiers="const">
<return type="String" /> <return type="String" />
<param index="0" name="number" type="String" /> <param index="0" name="number" type="String" />

View File

@@ -585,10 +585,9 @@ void EditorNode::_update_translations() {
if (main->is_enabled()) { if (main->is_enabled()) {
// Check for the exact locale. // Check for the exact locale.
// `get_potential_translations("zh_CN")` could return translations for "zh". if (main->has_translation_for_locale(main->get_locale_override(), true)) {
if (main->has_translation_for_locale(main->get_locale_override())) {
// The set of translation resources for the current locale changed. // The set of translation resources for the current locale changed.
const HashSet<Ref<Translation>> translations = main->get_potential_translations(main->get_locale_override()); const HashSet<Ref<Translation>> translations = main->find_translations(main->get_locale_override(), false);
if (translations != tracked_translations) { if (translations != tracked_translations) {
_translation_resources_changed(); _translation_resources_changed();
} }
@@ -609,7 +608,7 @@ void EditorNode::_translation_resources_changed() {
const Ref<TranslationDomain> main = TranslationServer::get_singleton()->get_main_domain(); const Ref<TranslationDomain> main = TranslationServer::get_singleton()->get_main_domain();
if (main->is_enabled()) { if (main->is_enabled()) {
const HashSet<Ref<Translation>> translations = main->get_potential_translations(main->get_locale_override()); const HashSet<Ref<Translation>> translations = main->find_translations(main->get_locale_override(), false);
tracked_translations.reserve(translations.size()); tracked_translations.reserve(translations.size());
for (const Ref<Translation> &translation : translations) { for (const Ref<Translation> &translation : translations) {
translation->connect_changed(callable_mp(this, &EditorNode::_queue_translation_notification)); translation->connect_changed(callable_mp(this, &EditorNode::_queue_translation_notification));
@@ -957,7 +956,7 @@ void EditorNode::_notification(int p_what) {
// Remember the selected locale to preview node translations. // Remember the selected locale to preview node translations.
const String preview_locale = EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "preview_locale", String()); const String preview_locale = EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "preview_locale", String());
if (!preview_locale.is_empty() && TranslationServer::get_singleton()->get_loaded_locales().has(preview_locale)) { if (!preview_locale.is_empty() && TranslationServer::get_singleton()->has_translation_for_locale(preview_locale, true)) {
set_preview_locale(preview_locale); set_preview_locale(preview_locale);
} }

View File

@@ -228,37 +228,37 @@ TEST_CASE("[TranslationCSV] CSV import") {
CHECK(result == OK); CHECK(result == OK);
CHECK(gen_files.size() == 4); CHECK(gen_files.size() == 4);
TranslationServer *ts = TranslationServer::get_singleton(); Ref<TranslationDomain> td = TranslationServer::get_singleton()->get_or_add_domain("godot.test");
for (const String &file : gen_files) { for (const String &file : gen_files) {
Ref<Translation> translation = ResourceLoader::load(file); Ref<Translation> translation = ResourceLoader::load(file);
CHECK(translation.is_valid()); CHECK(translation.is_valid());
ts->add_translation(translation); td->add_translation(translation);
} }
ts->set_locale("en"); td->set_locale_override("en");
// `tr` can be called on any Object, we reuse TranslationServer for convenience. CHECK(td->translate("GOOD_MORNING", StringName()) == "Good Morning");
CHECK(ts->tr("GOOD_MORNING") == "Good Morning"); CHECK(td->translate("GOOD_EVENING", StringName()) == "Good Evening");
CHECK(ts->tr("GOOD_EVENING") == "Good Evening");
ts->set_locale("de"); td->set_locale_override("de");
CHECK(ts->tr("GOOD_MORNING") == "Guten Morgen"); CHECK(td->translate("GOOD_MORNING", StringName()) == "Guten Morgen");
CHECK(ts->tr("GOOD_EVENING") == "Good Evening"); // Left blank in CSV, should source from 'en'. CHECK(td->translate("GOOD_EVENING", StringName()) == "Good Evening"); // Left blank in CSV, should source from 'en'.
ts->set_locale("ja"); td->set_locale_override("ja");
CHECK(ts->tr("GOOD_MORNING") == String::utf8("おはよう")); CHECK(td->translate("GOOD_MORNING", StringName()) == String::utf8("おはよう"));
CHECK(ts->tr("GOOD_EVENING") == String::utf8("こんばんは")); CHECK(td->translate("GOOD_EVENING", StringName()) == String::utf8("こんばんは"));
/* FIXME: This passes, but triggers a chain reaction that makes test_viewport /* FIXME: This passes, but triggers a chain reaction that makes test_viewport
* and test_text_edit explode in a billion glittery Unicode particles. * and test_text_edit explode in a billion glittery Unicode particles.
ts->set_locale("fa"); td->set_locale_override("fa");
CHECK(ts->tr("GOOD_MORNING") == String::utf8("صبح بخیر")); CHECK(td->translate("GOOD_MORNING", String()) == String::utf8("صبح بخیر"));
CHECK(ts->tr("GOOD_EVENING") == String::utf8("عصر بخیر")); CHECK(td->translate("GOOD_EVENING", String()) == String::utf8("عصر بخیر"));
*/ */
TranslationServer::get_singleton()->remove_domain("godot.test");
} }
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED

View File

@@ -36,39 +36,55 @@
namespace TestTranslationServer { namespace TestTranslationServer {
TEST_CASE("[TranslationServer] Translation operations") { TEST_CASE("[TranslationServer] Translation operations") {
Ref<TranslationDomain> td = TranslationServer::get_singleton()->get_or_add_domain("godot.test");
CHECK(td->get_translations().is_empty());
Ref<Translation> t1 = memnew(Translation); Ref<Translation> t1 = memnew(Translation);
t1->set_locale("uk"); t1->set_locale("uk"); // Ukrainian.
t1->add_message("Good Morning", String(U"Добрий ранок")); t1->add_message("Good Morning", String(U"Добрий ранок"));
td->add_translation(t1);
CHECK(td->get_translations().size() == 1);
CHECK(td->has_translation_for_locale("uk", true));
CHECK(td->has_translation_for_locale("uk", false));
CHECK_FALSE(td->has_translation_for_locale("uk_UA", true));
CHECK(td->has_translation_for_locale("uk_UA", false));
CHECK(td->find_translations("uk", false).size() == 1);
CHECK(td->find_translations("uk", true).size() == 1);
CHECK(td->find_translations("uk_UA", false).size() == 1);
CHECK(td->find_translations("uk_UA", true).size() == 0);
Ref<Translation> t2 = memnew(Translation); Ref<Translation> t2 = memnew(Translation);
t2->set_locale("uk"); t2->set_locale("uk_UA"); // Ukrainian in Ukraine.
t2->add_message("Hello Godot", String(U"你好戈多")); t2->add_message("Hello Godot", String(U"Привіт, Годо."));
td->add_translation(t2);
CHECK(td->get_translations().size() == 2);
CHECK(td->has_translation_for_locale("uk", true));
CHECK(td->has_translation_for_locale("uk", false));
CHECK(td->has_translation_for_locale("uk_UA", true));
CHECK(td->has_translation_for_locale("uk_UA", false));
CHECK(td->find_translations("uk", false).size() == 2);
CHECK(td->find_translations("uk", true).size() == 1);
CHECK(td->find_translations("uk_UA", false).size() == 2);
CHECK(td->find_translations("uk_UA", true).size() == 1);
TranslationServer *ts = TranslationServer::get_singleton(); td->set_locale_override("uk");
CHECK(td->translate("Good Morning", StringName()) == String::utf8("Добрий ранок"));
// Adds translation for UK locale for the first time. td->remove_translation(t1);
int l_count_before = ts->get_loaded_locales().size(); CHECK(td->get_translations().size() == 1);
ts->add_translation(t1); CHECK_FALSE(td->has_translation_for_locale("uk", true));
int l_count_after = ts->get_loaded_locales().size(); CHECK(td->has_translation_for_locale("uk", false));
CHECK(l_count_after > l_count_before); CHECK(td->has_translation_for_locale("uk_UA", true));
CHECK(td->has_translation_for_locale("uk_UA", false));
CHECK(td->find_translations("uk", true).size() == 0);
CHECK(td->find_translations("uk", false).size() == 1);
CHECK(td->find_translations("uk_UA", true).size() == 1);
CHECK(td->find_translations("uk_UA", false).size() == 1);
// Adds translation for UK locale again.
ts->add_translation(t2);
CHECK_EQ(ts->get_loaded_locales().size(), l_count_after);
// Removing that translation.
ts->remove_translation(t2);
CHECK_EQ(ts->get_loaded_locales().size(), l_count_after);
CHECK(ts->get_translation_object("uk").is_valid());
ts->set_locale("uk");
CHECK(ts->translate("Good Morning") == String::utf8("Добрий ранок"));
ts->remove_translation(t1);
CHECK(ts->get_translation_object("uk").is_null());
// If no suitable Translation object has been found - the original message should be returned. // If no suitable Translation object has been found - the original message should be returned.
CHECK(ts->translate("Good Morning") == "Good Morning"); CHECK(td->translate("Good Morning", StringName()) == "Good Morning");
TranslationServer::get_singleton()->remove_domain("godot.test");
} }
TEST_CASE("[TranslationServer] Locale operations") { TEST_CASE("[TranslationServer] Locale operations") {