Переглянути джерело

Merge pull request #86222 from YeldhamDev/give_me_those_strings_baby

Add option to add built-in strings in the POT generation
Rémi Verschelde 1 рік тому
батько
коміт
846428e0c6

+ 1 - 0
core/config/project_settings.cpp

@@ -1514,6 +1514,7 @@ ProjectSettings::ProjectSettings() {
 	GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
 	GLOBAL_DEF_INTERNAL("internationalization/locale/translations", PackedStringArray());
 	GLOBAL_DEF_INTERNAL("internationalization/locale/translations_pot_files", PackedStringArray());
+	GLOBAL_DEF_INTERNAL("internationalization/locale/translation_add_builtin_strings_to_pot", false);
 
 	ProjectSettings::get_singleton()->add_hidden_prefix("input/");
 }

+ 14 - 4
core/object/object.cpp

@@ -1483,10 +1483,15 @@ String Object::tr(const StringName &p_message, const StringName &p_context) cons
 	}
 
 	if (Engine::get_singleton()->is_editor_hint()) {
+		String tr_msg = TranslationServer::get_singleton()->extractable_translate(p_message, p_context);
+		if (!tr_msg.is_empty()) {
+			return tr_msg;
+		}
+
 		return TranslationServer::get_singleton()->tool_translate(p_message, p_context);
-	} else {
-		return TranslationServer::get_singleton()->translate(p_message, p_context);
 	}
+
+	return TranslationServer::get_singleton()->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 {
@@ -1499,10 +1504,15 @@ String Object::tr_n(const StringName &p_message, const StringName &p_message_plu
 	}
 
 	if (Engine::get_singleton()->is_editor_hint()) {
+		String tr_msg = TranslationServer::get_singleton()->extractable_translate_plural(p_message, p_message_plural, p_n, p_context);
+		if (!tr_msg.is_empty()) {
+			return tr_msg;
+		}
+
 		return TranslationServer::get_singleton()->tool_translate_plural(p_message, p_message_plural, p_n, p_context);
-	} else {
-		return TranslationServer::get_singleton()->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);
 }
 
 void Object::_clear_internal_resource_paths(const Variant &p_var) {

+ 33 - 5
core/string/translation.cpp

@@ -772,6 +772,20 @@ StringName TranslationServer::tool_translate_plural(const StringName &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 {
+	if (property_translation.is_valid()) {
+		StringName r = property_translation->get_message(p_message);
+		if (r) {
+			return r;
+		}
+	}
+	return p_message;
+}
+
 void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
 	doc_translation = p_translation;
 }
@@ -800,13 +814,13 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message,
 	return p_message_plural;
 }
 
-void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) {
-	property_translation = p_translation;
+void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) {
+	extractable_translation = p_translation;
 }
 
-StringName TranslationServer::property_translate(const StringName &p_message) const {
-	if (property_translation.is_valid()) {
-		StringName r = property_translation->get_message(p_message);
+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;
 		}
@@ -814,6 +828,20 @@ StringName TranslationServer::property_translate(const StringName &p_message) co
 	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 {
 	return pseudolocalization_enabled;
 }

+ 7 - 3
core/string/translation.h

@@ -82,8 +82,9 @@ class TranslationServer : public Object {
 
 	HashSet<Ref<Translation>> translations;
 	Ref<Translation> tool_translation;
-	Ref<Translation> doc_translation;
 	Ref<Translation> property_translation;
+	Ref<Translation> doc_translation;
+	Ref<Translation> extractable_translation;
 
 	bool enabled = true;
 
@@ -181,11 +182,14 @@ public:
 	Ref<Translation> get_tool_translation() 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;
+	void set_property_translation(const Ref<Translation> &p_translation);
+	StringName property_translate(const StringName &p_message) 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_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;
+	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;
 
 	void setup();
 

+ 7 - 14
core/string/ustring.cpp

@@ -5391,9 +5391,7 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St
 
 /**
  * "Run-time TRanslate". Performs string replacement for internationalization
- * within a running project. The translation string must be supplied by the
- * project, as Godot does not provide built-in translations for `RTR()` strings
- * to keep binary size low. A translation context can optionally be specified to
+ * without the editor. A translation context can optionally be specified to
  * disambiguate between identical source strings in translations. When
  * placeholders are desired, use `vformat(RTR("Example: %s"), some_string)`.
  * If a string mentions a quantity (and may therefore need a dynamic plural form),
@@ -5407,9 +5405,8 @@ String RTR(const String &p_text, const String &p_context) {
 		String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context);
 		if (rtr.is_empty() || rtr == p_text) {
 			return TranslationServer::get_singleton()->translate(p_text, p_context);
-		} else {
-			return rtr;
 		}
+		return rtr;
 	}
 
 	return p_text;
@@ -5417,13 +5414,10 @@ String RTR(const String &p_text, const String &p_context) {
 
 /**
  * "Run-time TRanslate for N items". Performs string replacement for
- * internationalization within a running project. The translation string must be
- * supplied by the project, as Godot does not provide built-in translations for
- * `RTRN()` strings to keep binary size low. A translation context can
- * optionally be specified to disambiguate between identical source strings in
- * translations. Use `RTR()` if the string doesn't need dynamic plural form.
- * When placeholders are desired, use
- * `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`.
+ * internationalization without the editor. A translation context can optionally
+ * be specified to disambiguate between identical source strings in translations.
+ * Use `RTR()` if the string doesn't need dynamic plural form. When placeholders
+ * are desired, use `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`.
  * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`.
  *
  * NOTE: Do not use `RTRN()` in editor-only code (typically within the `editor/`
@@ -5434,9 +5428,8 @@ String RTRN(const String &p_text, const String &p_text_plural, int p_n, const St
 		String rtr = TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context);
 		if (rtr.is_empty() || rtr == p_text || rtr == p_text_plural) {
 			return TranslationServer::get_singleton()->translate_plural(p_text, p_text_plural, p_n, p_context);
-		} else {
-			return rtr;
 		}
+		return rtr;
 	}
 
 	// Return message based on English plural rule if translation is not possible.

+ 37 - 0
core/string/ustring.h

@@ -556,6 +556,43 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St
 String RTR(const String &p_text, const String &p_context = "");
 String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
 
+/**
+ * "Extractable TRanslate". Used for strings that can appear inside an exported
+ * project (such as the ones in nodes like `FileDialog`), which are made possible
+ * to add in the POT generator. A translation context can optionally be specified
+ * to disambiguate between identical source strings in translations.
+ * When placeholders are desired, use vformat(ETR("Example: %s"), some_string)`.
+ * If a string mentions a quantity (and may therefore need a dynamic plural form),
+ * use `ETRN()` instead of `ETR()`.
+ *
+ * NOTE: This function is for string extraction only, and will just return the
+ * string it was given. The translation itself should be done internally by nodes
+ * with `atr()` instead.
+ */
+_FORCE_INLINE_ String ETR(const String &p_text, const String &p_context = "") {
+	return p_text;
+}
+
+/**
+ * "Extractable TRanslate for N items". Used for strings that can appear inside an
+ * exported project (such as the ones in nodes like `FileDialog`), which are made
+ * possible to add in the POT generator. A translation context can optionally be
+ * specified to disambiguate between identical source strings in translations.
+ * Use `ETR()` if the string doesn't need dynamic plural form. When placeholders
+ * are desired, use `vformat(ETRN("%d item", "%d items", some_integer), some_integer)`.
+ * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`.
+ *
+ * NOTE: This function is for string extraction only, and will just return the
+ * string it was given. The translation itself should be done internally by nodes
+ * with `atr()` instead.
+ */
+_FORCE_INLINE_ String ETRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "") {
+	if (p_n == 1) {
+		return p_text;
+	}
+	return p_text_plural;
+}
+
 bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
 
 _FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) {

+ 9 - 0
editor/SCsub

@@ -104,6 +104,15 @@ if env.editor_build:
         env.Run(editor_builders.make_doc_translations_header, "Generating translations header."),
     )
 
