Make editor use translation domains

How editor plugins use this feature:
1. Pick a unique translation domain name.
2. `_enter_tree()`: load translations into that translation domain.
3. Call `set_translation_domain()` for its root UI node.
4. `_exit_tree()`: remove that translation domain.

Plugins can also set the translation domain to `godot.editor` for
nested nodes that should use editor translations. `EditorFileDialog`
automatically does this.
This commit is contained in:
Haoyu Qiu 2024-08-16 22:19:14 +08:00
parent c5d147b9b5
commit 818acb4290
10 changed files with 38 additions and 125 deletions

View File

@ -1540,15 +1540,6 @@ String Object::tr(const StringName &p_message, const StringName &p_context) cons
return p_message; return p_message;
} }
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
String tr_msg = TranslationServer::get_singleton()->extractable_translate(p_message, p_context);
if (!tr_msg.is_empty() && tr_msg != p_message) {
return tr_msg;
}
return TranslationServer::get_singleton()->tool_translate(p_message, p_context);
}
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain()); const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain());
return domain->translate(p_message, p_context); return domain->translate(p_message, p_context);
} }
@ -1562,15 +1553,6 @@ String Object::tr_n(const StringName &p_message, const StringName &p_message_plu
return p_message_plural; return p_message_plural;
} }
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
String tr_msg = TranslationServer::get_singleton()->extractable_translate_plural(p_message, p_message_plural, p_n, p_context);
if (!tr_msg.is_empty() && tr_msg != p_message && tr_msg != p_message_plural) {
return tr_msg;
}
return TranslationServer::get_singleton()->tool_translate_plural(p_message, p_message_plural, p_n, p_context);
}
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain()); const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain());
return domain->translate_plural(p_message, p_message_plural, p_n, p_context); return domain->translate_plural(p_message, p_message_plural, p_n, p_context);
} }

View File

