diff --git a/core/object/object.cpp b/core/object/object.cpp index da3ca6bc61f..faf109a13c4 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1527,6 +1527,14 @@ void Object::initialize_class() { initialized = true; } +StringName Object::get_translation_domain() const { + return _translation_domain; +} + +void Object::set_translation_domain(const StringName &p_domain) { + _translation_domain = p_domain; +} + String Object::tr(const StringName &p_message, const StringName &p_context) const { if (!_can_translate || !TranslationServer::get_singleton()) { return p_message; @@ -1541,7 +1549,8 @@ String Object::tr(const StringName &p_message, const StringName &p_context) cons return TranslationServer::get_singleton()->tool_translate(p_message, p_context); } - return TranslationServer::get_singleton()->translate(p_message, p_context); + const Ref domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain()); + return domain->translate(p_message, p_context); } String Object::tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1562,7 +1571,8 @@ String Object::tr_n(const StringName &p_message, const StringName &p_message_plu return TranslationServer::get_singleton()->tool_translate_plural(p_message, p_message_plural, p_n, p_context); } - return TranslationServer::get_singleton()->translate_plural(p_message, p_message_plural, p_n, p_context); + const Ref domain = TranslationServer::get_singleton()->get_or_add_domain(get_translation_domain()); + return domain->translate_plural(p_message, p_message_plural, p_n, p_context); } void Object::_clear_internal_resource_paths(const Variant &p_var) { @@ -1714,6 +1724,8 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages); ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("get_translation_domain"), &Object::get_translation_domain); + ClassDB::bind_method(D_METHOD("set_translation_domain", "domain"), &Object::set_translation_domain); ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion); ClassDB::bind_method(D_METHOD("cancel_free"), &Object::cancel_free); diff --git a/core/object/object.h b/core/object/object.h index 6d22f320afc..1274247d716 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -665,6 +665,8 @@ private: Object(bool p_reference); protected: + StringName _translation_domain; + _FORCE_INLINE_ bool _instance_binding_reference(bool p_reference) { bool can_die = true; if (_instance_bindings) { @@ -954,6 +956,9 @@ public: _FORCE_INLINE_ void set_message_translation(bool p_enable) { _can_translate = p_enable; } _FORCE_INLINE_ bool can_translate_messages() const { return _can_translate; } + virtual StringName get_translation_domain() const; + virtual void set_translation_domain(const StringName &p_domain); + #ifdef TOOLS_ENABLED virtual void get_argument_options(const StringName &p_function, int p_idx, List *r_options) const; void editor_set_section_unfold(const String &p_section, bool p_unfolded); diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 0042ce67d52..42753f70715 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -973,6 +973,13 @@ Similar to [method call_thread_safe], but for setting properties. + + + + Makes this node inherit the translation domain from its parent node. If this node has no parent, the main translation domain will be used. + This is the default behavior for all nodes. Calling [method Object.set_translation_domain] disables this behavior. + + diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index a331c05e47c..98811ba2903 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -818,6 +818,12 @@ [b]Note:[/b] Due of the implementation, each [Dictionary] is formatted very similarly to the returned values of [method get_method_list]. + + + + Returns the name of the translation domain used by [method tr] and [method tr_n]. See also [TranslationServer]. + + @@ -1070,6 +1076,13 @@ If a script already exists, its instance is detached, and its property values and state are lost. Built-in property values are still kept. + + + + + Sets the name of the translation domain used by [method tr] and [method tr_n]. See also [TranslationServer]. + + diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 858fc2246b8..eb3448e1a2a 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -111,6 +111,7 @@ void Node::_notification(int p_notification) { data.auto_translate_mode = AUTO_TRANSLATE_MODE_ALWAYS; } 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. @@ -1320,6 +1321,51 @@ bool Node::can_auto_translate() const { return data.is_auto_translating; } +StringName Node::get_translation_domain() const { + ERR_READ_THREAD_GUARD_V(StringName()); + + if (data.is_translation_domain_inherited && data.is_translation_domain_dirty) { + const_cast(this)->_translation_domain = data.parent ? data.parent->get_translation_domain() : StringName(); + data.is_translation_domain_dirty = false; + } + return _translation_domain; +} + +void Node::set_translation_domain(const StringName &p_domain) { + ERR_THREAD_GUARD + + if (!data.is_translation_domain_inherited && _translation_domain == p_domain) { + return; + } + + _translation_domain = p_domain; + data.is_translation_domain_inherited = false; + data.is_translation_domain_dirty = false; + _propagate_translation_domain_dirty(); +} + +void Node::set_translation_domain_inherited() { + ERR_THREAD_GUARD + + if (data.is_translation_domain_inherited) { + return; + } + data.is_translation_domain_inherited = true; + data.is_translation_domain_dirty = true; + _propagate_translation_domain_dirty(); +} + +void Node::_propagate_translation_domain_dirty() { + for (KeyValue &K : data.children) { + Node *child = K.value; + if (child->data.is_translation_domain_inherited) { + child->data.is_translation_domain_dirty = true; + child->_propagate_translation_domain_dirty(); + } + } + notification(NOTIFICATION_TRANSLATION_CHANGED); +} + StringName Node::get_name() const { return data.name; } @@ -3610,6 +3656,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_translate_mode", "mode"), &Node::set_auto_translate_mode); ClassDB::bind_method(D_METHOD("get_auto_translate_mode"), &Node::get_auto_translate_mode); + ClassDB::bind_method(D_METHOD("set_translation_domain_inherited"), &Node::set_translation_domain_inherited); ClassDB::bind_method(D_METHOD("get_window"), &Node::get_window); ClassDB::bind_method(D_METHOD("get_last_exclusive_window"), &Node::get_last_exclusive_window); diff --git a/scene/main/node.h b/scene/main/node.h index dc65513fcae..4560ed085c1 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -255,6 +255,9 @@ private: mutable bool is_auto_translating = true; mutable bool is_auto_translate_dirty = true; + mutable bool is_translation_domain_inherited = true; + mutable bool is_translation_domain_dirty = true; + mutable NodePath *path_cache = nullptr; } data; @@ -281,6 +284,7 @@ private: void _propagate_physics_interpolation_reset_requested(bool p_requested); void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification); void _propagate_groups_dirty(); + void _propagate_translation_domain_dirty(); Array _get_node_and_resource(const NodePath &p_path); void _duplicate_properties(const Node *p_root, const Node *p_original, Node *p_copy, int p_flags) const; @@ -735,6 +739,10 @@ public: AutoTranslateMode get_auto_translate_mode() const; bool can_auto_translate() const; + virtual StringName get_translation_domain() const override; + virtual void set_translation_domain(const StringName &p_domain) override; + void set_translation_domain_inherited(); + _FORCE_INLINE_ String atr(const String p_message, const StringName p_context = "") const { return can_auto_translate() ? tr(p_message, p_context) : p_message; } _FORCE_INLINE_ String atr_n(const String p_message, const StringName &p_message_plural, int p_n, const StringName p_context = "") const { return can_auto_translate() ? tr_n(p_message, p_message_plural, p_n, p_context) : p_message; }