+    # Extractable translations
+    tlist = glob.glob(env.Dir("#editor/translations/extractable").abspath + "/*.po")
+    env.Depends("#editor/extractable_translations.gen.h", tlist)
+    env.CommandNoCache(
+        "#editor/extractable_translations.gen.h",
+        tlist,
+        env.Run(editor_builders.make_extractable_translations_header, "Generating extractable translations header."),
+    )
+
     env.add_source_files(env.editor_sources, "*.cpp")
     env.add_source_files(env.editor_sources, "register_exporters.gen.cpp")
 

+ 4 - 0
editor/editor_builders.py

@@ -140,5 +140,9 @@ def make_doc_translations_header(target, source, env):
     make_translations_header(target, source, env, "doc")
 
 
+def make_extractable_translations_header(target, source, env):
+    make_translations_header(target, source, env, "extractable")
+
+
 if __name__ == "__main__":
     subprocess_main(globals())

+ 3 - 0
editor/editor_settings.cpp

@@ -1060,6 +1060,9 @@ void EditorSettings::setup_language() {
 
 	// Load class reference translation.
 	load_doc_translations(lang);
+
+	// Load extractable translation for projects.
+	load_extractable_translations(lang);
 }
 
 void EditorSettings::setup_network() {

+ 56 - 3
editor/editor_translation.cpp

@@ -35,6 +35,7 @@
 #include "core/io/translation_loader_po.h"
 #include "editor/doc_translations.gen.h"
 #include "editor/editor_translations.gen.h"
+#include "editor/extractable_translations.gen.h"
 #include "editor/property_translations.gen.h"
 
 Vector<String> get_editor_locales() {
@@ -77,6 +78,32 @@ void load_editor_translations(const String &p_locale) {
 	}
 }
 
+void load_property_translations(const String &p_locale) {
+	PropertyTranslationList *etl = _property_translations;
+	while (etl->data) {
+		if (etl->lang == p_locale) {
+			Vector<uint8_t> data;
+			data.resize(etl->uncomp_size);
+			int ret = Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE);
+			ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt.");
+
+			Ref<FileAccessMemory> fa;
+			fa.instantiate();
+			fa->open_custom(data.ptr(), data.size());
+
+			Ref<Translation> tr = TranslationLoaderPO::load_translation(fa);
+
+			if (tr.is_valid()) {
+				tr->set_locale(etl->lang);
+				TranslationServer::get_singleton()->set_property_translation(tr);
+				break;
+			}
+		}
+
+		etl++;
+	}
+}
+
 void load_doc_translations(const String &p_locale) {
 	DocTranslationList *dtl = _doc_translations;
 	while (dtl->data) {
@@ -103,8 +130,8 @@ void load_doc_translations(const String &p_locale) {
 	}
 }
 
-void load_property_translations(const String &p_locale) {
-	PropertyTranslationList *etl = _property_translations;
+void load_extractable_translations(const String &p_locale) {
+	ExtractableTranslationList *etl = _extractable_translations;
 	while (etl->data) {
 		if (etl->lang == p_locale) {
 			Vector<uint8_t> data;
@@ -120,7 +147,7 @@ void load_property_translations(const String &p_locale) {
 
 			if (tr.is_valid()) {
 				tr->set_locale(etl->lang);
-				TranslationServer::get_singleton()->set_property_translation(tr);
+				TranslationServer::get_singleton()->set_extractable_translation(tr);
 				break;
 			}
 		}
@@ -128,3 +155,29 @@ void load_property_translations(const String &p_locale) {
 		etl++;
 	}
 }
+
+List<StringName> get_extractable_message_list() {
+	ExtractableTranslationList *etl = _extractable_translations;
+	List<StringName> msgids;
+	while (etl->data) {
+		Vector<uint8_t> data;
+		data.resize(etl->uncomp_size);
+		int ret = Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE);
+		ERR_FAIL_COND_V_MSG(ret == -1, msgids, "Compressed file is corrupt.");
+
+		Ref<FileAccessMemory> fa;
+		fa.instantiate();
+		fa->open_custom(data.ptr(), data.size());
+
+		Ref<Translation> tr = TranslationLoaderPO::load_translation(fa);
+
+		if (tr.is_valid()) {
+			tr->get_message_list(&msgids);
+			break;
+		}
+
+		etl++;
+	}
+
+	return msgids;
+}

+ 4 - 1
editor/editor_translation.h

@@ -32,11 +32,14 @@
 #define EDITOR_TRANSLATION_H
 
 #include "core/string/ustring.h"
+#include "core/templates/list.h"
 #include "core/templates/vector.h"
 
 Vector<String> get_editor_locales();
 void load_editor_translations(const String &p_locale);
-void load_doc_translations(const String &p_locale);
 void load_property_translations(const String &p_locale);
+void load_doc_translations(const String &p_locale);
+void load_extractable_translations(const String &p_locale);
+List<StringName> get_extractable_message_list();
 
 #endif // EDITOR_TRANSLATION_H

+ 12 - 5
editor/localization_editor.cpp

@@ -45,6 +45,7 @@ void LocalizationEditor::_notification(int p_what) {
 		case NOTIFICATION_ENTER_TREE: {
 			translation_list->connect("button_clicked", callable_mp(this, &LocalizationEditor::_translation_delete));
 			translation_pot_list->connect("button_clicked", callable_mp(this, &LocalizationEditor::_pot_delete));
+			translation_pot_add_builtin->set_pressed(GLOBAL_GET("internationalization/locale/translation_add_builtin_strings_to_pot"));
 
 			List<String> tfn;
 			ResourceLoader::get_recognized_extensions_for_type("Translation", &tfn);
@@ -377,6 +378,11 @@ void LocalizationEditor::_pot_generate_open() {
 	pot_generate_dialog->popup_file_dialog();
 }
 
+void LocalizationEditor::_pot_add_builtin_toggled() {
+	ProjectSettings::get_singleton()->set_setting("internationalization/locale/translation_add_builtin_strings_to_pot", translation_pot_add_builtin->is_pressed());
+	ProjectSettings::get_singleton()->save();
+}
+
 void LocalizationEditor::_pot_generate(const String &p_file) {
 	POTGenerator::get_singleton()->generate_pot(p_file);
 }
@@ -730,13 +736,14 @@ LocalizationEditor::LocalizationEditor() {
 		pot_generate_button->connect("pressed", callable_mp(this, &LocalizationEditor::_pot_generate_open));
 		thb->add_child(pot_generate_button);
 
-		VBoxContainer *tmc = memnew(VBoxContainer);
-		tmc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-		tvb->add_child(tmc);
-
 		translation_pot_list = memnew(Tree);
 		translation_pot_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-		tmc->add_child(translation_pot_list);
+		tvb->add_child(translation_pot_list);
+
+		translation_pot_add_builtin = memnew(CheckBox(TTR("Add Built-in Strings to POT")));
+		translation_pot_add_builtin->set_tooltip_text(TTR("Add strings from built-in components such as certain Control nodes."));
+		translation_pot_add_builtin->connect("pressed", callable_mp(this, &LocalizationEditor::_pot_add_builtin_toggled));
+		tvb->add_child(translation_pot_add_builtin);
 
 		pot_generate_dialog = memnew(EditorFileDialog);
 		pot_generate_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);

+ 3 - 0
editor/localization_editor.h

@@ -32,6 +32,7 @@
 #define LOCALIZATION_EDITOR_H
 
 #include "editor/editor_locale_dialog.h"
+#include "scene/gui/check_box.h"
 #include "scene/gui/tree.h"
 
 class EditorFileDialog;
@@ -52,6 +53,7 @@ class LocalizationEditor : public VBoxContainer {
 	Tree *translation_remap_options = nullptr;
 
 	Tree *translation_pot_list = nullptr;
+	CheckBox *translation_pot_add_builtin = nullptr;
 	EditorFileDialog *pot_file_open_dialog = nullptr;
 	EditorFileDialog *pot_generate_dialog = nullptr;
 	Button *pot_generate_button = nullptr;
@@ -78,6 +80,7 @@ class LocalizationEditor : public VBoxContainer {
 	void _pot_delete(Object *p_item, int p_column, int p_button, MouseButton p_mouse_button);
 	void _pot_file_open();
 	void _pot_generate_open();
+	void _pot_add_builtin_toggled();
 	void _pot_generate(const String &p_file);
 	void _update_pot_file_extensions();
 

+ 12 - 1
editor/pot_generator.cpp

@@ -32,6 +32,7 @@
 
 #include "core/config/project_settings.h"
 #include "core/error/error_macros.h"
+#include "editor/editor_translation.h"
 #include "editor/editor_translation_parser.h"
 #include "plugins/packed_scene_translation_parser_plugin.h"
 
@@ -65,6 +66,8 @@ void POTGenerator::generate_pot(const String &p_file) {
 	// Clear all_translation_strings of the previous round.
 	all_translation_strings.clear();
 
+	List<StringName> extractable_msgids = get_extractable_message_list();
+
 	// Collect all translatable strings according to files order in "POT Generation" setting.
 	for (int i = 0; i < files.size(); i++) {
 		Vector<String> msgids;
@@ -88,6 +91,12 @@ void POTGenerator::generate_pot(const String &p_file) {
 		}
 	}
 
+	if (GLOBAL_GET("internationalization/locale/translation_add_builtin_strings_to_pot")) {
+		for (int i = 0; i < extractable_msgids.size(); i++) {
+			_add_new_msgid(extractable_msgids[i], "", "", "");
+		}
+	}
+
 	_write_to_pot(p_file);
 }
 
@@ -136,7 +145,9 @@ void POTGenerator::_write_to_pot(const String &p_file) {
 
 			// Write file locations.
 			for (const String &E : locations) {
-				file->store_line("#: " + E.trim_prefix("res://").replace("\n", "\\n"));
+				if (!E.is_empty()) {
+					file->store_line("#: " + E.trim_prefix("res://").replace("\n", "\\n"));
+				}
 			}
 
 			// Write context.

+ 2 - 2
scene/gui/code_edit.cpp

@@ -1824,14 +1824,14 @@ void CodeEdit::create_code_region() {
 		}
 		int to_line = get_selection_to_line(caret_idx);
 		set_line(to_line, get_line(to_line) + "\n" + code_region_end_string);
-		insert_line_at(from_line, code_region_start_string + " " + RTR("New Code Region"));
+		insert_line_at(from_line, code_region_start_string + " " + atr(ETR("New Code Region")));
 		fold_line(from_line);
 	}
 
 	// Select name of the first region to allow quick edit.
 	remove_secondary_carets();
 	set_caret_line(first_region_start);
-	int tag_length = code_region_start_string.length() + RTR("New Code Region").length() + 1;
+	int tag_length = code_region_start_string.length() + atr(ETR("New Code Region")).length() + 1;
 	set_caret_column(tag_length);
 	select(first_region_start, code_region_start_string.length() + 1, first_region_start, tag_length);
 

+ 26 - 10
scene/gui/color_picker.cpp

@@ -54,6 +54,22 @@ void ColorPicker::_notification(int p_what) {
 			_update_color();
 		} break;
 
+		case NOTIFICATION_TRANSLATION_CHANGED: {
+			List<BaseButton *> buttons;
+			preset_group->get_buttons(&buttons);
+			for (List<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
+				Color preset_color = ((ColorPresetButton *)E->get())->get_preset_color();
+				E->get()->set_tooltip_text(vformat(atr(ETR("Color: #%s\nLMB: Apply color\nRMB: Remove preset")), preset_color.to_html(preset_color.a < 1)));
+			}
+
+			buttons.clear();
+			recent_preset_group->get_buttons(&buttons);
+			for (List<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
+				Color preset_color = ((ColorPresetButton *)E->get())->get_preset_color();
+				E->get()->set_tooltip_text(vformat(atr(ETR("Color: #%s\nLMB: Apply color")), preset_color.to_html(preset_color.a < 1)));
+			}
+		} break;
+
 		case NOTIFICATION_THEME_CHANGED: {
 			btn_pick->set_icon(theme_cache.screen_picker);
 			_update_drop_down_arrow(btn_preset->is_pressed(), btn_preset);
@@ -689,7 +705,7 @@ void ColorPicker::_text_type_toggled() {
 		text_type->set_icon(nullptr);
 
 		c_text->set_editable(true);
-		c_text->set_tooltip_text(RTR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
+		c_text->set_tooltip_text(ETR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
 	}
 	_update_color();
 }
@@ -735,7 +751,7 @@ inline int ColorPicker::_get_preset_size() {
 
 void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
 	ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size));
-	btn_preset_new->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1)));
+	btn_preset_new->set_tooltip_text(vformat(atr(ETR("Color: #%s\nLMB: Apply color\nRMB: Remove preset")), p_color.to_html(p_color.a < 1)));
 	SET_DRAG_FORWARDING_GCDU(btn_preset_new, ColorPicker);
 	btn_preset_new->set_button_group(preset_group);
 	preset_container->add_child(btn_preset_new);
@@ -745,7 +761,7 @@ void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
 
 void ColorPicker::_add_recent_preset_button(int p_size, const Color &p_color) {
 	ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size));
-	btn_preset_new->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color"), p_color.to_html(p_color.a < 1)));
+	btn_preset_new->set_tooltip_text(vformat(atr(ETR("Color: #%s\nLMB: Apply color")), p_color.to_html(p_color.a < 1)));
 	btn_preset_new->set_button_group(recent_preset_group);
 	recent_preset_hbc->add_child(btn_preset_new);
 	recent_preset_hbc->move_child(btn_preset_new, 0);
@@ -1807,11 +1823,11 @@ ColorPicker::ColorPicker() {
 	btn_pick = memnew(Button);
 	sample_hbc->add_child(btn_pick);
 	if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SCREEN_CAPTURE)) {
-		btn_pick->set_tooltip_text(RTR("Pick a color from the screen."));
+		btn_pick->set_tooltip_text(ETR("Pick a color from the screen."));
 		btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed));
 	} else {
 		// On unsupported platforms, use a legacy method for color picking.
-		btn_pick->set_tooltip_text(RTR("Pick a color from the application window."));
+		btn_pick->set_tooltip_text(ETR("Pick a color from the application window."));
 		btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed_legacy));
 	}
 
