From 3181ac36666e1292e6f88fb05ac0ea34333d929f Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Fri, 13 Sep 2024 00:50:09 +0800 Subject: [PATCH] Add translation preview in editor --- core/string/translation_domain.cpp | 15 ++++++-- core/string/translation_domain.h | 4 +++ core/string/translation_server.h | 1 + doc/classes/TranslationDomain.xml | 16 +++++++++ editor/editor_node.cpp | 23 ++++++++++++ editor/editor_node.h | 3 ++ editor/editor_settings.cpp | 3 ++ editor/plugins/canvas_item_editor_plugin.cpp | 38 ++++++++++++++++++++ editor/plugins/canvas_item_editor_plugin.h | 4 +++ scene/main/node.cpp | 7 ---- 10 files changed, 105 insertions(+), 9 deletions(-) diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp index a22e97a07bba..24c3ca308366 100644 --- a/core/string/translation_domain.cpp +++ b/core/string/translation_domain.cpp @@ -284,7 +284,7 @@ void TranslationDomain::clear() { } StringName TranslationDomain::translate(const StringName &p_message, const StringName &p_context) const { - const String &locale = TranslationServer::get_singleton()->get_locale(); + const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override; StringName res = get_message_from_translations(locale, p_message, p_context); const String &fallback = TranslationServer::get_singleton()->get_fallback_locale(); @@ -299,7 +299,7 @@ StringName TranslationDomain::translate(const StringName &p_message, const Strin } 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(); + const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override; 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(); @@ -316,6 +316,15 @@ StringName TranslationDomain::translate_plural(const StringName &p_message, cons return res; } +String TranslationDomain::get_locale_override() const { + return locale_override; +} + +void TranslationDomain::set_locale_override(const String &p_locale) { + String new_locale = p_locale.is_empty() ? p_locale : TranslationServer::get_singleton()->standardize_locale(p_locale); + locale_override = new_locale; +} + bool TranslationDomain::is_pseudolocalization_enabled() const { return pseudolocalization.enabled; } @@ -417,6 +426,8 @@ void TranslationDomain::_bind_methods() { 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())); + ClassDB::bind_method(D_METHOD("get_locale_override"), &TranslationDomain::get_locale_override); + ClassDB::bind_method(D_METHOD("set_locale_override", "locale"), &TranslationDomain::set_locale_override); ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationDomain::is_pseudolocalization_enabled); ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_enabled); diff --git a/core/string/translation_domain.h b/core/string/translation_domain.h index 9d0e7d4dae66..20ceecc63ea0 100644 --- a/core/string/translation_domain.h +++ b/core/string/translation_domain.h @@ -50,6 +50,7 @@ class TranslationDomain : public RefCounted { String suffix = "]"; }; + String locale_override; HashSet> translations; PseudolocalizationConfig pseudolocalization; @@ -80,6 +81,9 @@ class TranslationDomain : public RefCounted { 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; + String get_locale_override() const; + void set_locale_override(const String &p_locale); + bool is_pseudolocalization_enabled() const; void set_pseudolocalization_enabled(bool p_enabled); bool is_pseudolocalization_accents_enabled() const; diff --git a/core/string/translation_server.h b/core/string/translation_server.h index a09230c0192d..d4d0caed9697 100644 --- a/core/string/translation_server.h +++ b/core/string/translation_server.h @@ -74,6 +74,7 @@ class TranslationServer : public Object { public: _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; } + Ref get_main_domain() const { return main_domain; } Ref get_editor_domain() const { return editor_domain; } void set_enabled(bool p_enabled) { enabled = p_enabled; } diff --git a/doc/classes/TranslationDomain.xml b/doc/classes/TranslationDomain.xml index 00047cb28413..fef27c25201f 100644 --- a/doc/classes/TranslationDomain.xml +++ b/doc/classes/TranslationDomain.xml @@ -6,6 +6,7 @@ [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]. + Because translation domain is intended to be advanced usage, modifications to it do not emit [constant MainLoop.NOTIFICATION_TRANSLATION_CHANGED] on the scene tree automatically. See the description of methods and properties below for details. @@ -23,6 +24,12 @@ Removes all translations. + + + + Returns the locale override of the domain. Returns an empty string if locale override is disabled. + + @@ -44,6 +51,15 @@ Removes the given translation. + + + + + Sets the locale override of the domain. + If [param locale] is an empty string, locale override is disabled. Otherwise, [param locale] will be standardized to match known locales (e.g. [code]en-US[/code] would be matched to [code]en_US[/code]). + [b]Note: [/b] Updating this property does not automatically update texts in the scene tree. Please propagate the [constant MainLoop.NOTIFICATION_TRANSLATION_CHANGED] signal manually. + + diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 770bf5726dce..0e0657f2bc53 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -466,6 +466,10 @@ void EditorNode::_update_from_settings() { ResourceImporterTexture::get_singleton()->update_imports(); + TranslationServer::get_singleton()->clear(); + TranslationServer::get_singleton()->load_translations(); + scene_root->propagate_notification(NOTIFICATION_TRANSLATION_CHANGED); + #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/edge_connection_color")); NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/geometry_edge_color")); @@ -3673,6 +3677,21 @@ void EditorNode::set_edited_scene_root(Node *p_scene, bool p_auto_add) { } } +String EditorNode::get_preview_locale() const { + const String override = TranslationServer::get_singleton()->get_main_domain()->get_locale_override(); + // Override should always be non-empty, but just in case. + return override.is_empty() ? TranslationServer::get_singleton()->get_locale() : override; +} + +void EditorNode::set_preview_locale(const String &p_locale) { + Ref main_domain = TranslationServer::get_singleton()->get_main_domain(); + if (main_domain->get_locale_override() == p_locale) { + return; + } + TranslationServer::get_singleton()->get_main_domain()->set_locale_override(p_locale); + scene_root->propagate_notification(NOTIFICATION_TRANSLATION_CHANGED); +} + Dictionary EditorNode::_get_main_scene_state() { Dictionary state; state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value(); @@ -6459,6 +6478,9 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene); + ClassDB::bind_method("get_preview_locale", &EditorNode::get_preview_locale); + ClassDB::bind_method(D_METHOD("set_preview_locale", "locale"), &EditorNode::set_preview_locale); + ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process); ADD_SIGNAL(MethodInfo("request_help_search")); @@ -6996,6 +7018,7 @@ EditorNode::EditorNode() { editor_main_screen->set_v_size_flags(Control::SIZE_EXPAND_FILL); scene_root = memnew(SubViewport); + scene_root->set_translation_domain(StringName()); scene_root->set_embedding_subwindows(true); scene_root->set_disable_3d(true); scene_root->set_disable_input(true); diff --git a/editor/editor_node.h b/editor/editor_node.h index 4127dd1539db..7b06a7ef1049 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -769,6 +769,9 @@ class EditorNode : public Node { void set_edited_scene_root(Node *p_scene, bool p_auto_add); Node *get_edited_scene() { return editor_data.get_edited_scene_root(); } + String get_preview_locale() const; + void set_preview_locale(const String &p_locale); + void fix_dependencies(const String &p_for_file); int new_scene(); Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 2bc14b3410eb..efa5976e8dd2 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -1212,6 +1212,9 @@ void EditorSettings::setup_language() { String lang = get("interface/editor/editor_language"); TranslationServer::get_singleton()->set_locale(lang); + // Used by edited scene. + TranslationServer::get_singleton()->get_main_domain()->set_locale_override("en"); + if (lang == "en") { return; // Default, nothing to do. } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index e9a796dae723..739ba089aacf 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/input/input.h" #include "core/os/keyboard.h" +#include "core/string/translation_server.h" #include "editor/debugger/editor_debugger_node.h" #include "editor/editor_main_screen.h" #include "editor/editor_node.h" @@ -63,6 +64,7 @@ #include "scene/main/window.h" #include "scene/resources/packed_scene.h" #include "scene/resources/style_box_texture.h" +#include "scene/scene_string_names.h" #define RULER_WIDTH (15 * EDSCALE) #define DRAG_THRESHOLD (8 * EDSCALE) @@ -1062,6 +1064,36 @@ void CanvasItemEditor::_switch_theme_preview(int p_mode) { EditorNode::get_singleton()->update_preview_themes(theme_preview); } +void CanvasItemEditor::_prepare_translation_menu() { + translation_menu->clear(); + translation_menu->add_radio_check_item(TTR("None")); + translation_menu->set_item_metadata(-1, "en"); + + Vector locales = TranslationServer::get_singleton()->get_loaded_locales(); + + const String current_preview_locale = EditorNode::get_singleton()->get_preview_locale(); + if (!locales.is_empty()) { + translation_menu->add_separator(); + } + bool using_default = true; + for (const String &locale : locales) { + translation_menu->add_radio_check_item(locale); + translation_menu->set_item_metadata(-1, locale); + if (locale == current_preview_locale) { + translation_menu->set_item_checked(-1, true); + using_default = false; + } + } + if (using_default) { + translation_menu->set_item_checked(0, true); + } +} + +void CanvasItemEditor::_switch_translation_preview(int p_translation) { + view_menu->get_popup()->hide(); + EditorNode::get_singleton()->set_preview_locale(translation_menu->get_item_metadata(p_translation)); +} + bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref &p_event) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); Ref b = p_event; @@ -5582,6 +5614,12 @@ CanvasItemEditor::CanvasItemEditor() { theme_menu->set_item_checked(i, i == theme_preview); } + translation_menu = memnew(PopupMenu); + translation_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + translation_menu->connect("about_to_popup", callable_mp(this, &CanvasItemEditor::_prepare_translation_menu)); + translation_menu->connect("index_pressed", callable_mp(this, &CanvasItemEditor::_switch_translation_preview)); + p->add_submenu_node_item(TTR("Preview Translation"), translation_menu); + main_menu_hbox->add_child(memnew(VSeparator)); // Contextual toolbars. diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index bae9efebc91e..2dfbbceead56 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -339,6 +339,7 @@ class CanvasItemEditor : public VBoxContainer { MenuButton *view_menu = nullptr; PopupMenu *grid_menu = nullptr; PopupMenu *theme_menu = nullptr; + PopupMenu *translation_menu = nullptr; PopupMenu *gizmos_menu = nullptr; HBoxContainer *animation_hb = nullptr; MenuButton *animation_menu = nullptr; @@ -432,6 +433,9 @@ class CanvasItemEditor : public VBoxContainer { ThemePreviewMode theme_preview = THEME_PREVIEW_PROJECT; void _switch_theme_preview(int p_mode); + void _prepare_translation_menu(); + void _switch_translation_preview(int p_translation); + List _get_edited_canvas_items(bool p_retrieve_locked = false, bool p_remove_canvas_item_if_parent_in_selection = true, bool *r_has_locked_items = nullptr) const; Rect2 _get_encompassing_rect_from_list(const List &p_list); void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index eb3448e1a2ae..a5db43e7dea8 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -113,13 +113,6 @@ void Node::_notification(int p_notification) { data.is_auto_translate_dirty = true; data.is_translation_domain_dirty = true; -#ifdef TOOLS_ENABLED - // Don't translate UI elements when they're being edited. - if (is_part_of_edited_scene()) { - set_message_translation(false); - } -#endif - if (data.auto_translate_mode != AUTO_TRANSLATE_MODE_DISABLED) { notification(NOTIFICATION_TRANSLATION_CHANGED); }