summaryrefslogtreecommitdiffstats
path: root/core/string
diff options
context:
space:
mode:
authorHaoyu Qiu <timothyqiu32@gmail.com>2024-08-15 09:14:41 +0800
committerHaoyu Qiu <timothyqiu32@gmail.com>2024-09-17 13:09:44 +0800
commit68d494e24e2d6704ae95b1d00fa91b440311e8c3 (patch)
tree0216a77378d717873d4fbf7c71f405e0cc02081b /core/string
parent48403b5358c11ffff702da82c48464db8c536ee3 (diff)
downloadredot-engine-68d494e24e2d6704ae95b1d00fa91b440311e8c3.tar.gz
Add translation domain
Diffstat (limited to 'core/string')
-rw-r--r--core/string/translation_domain.cpp165
-rw-r--r--core/string/translation_domain.h65
-rw-r--r--core/string/translation_server.cpp153
-rw-r--r--core/string/translation_server.h12
4 files changed, 287 insertions, 108 deletions
diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp
new file mode 100644
index 0000000000..b44eb40366
--- /dev/null
+++ b/core/string/translation_domain.cpp
@@ -0,0 +1,165 @@
+/**************************************************************************/
+/* translation_domain.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "translation_domain.h"
+
+#include "core/string/translation.h"
+#include "core/string/translation_server.h"
+
+StringName TranslationDomain::get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const {
+ StringName res;
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ ERR_CONTINUE(E.is_null());
+ int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
+ if (score > 0 && score >= best_score) {
+ const StringName r = E->get_message(p_message, p_context);
+ if (!r) {
+ continue;
+ }
+ res = r;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+
+ return res;
+}
+
+StringName TranslationDomain::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 res;
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ ERR_CONTINUE(E.is_null());
+ int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
+ if (score > 0 && score >= best_score) {
+ const StringName r = E->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (!r) {
+ continue;
+ }
+ res = r;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+
+ return res;
+}
+
+PackedStringArray TranslationDomain::get_loaded_locales() const {
+ PackedStringArray locales;
+ for (const Ref<Translation> &E : translations) {
+ ERR_CONTINUE(E.is_null());
+ locales.push_back(E->get_locale());
+ }
+ return locales;
+}
+
+Ref<Translation> TranslationDomain::get_translation_object(const String &p_locale) const {
+ Ref<Translation> res;
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ ERR_CONTINUE(E.is_null());
+
+ int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
+ if (score > 0 && score >= best_score) {
+ res = E;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+ return res;
+}
+
+void TranslationDomain::add_translation(const Ref<Translation> &p_translation) {
+ translations.insert(p_translation);
+}
+
+void TranslationDomain::remove_translation(const Ref<Translation> &p_translation) {
+ translations.erase(p_translation);
+}
+
+void TranslationDomain::clear() {
+ translations.clear();
+}
+
+StringName TranslationDomain::translate(const StringName &p_message, const StringName &p_context) const {
+ const String &locale = TranslationServer::get_singleton()->get_locale();
+ StringName res = get_message_from_translations(locale, p_message, p_context);
+
+ const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
+ if (!res && fallback.length() >= 2) {
+ res = get_message_from_translations(fallback, p_message, p_context);
+ }
+
+ if (!res) {
+ return p_message;
+ }
+ return res;
+}
+
+StringName TranslationDomain::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ const String &locale = TranslationServer::get_singleton()->get_locale();
+ StringName res = get_message_from_translations(locale, p_message, p_message_plural, p_n, p_context);
+
+ const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
+ if (!res && fallback.length() >= 2) {
+ res = get_message_from_translations(fallback, p_message, p_message_plural, p_n, p_context);
+ }
+
+ if (!res) {
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+ return res;
+}
+
+void TranslationDomain::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationDomain::get_translation_object);
+ 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("clear"), &TranslationDomain::clear);
+ 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()));
+}
+
+TranslationDomain::TranslationDomain() {
+}
diff --git a/core/string/translation_domain.h b/core/string/translation_domain.h
new file mode 100644
index 0000000000..6139967217
--- /dev/null
+++ b/core/string/translation_domain.h
@@ -0,0 +1,65 @@
+/**************************************************************************/
+/* translation_domain.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TRANSLATION_DOMAIN_H
+#define TRANSLATION_DOMAIN_H
+
+#include "core/object/ref_counted.h"
+
+class Translation;
+
+class TranslationDomain : public RefCounted {
+ GDCLASS(TranslationDomain, RefCounted);
+
+ HashSet<Ref<Translation>> translations;
+
+protected:
+ static void _bind_methods();
+
+public:
+ // Methods in this section are not intended for scripting.
+ 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;
+ PackedStringArray get_loaded_locales() const;
+
+public:
+ Ref<Translation> get_translation_object(const String &p_locale) const;
+
+ void add_translation(const Ref<Translation> &p_translation);
+ void remove_translation(const Ref<Translation> &p_translation);
+ void clear();
+
+ 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;
+
+ TranslationDomain();
+};
+
+#endif // TRANSLATION_DOMAIN_H
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
index d4aa152340..ae822b43fb 100644
--- a/core/string/translation_server.cpp
+++ b/core/string/translation_server.cpp
@@ -404,69 +404,36 @@ String TranslationServer::get_locale() const {
return locale;
}
-PackedStringArray TranslationServer::get_loaded_locales() const {
- PackedStringArray locales;
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), PackedStringArray());
- String l = t->get_locale();
-
- locales.push_back(l);
- }
+String TranslationServer::get_fallback_locale() const {
+ return fallback;
+}
- return locales;
+PackedStringArray TranslationServer::get_loaded_locales() const {
+ return main_domain->get_loaded_locales();
}
void TranslationServer::add_translation(const Ref<Translation> &p_translation) {
- translations.insert(p_translation);
+ main_domain->add_translation(p_translation);
}
void TranslationServer::remove_translation(const Ref<Translation> &p_translation) {
- translations.erase(p_translation);
+ main_domain->remove_translation(p_translation);
}
Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
- Ref<Translation> res;
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), nullptr);
- String l = t->get_locale();
-
- int score = compare_locales(p_locale, l);
- if (score > 0 && score >= best_score) {
- res = t;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
- return res;
+ return main_domain->get_translation_object(p_locale);
}
void TranslationServer::clear() {
- translations.clear();
+ main_domain->clear();
}
StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
- // Match given message against the translation catalog for the project locale.
-
if (!enabled) {
return p_message;
}
- StringName res = _get_message_from_translations(p_message, p_context, locale, false);
-
- if (!res && fallback.length() >= 2) {
- res = _get_message_from_translations(p_message, p_context, fallback, false);
- }
-
- if (!res) {
- return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
- }
-
+ const StringName res = main_domain->translate(p_message, p_context);
return pseudolocalization_enabled ? pseudolocalize(res) : res;
}
@@ -478,51 +445,7 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons
return p_message_plural;
}
- StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
-
- if (!res && fallback.length() >= 2) {
- res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
- }
-
- if (!res) {
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
- }
-
- return res;
-}
-
-StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
- StringName res;
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), p_message);
- String l = t->get_locale();
-
- int score = compare_locales(p_locale, l);
- if (score > 0 && score >= best_score) {
- StringName r;
- if (!plural) {
- r = t->get_message(p_message, p_context);
- } else {
- r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
- }
- if (!r) {
- continue;
- }
- res = r;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
-
- return res;
+ return main_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
TranslationServer *TranslationServer::singleton = nullptr;
@@ -549,6 +472,34 @@ bool TranslationServer::_load_translations(const String &p_from) {
return false;
}
+bool TranslationServer::has_domain(const StringName &p_domain) const {
+ if (p_domain == StringName()) {
+ return true;
+ }
+ return custom_domains.has(p_domain);
+}
+
+Ref<TranslationDomain> TranslationServer::get_or_add_domain(const StringName &p_domain) {
+ if (p_domain == StringName()) {
+ return main_domain;
+ }
+ const Ref<TranslationDomain> *domain = custom_domains.getptr(p_domain);
+ if (domain) {
+ if (domain->is_valid()) {
+ return *domain;
+ }
+ ERR_PRINT("Bug (please report): Found invalid translation domain.");
+ }
+ Ref<TranslationDomain> new_domain = memnew(TranslationDomain);
+ custom_domains[p_domain] = new_domain;
+ return new_domain;
+}
+
+void TranslationServer::remove_domain(const StringName &p_domain) {
+ ERR_FAIL_COND_MSG(p_domain == StringName(), "Cannot remove main translation domain.");
+ custom_domains.erase(p_domain);
+}
+
void TranslationServer::setup() {
String test = GLOBAL_DEF("internationalization/locale/test", "");
test = test.strip_edges();
@@ -595,24 +546,11 @@ String TranslationServer::get_tool_locale() {
{
#endif
// Look for best matching loaded translation.
- String best_locale = "en";
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), best_locale);
- String l = t->get_locale();
-
- int score = compare_locales(locale, l);
- if (score > 0 && score >= best_score) {
- best_locale = l;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
+ Ref<Translation> t = main_domain->get_translation_object(locale);
+ if (t.is_null()) {
+ return "en";
}
- return best_locale;
+ return t->get_locale();
}
}
@@ -925,6 +863,10 @@ void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
+ 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("remove_domain", "domain"), &TranslationServer::remove_domain);
+
ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
@@ -947,5 +889,6 @@ void TranslationServer::load_translations() {
TranslationServer::TranslationServer() {
singleton = this;
+ main_domain.instantiate();
init_locale_info();
}
diff --git a/core/string/translation_server.h b/core/string/translation_server.h
index bb285ab19c..1271abec99 100644
--- a/core/string/translation_server.h
+++ b/core/string/translation_server.h
@@ -32,6 +32,7 @@
#define TRANSLATION_SERVER_H
#include "core/string/translation.h"
+#include "core/string/translation_domain.h"
class TranslationServer : public Object {
GDCLASS(TranslationServer, Object);
@@ -39,7 +40,9 @@ class TranslationServer : public Object {
String locale = "en";
String fallback;
- HashSet<Ref<Translation>> translations;
+ Ref<TranslationDomain> main_domain;
+ HashMap<StringName, Ref<TranslationDomain>> custom_domains;
+
Ref<Translation> tool_translation;
Ref<Translation> property_translation;
Ref<Translation> doc_translation;
@@ -70,8 +73,6 @@ class TranslationServer : public Object {
bool _load_translations(const String &p_from);
String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
- StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
-
static void _bind_methods();
struct LocaleScriptInfo {
@@ -99,6 +100,7 @@ public:
void set_locale(const String &p_locale);
String get_locale() const;
+ String get_fallback_locale() const;
Ref<Translation> get_translation_object(const String &p_locale);
Vector<String> get_all_languages() const;
@@ -144,6 +146,10 @@ public:
StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+ bool has_domain(const StringName &p_domain) const;
+ Ref<TranslationDomain> get_or_add_domain(const StringName &p_domain);
+ void remove_domain(const StringName &p_domain);
+
void setup();
void clear();