@@ -1825,7 +1841,7 @@ ColorPicker::ColorPicker() {
 	btn_shape->set_flat(false);
 	sample_hbc->add_child(btn_shape);
 	btn_shape->set_toggle_mode(true);
-	btn_shape->set_tooltip_text(RTR("Select a picker shape."));
+	btn_shape->set_tooltip_text(ETR("Select a picker shape."));
 
 	current_shape = SHAPE_HSV_RECTANGLE;
 
@@ -1864,7 +1880,7 @@ ColorPicker::ColorPicker() {
 	btn_mode->set_flat(false);
 	mode_hbc->add_child(btn_mode);
 	btn_mode->set_toggle_mode(true);
-	btn_mode->set_tooltip_text(RTR("Select a picker mode."));
+	btn_mode->set_tooltip_text(ETR("Select a picker mode."));
 
 	current_mode = MODE_RGB;
 
@@ -1918,8 +1934,8 @@ ColorPicker::ColorPicker() {
 	hex_hbc->add_child(c_text);
 	c_text->set_h_size_flags(SIZE_EXPAND_FILL);
 	c_text->set_select_all_on_focus(true);
-	c_text->set_tooltip_text(RTR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
-	c_text->set_placeholder(RTR("Hex code or named color"));
+	c_text->set_tooltip_text(ETR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
+	c_text->set_placeholder(ETR("Hex code or named color"));
 	c_text->connect("text_submitted", callable_mp(this, &ColorPicker::_html_submitted));
 	c_text->connect("text_changed", callable_mp(this, &ColorPicker::_text_changed));
 	c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit));
@@ -1997,7 +2013,7 @@ ColorPicker::ColorPicker() {
 
 	btn_add_preset = memnew(Button);
 	btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
-	btn_add_preset->set_tooltip_text(RTR("Add current color as a preset."));
+	btn_add_preset->set_tooltip_text(ETR("Add current color as a preset."));
 	btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed));
 	preset_container->add_child(btn_add_preset);
 }

+ 31 - 31
scene/gui/file_dialog.cpp

@@ -302,7 +302,7 @@ void FileDialog::update_dir() {
 	if (drives->is_visible()) {
 		if (dir_access->get_current_dir().is_network_share_path()) {
 			_update_drives(false);
-			drives->add_item(RTR("Network"));
+			drives->add_item(ETR("Network"));
 			drives->set_item_disabled(-1, true);
 			drives->select(drives->get_item_count() - 1);
 		} else {
@@ -459,7 +459,7 @@ void FileDialog::_action_pressed() {
 		}
 
 		if (dir_access->file_exists(f)) {
-			confirm_save->set_text(vformat(RTR("File \"%s\" already exists.\nDo you want to overwrite it?"), f));
+			confirm_save->set_text(vformat(atr(ETR("File \"%s\" already exists.\nDo you want to overwrite it?")), f));
 			confirm_save->popup_centered(Size2(250, 80));
 		} else {
 			emit_signal(SNAME("file_selected"), f);
@@ -539,10 +539,10 @@ void FileDialog::deselect_all() {
 		switch (mode) {
 			case FILE_MODE_OPEN_FILE:
 			case FILE_MODE_OPEN_FILES:
-				set_ok_button_text(RTR("Open"));
+				set_ok_button_text(ETR("Open"));
 				break;
 			case FILE_MODE_OPEN_DIR:
-				set_ok_button_text(RTR("Select Current Folder"));
+				set_ok_button_text(ETR("Select Current Folder"));
 				break;
 			case FILE_MODE_OPEN_ANY:
 			case FILE_MODE_SAVE_FILE:
@@ -566,7 +566,7 @@ void FileDialog::_tree_selected() {
 	if (!d["dir"]) {
 		file->set_text(d["name"]);
 	} else if (mode == FILE_MODE_OPEN_DIR) {
-		set_ok_button_text(RTR("Select This Folder"));
+		set_ok_button_text(ETR("Select This Folder"));
 	}
 
 	get_ok_button()->set_disabled(_is_open_should_be_disabled());
@@ -621,7 +621,7 @@ void FileDialog::update_file_list() {
 	if (dir_access->is_readable(dir_access->get_current_dir().utf8().get_data())) {
 		message->hide();
 	} else {
-		message->set_text(RTR("You don't have permission to access contents of this folder."));
+		message->set_text(ETR("You don't have permission to access contents of this folder."));
 		message->show();
 	}
 
@@ -771,7 +771,7 @@ void FileDialog::update_filters() {
 			all_filters += ", ...";
 		}
 
-		filter->add_item(RTR("All Recognized") + " (" + all_filters + ")");
+		filter->add_item(atr(ETR("All Recognized")) + " (" + all_filters + ")");
 	}
 	for (int i = 0; i < filters.size(); i++) {
 		String flt = filters[i].get_slice(";", 0).strip_edges();
@@ -783,7 +783,7 @@ void FileDialog::update_filters() {
 		}
 	}
 
-	filter->add_item(RTR("All Files") + " (*)");
+	filter->add_item(atr(ETR("All Files")) + " (*)");
 }
 
 void FileDialog::clear_filters() {
@@ -896,37 +896,37 @@ void FileDialog::set_file_mode(FileMode p_mode) {
 	mode = p_mode;
 	switch (mode) {
 		case FILE_MODE_OPEN_FILE:
-			set_ok_button_text(RTR("Open"));
+			set_ok_button_text(ETR("Open"));
 			if (mode_overrides_title) {
-				set_title(TTRC("Open a File"));
+				set_title(ETR("Open a File"));
 			}
 			makedir->hide();
 			break;
 		case FILE_MODE_OPEN_FILES:
-			set_ok_button_text(RTR("Open"));
+			set_ok_button_text(ETR("Open"));
 			if (mode_overrides_title) {
-				set_title(TTRC("Open File(s)"));
+				set_title(ETR("Open File(s)"));
 			}
 			makedir->hide();
 			break;
 		case FILE_MODE_OPEN_DIR:
-			set_ok_button_text(RTR("Select Current Folder"));
+			set_ok_button_text(ETR("Select Current Folder"));
 			if (mode_overrides_title) {
-				set_title(TTRC("Open a Directory"));
+				set_title(ETR("Open a Directory"));
 			}
 			makedir->show();
 			break;
 		case FILE_MODE_OPEN_ANY:
-			set_ok_button_text(RTR("Open"));
+			set_ok_button_text(ETR("Open"));
 			if (mode_overrides_title) {
-				set_title(TTRC("Open a File or Directory"));
+				set_title(ETR("Open a File or Directory"));
 			}
 			makedir->show();
 			break;
 		case FILE_MODE_SAVE_FILE:
-			set_ok_button_text(RTR("Save"));
+			set_ok_button_text(ETR("Save"));
 			if (mode_overrides_title) {
-				set_title(TTRC("Save a File"));
+				set_title(ETR("Save a File"));
 			}
 			makedir->show();
 			break;
@@ -1389,13 +1389,13 @@ FileDialog::FileDialog() {
 
 	dir_prev = memnew(Button);
 	dir_prev->set_theme_type_variation("FlatButton");
-	dir_prev->set_tooltip_text(RTR("Go to previous folder."));
+	dir_prev->set_tooltip_text(ETR("Go to previous folder."));
 	dir_next = memnew(Button);
 	dir_next->set_theme_type_variation("FlatButton");
-	dir_next->set_tooltip_text(RTR("Go to next folder."));
+	dir_next->set_tooltip_text(ETR("Go to next folder."));
 	dir_up = memnew(Button);
 	dir_up->set_theme_type_variation("FlatButton");
-	dir_up->set_tooltip_text(RTR("Go to parent folder."));
+	dir_up->set_tooltip_text(ETR("Go to parent folder."));
 	hbc->add_child(dir_prev);
 	hbc->add_child(dir_next);
 	hbc->add_child(dir_up);
@@ -1403,7 +1403,7 @@ FileDialog::FileDialog() {
 	dir_next->connect("pressed", callable_mp(this, &FileDialog::_go_forward));
 	dir_up->connect("pressed", callable_mp(this, &FileDialog::_go_up));
 
-	hbc->add_child(memnew(Label(RTR("Path:"))));
+	hbc->add_child(memnew(Label(ETR("Path:"))));
 
 	drives_container = memnew(HBoxContainer);
 	hbc->add_child(drives_container);
@@ -1419,7 +1419,7 @@ FileDialog::FileDialog() {
 
 	refresh = memnew(Button);
 	refresh->set_theme_type_variation("FlatButton");
-	refresh->set_tooltip_text(RTR("Refresh files."));
+	refresh->set_tooltip_text(ETR("Refresh files."));
 	refresh->connect("pressed", callable_mp(this, &FileDialog::update_file_list));
 	hbc->add_child(refresh);
 
@@ -1427,7 +1427,7 @@ FileDialog::FileDialog() {
 	show_hidden->set_theme_type_variation("FlatButton");
 	show_hidden->set_toggle_mode(true);
 	show_hidden->set_pressed(is_showing_hidden_files());
-	show_hidden->set_tooltip_text(RTR("Toggle the visibility of hidden files."));
+	show_hidden->set_tooltip_text(ETR("Toggle the visibility of hidden files."));
 	show_hidden->connect("toggled", callable_mp(this, &FileDialog::set_show_hidden_files));
 	hbc->add_child(show_hidden);
 
@@ -1436,14 +1436,14 @@ FileDialog::FileDialog() {
 
 	makedir = memnew(Button);
 	makedir->set_theme_type_variation("FlatButton");
-	makedir->set_tooltip_text(RTR("Create a new folder."));
+	makedir->set_tooltip_text(ETR("Create a new folder."));
 	makedir->connect("pressed", callable_mp(this, &FileDialog::_make_dir));
 	hbc->add_child(makedir);
 	vbox->add_child(hbc);
 
 	tree = memnew(Tree);
 	tree->set_hide_root(true);
-	vbox->add_margin_child(RTR("Directories & Files:"), tree, true);
+	vbox->add_margin_child(ETR("Directories & Files:"), tree, true);
 
 	message = memnew(Label);
 	message->hide();
@@ -1453,7 +1453,7 @@ FileDialog::FileDialog() {
 	tree->add_child(message);
 
 	file_box = memnew(HBoxContainer);
-	file_box->add_child(memnew(Label(RTR("File:"))));
+	file_box->add_child(memnew(Label(ETR("File:"))));
 	file = memnew(LineEdit);
 	file->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
 	file->set_stretch_ratio(4);
@@ -1489,22 +1489,22 @@ FileDialog::FileDialog() {
 	confirm_save->connect("confirmed", callable_mp(this, &FileDialog::_save_confirm_pressed));
 
 	makedialog = memnew(ConfirmationDialog);
-	makedialog->set_title(RTR("Create Folder"));
+	makedialog->set_title(ETR("Create Folder"));
 	VBoxContainer *makevb = memnew(VBoxContainer);
 	makedialog->add_child(makevb);
 
 	makedirname = memnew(LineEdit);
 	makedirname->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
-	makevb->add_margin_child(RTR("Name:"), makedirname);
+	makevb->add_margin_child(ETR("Name:"), makedirname);
 	add_child(makedialog, false, INTERNAL_MODE_FRONT);
 	makedialog->register_text_enter(makedirname);
 	makedialog->connect("confirmed", callable_mp(this, &FileDialog::_make_dir_confirm));
 	mkdirerr = memnew(AcceptDialog);
-	mkdirerr->set_text(RTR("Could not create folder."));
+	mkdirerr->set_text(ETR("Could not create folder."));
 	add_child(mkdirerr, false, INTERNAL_MODE_FRONT);
 
 	exterr = memnew(AcceptDialog);
-	exterr->set_text(RTR("Invalid extension, or empty filename."));
+	exterr->set_text(ETR("Invalid extension, or empty filename."));
 	add_child(exterr, false, INTERNAL_MODE_FRONT);
 
 	update_filters();

+ 8 - 8
scene/gui/graph_edit.cpp

@@ -2435,7 +2435,7 @@ GraphEdit::GraphEdit() {
 	zoom_minus_button = memnew(Button);
 	zoom_minus_button->set_theme_type_variation("FlatButton");
 	zoom_minus_button->set_visible(show_zoom_buttons);
-	zoom_minus_button->set_tooltip_text(RTR("Zoom Out"));
+	zoom_minus_button->set_tooltip_text(ETR("Zoom Out"));
 	zoom_minus_button->set_focus_mode(FOCUS_NONE);
 	menu_hbox->add_child(zoom_minus_button);
 	zoom_minus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus));
@@ -2443,7 +2443,7 @@ GraphEdit::GraphEdit() {
 	zoom_reset_button = memnew(Button);
 	zoom_reset_button->set_theme_type_variation("FlatButton");
 	zoom_reset_button->set_visible(show_zoom_buttons);
-	zoom_reset_button->set_tooltip_text(RTR("Zoom Reset"));
+	zoom_reset_button->set_tooltip_text(ETR("Zoom Reset"));
 	zoom_reset_button->set_focus_mode(FOCUS_NONE);
 	menu_hbox->add_child(zoom_reset_button);
 	zoom_reset_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset));
@@ -2451,7 +2451,7 @@ GraphEdit::GraphEdit() {
 	zoom_plus_button = memnew(Button);
 	zoom_plus_button->set_theme_type_variation("FlatButton");
 	zoom_plus_button->set_visible(show_zoom_buttons);
-	zoom_plus_button->set_tooltip_text(RTR("Zoom In"));
+	zoom_plus_button->set_tooltip_text(ETR("Zoom In"));
 	zoom_plus_button->set_focus_mode(FOCUS_NONE);
 	menu_hbox->add_child(zoom_plus_button);
 	zoom_plus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus));
@@ -2463,7 +2463,7 @@ GraphEdit::GraphEdit() {
 	toggle_grid_button->set_visible(show_grid_buttons);
 	toggle_grid_button->set_toggle_mode(true);
 	toggle_grid_button->set_pressed(true);
-	toggle_grid_button->set_tooltip_text(RTR("Toggle the visual grid."));
+	toggle_grid_button->set_tooltip_text(ETR("Toggle the visual grid."));
 	toggle_grid_button->set_focus_mode(FOCUS_NONE);
 	menu_hbox->add_child(toggle_grid_button);
 	toggle_grid_button->connect("pressed", callable_mp(this, &GraphEdit::_show_grid_toggled));
@@ -2472,7 +2472,7 @@ GraphEdit::GraphEdit() {
 	toggle_snapping_button->set_theme_type_variation("FlatButton");
 	toggle_snapping_button->set_visible(show_grid_buttons);
 	toggle_snapping_button->set_toggle_mode(true);
-	toggle_snapping_button->set_tooltip_text(RTR("Toggle snapping to the grid."));
+	toggle_snapping_button->set_tooltip_text(ETR("Toggle snapping to the grid."));
 	toggle_snapping_button->set_pressed(snapping_enabled);
 	toggle_snapping_button->set_focus_mode(FOCUS_NONE);
 	menu_hbox->add_child(toggle_snapping_button);
@@ -2484,7 +2484,7 @@ GraphEdit::GraphEdit() {
 	snapping_distance_spinbox->set_max(GRID_MAX_SNAPPING_DISTANCE);
 	snapping_distance_spinbox->set_step(1);
 	snapping_distance_spinbox->set_value(snapping_distance);
-	snapping_distance_spinbox->set_tooltip_text(RTR("Change the snapping distance."));
+	snapping_distance_spinbox->set_tooltip_text(ETR("Change the snapping distance."));
 	menu_hbox->add_child(snapping_distance_spinbox);
 	snapping_distance_spinbox->connect("value_changed", callable_mp(this, &GraphEdit::_snapping_distance_changed));
 
@@ -2494,7 +2494,7 @@ GraphEdit::GraphEdit() {
 	minimap_button->set_theme_type_variation("FlatButton");
 	minimap_button->set_visible(show_minimap_button);
 	minimap_button->set_toggle_mode(true);
-	minimap_button->set_tooltip_text(RTR("Toggle the graph minimap."));
+	minimap_button->set_tooltip_text(ETR("Toggle the graph minimap."));
 	minimap_button->set_pressed(show_grid);
 	minimap_button->set_focus_mode(FOCUS_NONE);
 	menu_hbox->add_child(minimap_button);
@@ -2506,7 +2506,7 @@ GraphEdit::GraphEdit() {
 	arrange_button->connect("pressed", callable_mp(this, &GraphEdit::arrange_nodes));
 	arrange_button->set_focus_mode(FOCUS_NONE);
 	menu_hbox->add_child(arrange_button);
-	arrange_button->set_tooltip_text(RTR("Automatically arrange selected nodes."));
+	arrange_button->set_tooltip_text(ETR("Automatically arrange selected nodes."));
 
 	// Minimap.
 

+ 31 - 31
scene/gui/line_edit.cpp

@@ -2406,45 +2406,45 @@ void LineEdit::_generate_context_menu() {
 	add_child(menu, false, INTERNAL_MODE_FRONT);
 
 	menu_dir = memnew(PopupMenu);
-	menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED);
-	menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO);
-	menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR);
-	menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL);
+	menu_dir->add_radio_check_item(ETR("Same as Layout Direction"), MENU_DIR_INHERITED);
+	menu_dir->add_radio_check_item(ETR("Auto-Detect Direction"), MENU_DIR_AUTO);
+	menu_dir->add_radio_check_item(ETR("Left-to-Right"), MENU_DIR_LTR);
+	menu_dir->add_radio_check_item(ETR("Right-to-Left"), MENU_DIR_RTL);
 
 	menu_ctl = memnew(PopupMenu);
-	menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
-	menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
-	menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
-	menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE);
-	menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO);
-	menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO);
-	menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF);
+	menu_ctl->add_item(ETR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
+	menu_ctl->add_item(ETR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
+	menu_ctl->add_item(ETR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
+	menu_ctl->add_item(ETR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE);
+	menu_ctl->add_item(ETR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO);
+	menu_ctl->add_item(ETR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO);
+	menu_ctl->add_item(ETR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF);
 	menu_ctl->add_separator();
-	menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM);
-	menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI);
-	menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI);
-	menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI);
-	menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI);
+	menu_ctl->add_item(ETR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM);
+	menu_ctl->add_item(ETR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI);
+	menu_ctl->add_item(ETR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI);
+	menu_ctl->add_item(ETR("First Strong Isolate (FSI)"), MENU_INSERT_FSI);
+	menu_ctl->add_item(ETR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI);
 	menu_ctl->add_separator();
-	menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ);
-	menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
-	menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ);
-	menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
-
-	menu->add_item(RTR("Cut"), MENU_CUT);
-	menu->add_item(RTR("Copy"), MENU_COPY);
-	menu->add_item(RTR("Paste"), MENU_PASTE);
+	menu_ctl->add_item(ETR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ);
+	menu_ctl->add_item(ETR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
+	menu_ctl->add_item(ETR("Word Joiner (WJ)"), MENU_INSERT_WJ);
+	menu_ctl->add_item(ETR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
+
+	menu->add_item(ETR("Cut"), MENU_CUT);
+	menu->add_item(ETR("Copy"), MENU_COPY);
+	menu->add_item(ETR("Paste"), MENU_PASTE);
 	menu->add_separator();
-	menu->add_item(RTR("Select All"), MENU_SELECT_ALL);
-	menu->add_item(RTR("Clear"), MENU_CLEAR);
+	menu->add_item(ETR("Select All"), MENU_SELECT_ALL);
+	menu->add_item(ETR("Clear"), MENU_CLEAR);
 	menu->add_separator();
-	menu->add_item(RTR("Undo"), MENU_UNDO);
-	menu->add_item(RTR("Redo"), MENU_REDO);
+	menu->add_item(ETR("Undo"), MENU_UNDO);
+	menu->add_item(ETR("Redo"), MENU_REDO);
 	menu->add_separator();
-	menu->add_submenu_node_item(RTR("Text Writing Direction"), menu_dir, MENU_SUBMENU_TEXT_DIR);
+	menu->add_submenu_node_item(ETR("Text Writing Direction"), menu_dir, MENU_SUBMENU_TEXT_DIR);
 	menu->add_separator();
-	menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC);
-	menu->add_submenu_node_item(RTR("Insert Control Character"), menu_ctl, MENU_SUBMENU_INSERT_UCC);
+	menu->add_check_item(ETR("Display Control Characters"), MENU_DISPLAY_UCC);
+	menu->add_submenu_node_item(ETR("Insert Control Character"), menu_ctl, MENU_SUBMENU_INSERT_UCC);
 
 	menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
 	menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));

+ 2 - 2
scene/gui/rich_text_label.cpp

@@ -6270,8 +6270,8 @@ void RichTextLabel::_generate_context_menu() {
 	add_child(menu, false, INTERNAL_MODE_FRONT);
 	menu->connect("id_pressed", callable_mp(this, &RichTextLabel::menu_option));
 
-	menu->add_item(RTR("Copy"), MENU_COPY);
-	menu->add_item(RTR("Select All"), MENU_SELECT_ALL);
+	menu->add_item(ETR("Copy"), MENU_COPY);
+	menu->add_item(ETR("Select All"), MENU_SELECT_ALL);
 }
 
 void RichTextLabel::_update_context_menu() {

+ 31 - 31
scene/gui/text_edit.cpp

@@ -6873,45 +6873,45 @@ void TextEdit::_generate_context_menu() {
 	add_child(menu, false, INTERNAL_MODE_FRONT);
 
 	menu_dir = memnew(PopupMenu);
-	menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED);
-	menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO);
-	menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR);
-	menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL);
+	menu_dir->add_radio_check_item(ETR("Same as Layout Direction"), MENU_DIR_INHERITED);
+	menu_dir->add_radio_check_item(ETR("Auto-Detect Direction"), MENU_DIR_AUTO);
+	menu_dir->add_radio_check_item(ETR("Left-to-Right"), MENU_DIR_LTR);
+	menu_dir->add_radio_check_item(ETR("Right-to-Left"), MENU_DIR_RTL);
 
 	menu_ctl = memnew(PopupMenu);
-	menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
-	menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
-	menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
-	menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE);
-	menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO);
-	menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO);
-	menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF);
+	menu_ctl->add_item(ETR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
+	menu_ctl->add_item(ETR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
+	menu_ctl->add_item(ETR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
+	menu_ctl->add_item(ETR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE);
+	menu_ctl->add_item(ETR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO);
+	menu_ctl->add_item(ETR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO);
+	menu_ctl->add_item(ETR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF);
 	menu_ctl->add_separator();
-	menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM);
-	menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI);
-	menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI);
-	menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI);
-	menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI);
+	menu_ctl->add_item(ETR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM);
+	menu_ctl->add_item(ETR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI);
+	menu_ctl->add_item(ETR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI);
+	menu_ctl->add_item(ETR("First Strong Isolate (FSI)"), MENU_INSERT_FSI);
+	menu_ctl->add_item(ETR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI);
 	menu_ctl->add_separator();
