summaryrefslogtreecommitdiffstats
path: root/core/string/translation_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/string/translation_server.cpp')
-rw-r--r--core/string/translation_server.cpp486
1 files changed, 79 insertions, 407 deletions
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
index d4aa152340..89b37d0b8a 100644
--- a/core/string/translation_server.cpp
+++ b/core/string/translation_server.cpp
@@ -39,66 +39,6 @@
#include "main/main.h"
#endif
-struct _character_accent_pair {
- const char32_t character;
- const char32_t *accented_character;
-};
-
-static _character_accent_pair _character_to_accented[] = {
- { 'A', U"Å" },
- { 'B', U"ß" },
- { 'C', U"Ç" },
- { 'D', U"Ð" },
- { 'E', U"É" },
- { 'F', U"F́" },
- { 'G', U"Ĝ" },
- { 'H', U"Ĥ" },
- { 'I', U"Ĩ" },
- { 'J', U"Ĵ" },
- { 'K', U"ĸ" },
- { 'L', U"Ł" },
- { 'M', U"Ḿ" },
- { 'N', U"й" },
- { 'O', U"Ö" },
- { 'P', U"Ṕ" },
- { 'Q', U"Q́" },
- { 'R', U"Ř" },
- { 'S', U"Ŝ" },
- { 'T', U"Ŧ" },
- { 'U', U"Ũ" },
- { 'V', U"Ṽ" },
- { 'W', U"Ŵ" },
- { 'X', U"X́" },
- { 'Y', U"Ÿ" },
- { 'Z', U"Ž" },
- { 'a', U"á" },
- { 'b', U"ḅ" },
- { 'c', U"ć" },
- { 'd', U"d́" },
- { 'e', U"é" },
- { 'f', U"f́" },
- { 'g', U"ǵ" },
- { 'h', U"h̀" },
- { 'i', U"í" },
- { 'j', U"ǰ" },
- { 'k', U"ḱ" },
- { 'l', U"ł" },
- { 'm', U"m̀" },
- { 'n', U"ή" },
- { 'o', U"ô" },
- { 'p', U"ṕ" },
- { 'q', U"q́" },
- { 'r', U"ŕ" },
- { 's', U"š" },
- { 't', U"ŧ" },
- { 'u', U"ü" },
- { 'v', U"ṽ" },
- { 'w', U"ŵ" },
- { 'x', U"x́" },
- { 'y', U"ý" },
- { 'z', U"ź" },
-};
-
Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
HashMap<String, String> TranslationServer::language_map;
@@ -404,70 +344,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;
- }
-
- return pseudolocalization_enabled ? pseudolocalize(res) : res;
+ return main_domain->translate(p_message, p_context);
}
StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
@@ -478,51 +384,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 +411,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();
@@ -559,163 +449,68 @@ void TranslationServer::setup() {
}
fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
- pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
- pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
- pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
- pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
- pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
- expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
- pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
- pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
+ main_domain->set_pseudolocalization_enabled(GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false));
+ main_domain->set_pseudolocalization_accents_enabled(GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true));
+ main_domain->set_pseudolocalization_double_vowels_enabled(GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false));
+ main_domain->set_pseudolocalization_fake_bidi_enabled(GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false));
+ main_domain->set_pseudolocalization_override_enabled(GLOBAL_DEF("internationalization/pseudolocalization/override", false));
+ main_domain->set_pseudolocalization_expansion_ratio(GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0));
+ main_domain->set_pseudolocalization_prefix(GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["));
+ main_domain->set_pseudolocalization_suffix(GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"));
+ main_domain->set_pseudolocalization_skip_placeholders_enabled(GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true));
#ifdef TOOLS_ENABLED
ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, ""));
#endif
}
-void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) {
- tool_translation = p_translation;
-}
-
-Ref<Translation> TranslationServer::get_tool_translation() const {
- return tool_translation;
-}
-
String TranslationServer::get_tool_locale() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
- if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) {
- return tool_translation->get_locale();
- } else {
+ const PackedStringArray &locales = editor_domain->get_loaded_locales();
+ if (locales.is_empty()) {
return "en";
}
+ return locales[0];
} else {
#else
{
#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();
}
}
StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
- if (tool_translation.is_valid()) {
- StringName r = tool_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
+ return editor_domain->translate(p_message, p_context);
}
StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (tool_translation.is_valid()) {
- StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) {
- property_translation = p_translation;
+ return editor_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
- if (property_translation.is_valid()) {
- StringName r = property_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
- doc_translation = p_translation;
+ return property_domain->translate(p_message, p_context);
}
StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
- if (doc_translation.is_valid()) {
- StringName r = doc_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
+ return doc_domain->translate(p_message, p_context);
}
StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (doc_translation.is_valid()) {
- StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) {
- extractable_translation = p_translation;
-}
-
-StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const {
- if (extractable_translation.is_valid()) {
- StringName r = extractable_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (extractable_translation.is_valid()) {
- StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
+ return doc_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
}
bool TranslationServer::is_pseudolocalization_enabled() const {
- return pseudolocalization_enabled;
+ return main_domain->is_pseudolocalization_enabled();
}
void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
- pseudolocalization_enabled = p_enabled;
+ main_domain->set_pseudolocalization_enabled(p_enabled);
ResourceLoader::reload_translation_remaps();
@@ -725,14 +520,14 @@ void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
}
void TranslationServer::reload_pseudolocalization() {
- pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
- pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
- pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
- pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
- expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
- pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
- pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
+ main_domain->set_pseudolocalization_accents_enabled(GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"));
+ main_domain->set_pseudolocalization_double_vowels_enabled(GLOBAL_GET("internationalization/pseudolocalization/double_vowels"));
+ main_domain->set_pseudolocalization_fake_bidi_enabled(GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"));
+ main_domain->set_pseudolocalization_override_enabled(GLOBAL_GET("internationalization/pseudolocalization/override"));
+ main_domain->set_pseudolocalization_expansion_ratio(GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"));
+ main_domain->set_pseudolocalization_prefix(GLOBAL_GET("internationalization/pseudolocalization/prefix"));
+ main_domain->set_pseudolocalization_suffix(GLOBAL_GET("internationalization/pseudolocalization/suffix"));
+ main_domain->set_pseudolocalization_skip_placeholders_enabled(GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"));
ResourceLoader::reload_translation_remaps();
@@ -742,138 +537,7 @@ void TranslationServer::reload_pseudolocalization() {
}
StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- int length = message.length();
- if (pseudolocalization_override_enabled) {
- message = get_override_string(message);
- }
-
- if (pseudolocalization_double_vowels_enabled) {
- message = double_vowels(message);
- }
-
- if (pseudolocalization_accents_enabled) {
- message = replace_with_accented_string(message);
- }
-
- if (pseudolocalization_fake_bidi_enabled) {
- message = wrap_with_fakebidi_characters(message);
- }
-
- StringName res = add_padding(message, length);
- return res;
-}
-
-StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- message = double_vowels(message);
- message = replace_with_accented_string(message);
- StringName res = "[!!! " + message + " !!!]";
- return res;
-}
-
-String TranslationServer::get_override_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += '*';
- }
- return res;
-}
-
-String TranslationServer::double_vowels(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += p_message[i];
- if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
- p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
- res += p_message[i];
- }
- }
- return res;
-};
-
-String TranslationServer::replace_with_accented_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- const char32_t *accented = get_accented_version(p_message[i]);
- if (accented) {
- res += accented;
- } else {
- res += p_message[i];
- }
- }
- return res;
-}
-
-String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
- String res;
- char32_t fakebidiprefix = U'\u202e';
- char32_t fakebidisuffix = U'\u202c';
- res += fakebidiprefix;
- // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
- for (int i = 0; i < p_message.length(); i++) {
- if (p_message[i] == '\n') {
- res += fakebidisuffix;
- res += p_message[i];
- res += fakebidiprefix;
- } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += fakebidisuffix;
- res += p_message[i];
- res += p_message[i + 1];
- res += fakebidiprefix;
- i++;
- } else {
- res += p_message[i];
- }
- }
- res += fakebidisuffix;
- return res;
-}
-
-String TranslationServer::add_padding(const String &p_message, int p_length) const {
- String underscores = String("_").repeat(p_length * expansion_ratio / 2);
- String prefix = pseudolocalization_prefix + underscores;
- String suffix = underscores + pseudolocalization_suffix;
-
- return prefix + p_message + suffix;
-}
-
-const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
- if (!is_ascii_alphabet_char(p_character)) {
- return nullptr;
- }
-
- for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
- if (_character_to_accented[i].character == p_character) {
- return _character_to_accented[i].accented_character;
- }
- }
-
- return nullptr;
-}
-
-bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
- return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
- (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
- p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
+ return main_domain->pseudolocalize(p_message);
}
#ifdef TOOLS_ENABLED
@@ -925,6 +589,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 +615,9 @@ void TranslationServer::load_translations() {
TranslationServer::TranslationServer() {
singleton = this;
+ main_domain.instantiate();
+ editor_domain = get_or_add_domain("godot.editor");
+ property_domain = get_or_add_domain("godot.properties");
+ doc_domain = get_or_add_domain("godot.documentation");
init_locale_info();
}