@ -525,22 +525,14 @@ void TranslationServer::setup() {
#endif #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() { 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 (TranslationServer::get_singleton()->get_tool_translation().is_valid()) { const PackedStringArray &locales = editor_domain->get_loaded_locales();
return tool_translation->get_locale(); if (locales.is_empty()) {
} else {
return "en"; return "en";
} }
return locales[0];
} else { } else {
#else #else
{ {
@ -555,97 +547,23 @@ String TranslationServer::get_tool_locale() {
} }
StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const { StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
if (tool_translation.is_valid()) { return editor_domain->translate(p_message, p_context);
StringName r = tool_translation->get_message(p_message, p_context);
if (r) {
return r;
}
}
return p_message;
} }
StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { 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()) { return editor_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
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;
} }
StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const { StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
if (property_translation.is_valid()) { return property_domain->translate(p_message, p_context);
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;
} }
StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const { StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
if (doc_translation.is_valid()) { return doc_domain->translate(p_message, p_context);
StringName r = doc_translation->get_message(p_message, p_context);
if (r) {
return r;
}
}
return p_message;
} }
StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { 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()) { return doc_domain->translate_plural(p_message, p_message_plural, p_n, p_context);
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;
} }
bool TranslationServer::is_pseudolocalization_enabled() const { bool TranslationServer::is_pseudolocalization_enabled() const {
@ -890,5 +808,8 @@ void TranslationServer::load_translations() {
TranslationServer::TranslationServer() { TranslationServer::TranslationServer() {
singleton = this; singleton = this;
main_domain.instantiate(); 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(); init_locale_info();
} }

View File

@ -41,13 +41,11 @@ class TranslationServer : public Object {
String fallback; String fallback;
Ref<TranslationDomain> main_domain; Ref<TranslationDomain> main_domain;
Ref<TranslationDomain> editor_domain;
Ref<TranslationDomain> property_domain;
Ref<TranslationDomain> doc_domain;
HashMap<StringName, Ref<TranslationDomain>> custom_domains; HashMap<StringName, Ref<TranslationDomain>> custom_domains;
Ref<Translation> tool_translation;
Ref<Translation> property_translation;
Ref<Translation> doc_translation;
Ref<Translation> extractable_translation;
bool enabled = true; bool enabled = true;
bool pseudolocalization_enabled = false; bool pseudolocalization_enabled = false;
@ -133,18 +131,11 @@ public:
int compare_locales(const String &p_locale_a, const String &p_locale_b) const; int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
String get_tool_locale(); String get_tool_locale();
void set_tool_translation(const Ref<Translation> &p_translation);
Ref<Translation> get_tool_translation() const;
StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const; StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_property_translation(const Ref<Translation> &p_translation);
StringName property_translate(const StringName &p_message, const StringName &p_context = "") const; StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
void set_doc_translation(const Ref<Translation> &p_translation);
StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const; StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_extractable_translation(const Ref<Translation> &p_translation);
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; bool has_domain(const StringName &p_domain) const;
Ref<TranslationDomain> get_or_add_domain(const StringName &p_domain); Ref<TranslationDomain> get_or_add_domain(const StringName &p_domain);

View File

@ -5,6 +5,7 @@
</brief_description> </brief_description>
<description> <description>
[TranslationDomain] is a self-contained collection of [Translation] resources. Translations can be added to or removed from it. [TranslationDomain] is a self-contained collection of [Translation] resources. Translations can be added to or removed from it.
If you're working with the main translation domain, it is more convenient to use the wrap methods on [TranslationServer].
</description> </description>
<tutorials> <tutorials>
</tutorials> </tutorials>

View File

@ -5,7 +5,7 @@
</brief_description> </brief_description>
<description> <description>
The translation server is the API backend that manages all language translations. The translation server is the API backend that manages all language translations.
Translations are stored in [TranslationDomain]s, which can be accessed by name. The most commonly used translation domain is the main translation domain, which always exists and can be accessed using an empty [StringName]. The translation server provides wrapper methods for accessing the master translation domain directly, without having to fetch the main translation domain first. Translations are stored in [TranslationDomain]s, which can be accessed by name. The most commonly used translation domain is the main translation domain. It always exists and can be accessed using an empty [StringName]. The translation server provides wrapper methods for accessing the main translation domain directly, without having to fetch the translation domain first. Custom translation domains are mainly for advanced usages like editor plugins. Names starting with [code]godot.[/code] are reserved for engine internals.
</description> </description>
<tutorials> <tutorials>
<link title="Internationalizing games">$DOCS_URL/tutorials/i18n/internationalizing_games.html</link> <link title="Internationalizing games">$DOCS_URL/tutorials/i18n/internationalizing_games.html</link>

View File

@ -6557,6 +6557,8 @@ EditorNode::EditorNode() {
DEV_ASSERT(!singleton); DEV_ASSERT(!singleton);
singleton = this; singleton = this;
set_translation_domain("godot.editor");
Resource::_get_local_scene_func = _resource_get_edited_scene; Resource::_get_local_scene_func = _resource_get_edited_scene;
{ {

View File

@ -1210,6 +1210,8 @@ fail:
void EditorSettings::setup_language() { void EditorSettings::setup_language() {
String lang = get("interface/editor/editor_language"); String lang = get("interface/editor/editor_language");
TranslationServer::get_singleton()->set_locale(lang);
if (lang == "en") { if (lang == "en") {
return; // Default, nothing to do. return; // Default, nothing to do.
} }

View File

@ -54,6 +54,8 @@ Vector<String> get_editor_locales() {
} }
void load_editor_translations(const String &p_locale) { void load_editor_translations(const String &p_locale) {
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.editor");
EditorTranslationList *etl = _editor_translations; EditorTranslationList *etl = _editor_translations;
while (etl->data) { while (etl->data) {
if (etl->lang == p_locale) { if (etl->lang == p_locale) {
@ -70,7 +72,7 @@ void load_editor_translations(const String &p_locale) {
if (tr.is_valid()) { if (tr.is_valid()) {
tr->set_locale(etl->lang); tr->set_locale(etl->lang);
TranslationServer::get_singleton()->set_tool_translation(tr); domain->add_translation(tr);
break; break;
} }
} }
@ -80,6 +82,8 @@ void load_editor_translations(const String &p_locale) {
} }
void load_property_translations(const String &p_locale) { void load_property_translations(const String &p_locale) {
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.properties");
PropertyTranslationList *etl = _property_translations; PropertyTranslationList *etl = _property_translations;
while (etl->data) { while (etl->data) {
if (etl->lang == p_locale) { if (etl->lang == p_locale) {
@ -96,7 +100,7 @@ void load_property_translations(const String &p_locale) {
if (tr.is_valid()) { if (tr.is_valid()) {
tr->set_locale(etl->lang); tr->set_locale(etl->lang);
TranslationServer::get_singleton()->set_property_translation(tr); domain->add_translation(tr);
break; break;
} }
} }
@ -106,6 +110,8 @@ void load_property_translations(const String &p_locale) {
} }
void load_doc_translations(const String &p_locale) { void load_doc_translations(const String &p_locale) {
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.documentation");
DocTranslationList *dtl = _doc_translations; DocTranslationList *dtl = _doc_translations;
while (dtl->data) { while (dtl->data) {
if (dtl->lang == p_locale) { if (dtl->lang == p_locale) {
@ -122,7 +128,7 @@ void load_doc_translations(const String &p_locale) {
if (tr.is_valid()) { if (tr.is_valid()) {
tr->set_locale(dtl->lang); tr->set_locale(dtl->lang);
TranslationServer::get_singleton()->set_doc_translation(tr); domain->add_translation(tr);
break; break;
} }
} }
@ -132,6 +138,8 @@ void load_doc_translations(const String &p_locale) {
} }
void load_extractable_translations(const String &p_locale) { void load_extractable_translations(const String &p_locale) {
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.editor");
ExtractableTranslationList *etl = _extractable_translations; ExtractableTranslationList *etl = _extractable_translations;
while (etl->data) { while (etl->data) {
if (etl->lang == p_locale) { if (etl->lang == p_locale) {
@ -148,7 +156,7 @@ void load_extractable_translations(const String &p_locale) {
if (tr.is_valid()) { if (tr.is_valid()) {
tr->set_locale(etl->lang); tr->set_locale(etl->lang);
TranslationServer::get_singleton()->set_extractable_translation(tr); domain->add_translation(tr);
break; break;
} }
} }

View File

@ -207,6 +207,10 @@ void EditorFileDialog::_update_theme_item_cache() {
void EditorFileDialog::_notification(int p_what) { void EditorFileDialog::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
set_translation_domain(SNAME("godot.editor"));
} break;
case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_THEME_CHANGED:
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: { case NOTIFICATION_TRANSLATION_CHANGED: {

View File

@ -1081,6 +1081,8 @@ void ProjectManager::_titlebar_resized() {
ProjectManager::ProjectManager() { ProjectManager::ProjectManager() {
singleton = this; singleton = this;
set_translation_domain("godot.editor");
// Turn off some servers we aren't going to be using in the Project Manager. // Turn off some servers we aren't going to be using in the Project Manager.
NavigationServer3D::get_singleton()->set_active(false); NavigationServer3D::get_singleton()->set_active(false);
PhysicsServer3D::get_singleton()->set_active(false); PhysicsServer3D::get_singleton()->set_active(false);