-	menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ);
-	menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
-	menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ);
-	menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
-
-	menu->add_item(RTR("Cut"), MENU_CUT);
-	menu->add_item(RTR("Copy"), MENU_COPY);
-	menu->add_item(RTR("Paste"), MENU_PASTE);
+	menu_ctl->add_item(ETR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ);
+	menu_ctl->add_item(ETR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
+	menu_ctl->add_item(ETR("Word Joiner (WJ)"), MENU_INSERT_WJ);
+	menu_ctl->add_item(ETR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
+
+	menu->add_item(ETR("Cut"), MENU_CUT);
+	menu->add_item(ETR("Copy"), MENU_COPY);
+	menu->add_item(ETR("Paste"), MENU_PASTE);
 	menu->add_separator();
-	menu->add_item(RTR("Select All"), MENU_SELECT_ALL);
-	menu->add_item(RTR("Clear"), MENU_CLEAR);
+	menu->add_item(ETR("Select All"), MENU_SELECT_ALL);
+	menu->add_item(ETR("Clear"), MENU_CLEAR);
 	menu->add_separator();
-	menu->add_item(RTR("Undo"), MENU_UNDO);
-	menu->add_item(RTR("Redo"), MENU_REDO);
+	menu->add_item(ETR("Undo"), MENU_UNDO);
+	menu->add_item(ETR("Redo"), MENU_REDO);
 	menu->add_separator();
-	menu->add_submenu_node_item(RTR("Text Writing Direction"), menu_dir, MENU_SUBMENU_TEXT_DIR);
+	menu->add_submenu_node_item(ETR("Text Writing Direction"), menu_dir, MENU_SUBMENU_TEXT_DIR);
 	menu->add_separator();
-	menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC);
-	menu->add_submenu_node_item(RTR("Insert Control Character"), menu_ctl, MENU_SUBMENU_INSERT_UCC);
+	menu->add_check_item(ETR("Display Control Characters"), MENU_DISPLAY_UCC);
+	menu->add_submenu_node_item(ETR("Insert Control Character"), menu_ctl, MENU_SUBMENU_INSERT_UCC);
 
 	menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
 	menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));

+ 1 - 1
scene/gui/tree.cpp

@@ -1949,7 +1949,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
 
 			int option = (int)p_item->cells[p_col].val;
 
-			valtext = RTR("(Other)");
+			valtext = atr(ETR("(Other)"));
 			Vector<String> strings = p_item->cells[p_col].text.split(",");
 			for (int j = 0; j < strings.size(); j++) {
 				int value = j;

+ 1 - 1
scene/main/viewport.cpp

@@ -1447,7 +1447,7 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Cont
 	String tooltip;
 
 	while (p_control) {
-		tooltip = p_control->get_tooltip(pos);
+		tooltip = p_control->atr(p_control->get_tooltip(pos));
 
 		// Temporary solution for PopupMenus.
 		PopupMenu *menu = Object::cast_to<PopupMenu>(this);