Browse Source

Add context support for editor property name i18n

Haoyu Qiu 1 year ago
parent
commit
479b7afa8e

+ 2 - 2
core/string/translation.cpp

@@ -776,9 +776,9 @@ void TranslationServer::set_property_translation(const Ref<Translation> &p_trans
 	property_translation = p_translation;
 }
 
-StringName TranslationServer::property_translate(const StringName &p_message) const {
+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);
+		StringName r = property_translation->get_message(p_message, p_context);
 		if (r) {
 			return r;
 		}

+ 1 - 1
core/string/translation.h

@@ -183,7 +183,7 @@ public:
 	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;
+	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_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;

+ 2 - 2
editor/editor_feature_profile.cpp

@@ -619,8 +619,8 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
 			if (!(E.usage & PROPERTY_USAGE_EDITOR)) {
 				continue;
 			}
-			const String text = EditorPropertyNameProcessor::get_singleton()->process_name(name, text_style);
-			const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(name, tooltip_style);
+			const String text = EditorPropertyNameProcessor::get_singleton()->process_name(name, text_style, name, class_name);
+			const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(name, tooltip_style, name, class_name);
 
 			TreeItem *property = property_list->create_item(properties);
 			property->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);

+ 5 - 5
editor/editor_inspector.cpp

@@ -57,7 +57,7 @@ bool EditorInspector::_property_path_matches(const String &p_property_path, cons
 
 	const Vector<String> prop_sections = p_property_path.split("/");
 	for (int i = 0; i < prop_sections.size(); i++) {
-		if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(prop_sections[i], p_style))) {
+		if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(prop_sections[i], p_style, p_property_path))) {
 			return true;
 		}
 	}
@@ -3012,7 +3012,7 @@ void EditorInspector::update_tree() {
 		if ((p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE) && name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED) {
 			name_style = EditorPropertyNameProcessor::STYLE_CAPITALIZED;
 		}
-		const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style) + feature_tag;
+		const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style, p.name, doc_name) + feature_tag;
 
 		// Remove the property from the path.
 		int idx = path.rfind("/");
@@ -3081,8 +3081,8 @@ void EditorInspector::update_tree() {
 						tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(component);
 					}
 				} else {
-					label = EditorPropertyNameProcessor::get_singleton()->process_name(component, section_name_style);
-					tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(component, EditorPropertyNameProcessor::get_tooltip_style(section_name_style));
+					label = EditorPropertyNameProcessor::get_singleton()->process_name(component, section_name_style, p.name, doc_name);
+					tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(component, EditorPropertyNameProcessor::get_tooltip_style(section_name_style), p.name, doc_name);
 				}
 
 				Color c = sscolor;
@@ -3145,7 +3145,7 @@ void EditorInspector::update_tree() {
 				editor_inspector_array = memnew(EditorInspectorArray(all_read_only));
 
 				String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path;
-				array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style);
+				array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style, p.name, doc_name);
 				int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0;
 				editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding);
 				editor_inspector_array->connect("page_change_request", callable_mp(this, &EditorInspector::_page_change_request).bind(array_element_prefix));

+ 41 - 2
editor/editor_property_name_processor.cpp

@@ -91,7 +91,27 @@ String EditorPropertyNameProcessor::_capitalize_name(const String &p_name) const
 	return capitalized;
 }
 
-String EditorPropertyNameProcessor::process_name(const String &p_name, Style p_style) const {
+StringName EditorPropertyNameProcessor::_get_context(const String &p_name, const String &p_property, const StringName &p_class) const {
+	if (p_property.is_empty() && p_class == StringName()) {
+		return StringName();
+	}
+	const HashMap<String, StringName> *context_map = translation_contexts.getptr(p_name);
+	if (context_map == nullptr) {
+		return StringName();
+	}
+	// It's expected that full property path is enough to distinguish between usages.
+	// In case a class name is needed, all usages should be prefixed with the class name.
+	const StringName *context = context_map->getptr(p_property);
+	if (context == nullptr && p_class != StringName()) {
+		context = context_map->getptr(String(p_class) + "::" + p_property);
+	}
+	if (context == nullptr) {
+		return StringName();
+	}
+	return *context;
+}
+
+String EditorPropertyNameProcessor::process_name(const String &p_name, Style p_style, const String &p_property, const StringName &p_class) const {
 	switch (p_style) {
 		case STYLE_RAW: {
 			return p_name;
@@ -104,7 +124,7 @@ String EditorPropertyNameProcessor::process_name(const String &p_name, Style p_s
 		case STYLE_LOCALIZED: {
 			const String capitalized = _capitalize_name(p_name);
 			if (TranslationServer::get_singleton()) {
-				return TranslationServer::get_singleton()->property_translate(capitalized);
+				return TranslationServer::get_singleton()->property_translate(capitalized, _get_context(p_name, p_property, p_class));
 			}
 			return capitalized;
 		} break;
@@ -320,6 +340,25 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
 			"then",
 			"to",
 	});
+
+	// Translation context associated with a name.
+	// The second key is either:
+	// - `full/property/path`
+	// - `Class::full/property/path`
+	// In case a class name is needed to distinguish between usages, all usages should use the second format.
+	//
+	// The following initialization is parsed in `editor/translations/scripts/common.py` with a regex.
+	// The map name and value definition format should be kept synced with the regex.
+	translation_contexts["force"]["constant_force"] = "Physics";
+	translation_contexts["force"]["force/8_bit"] = "Enforce";
+	translation_contexts["force"]["force/mono"] = "Enforce";
+	translation_contexts["force"]["force/max_rate"] = "Enforce";
+	translation_contexts["force"]["force/max_rate_hz"] = "Enforce";
+	translation_contexts["normal"]["theme_override_styles/normal"] = "Ordinary";
+	translation_contexts["normal"]["TextureButton::texture_normal"] = "Ordinary";
+	translation_contexts["normal"]["Decal::texture_normal"] = "Geometry";
+	translation_contexts["normal"]["detail_normal"] = "Geometry";
+	translation_contexts["normal"]["normal"] = "Geometry";
 }
 
 EditorPropertyNameProcessor::~EditorPropertyNameProcessor() {

+ 7 - 1
editor/editor_property_name_processor.h

@@ -42,9 +42,14 @@ class EditorPropertyNameProcessor : public Node {
 	HashMap<String, String> capitalize_string_remaps;
 	LocalVector<String> stop_words; // Exceptions that shouldn't be capitalized.
 
+	HashMap<String, HashMap<String, StringName>> translation_contexts;
+
 	// Capitalizes property path segments.
 	String _capitalize_name(const String &p_name) const;
 
+	// Returns the translation context for the given name.
+	StringName _get_context(const String &p_name, const String &p_property, const StringName &p_class) const;
+
 public:
 	// Matches `interface/inspector/capitalize_properties` editor setting.
 	enum Style {
@@ -62,7 +67,8 @@ public:
 	static bool is_localization_available();
 
 	// Turns property path segment into the given style.
-	String process_name(const String &p_name, Style p_style) const;
+	// `p_class` and `p_property` are only used for `STYLE_LOCALIZED`, associating the name with a translation context.
+	String process_name(const String &p_name, Style p_style, const String &p_property = "", const StringName &p_class = "") const;
 
 	// Translate plain text group names.
 	String translate_group_name(const String &p_name) const;

+ 3 - 3
editor/editor_sectioned_inspector.cpp

@@ -42,7 +42,7 @@ static bool _property_path_matches(const String &p_property_path, const String &
 
 	const Vector<String> sections = p_property_path.split("/");
 	for (int i = 0; i < sections.size(); i++) {
-		if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style))) {
+		if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style, p_property_path))) {
 			return true;
 		}
 	}
@@ -278,8 +278,8 @@ void SectionedInspector::update_category_list() {
 				TreeItem *ms = sections->create_item(parent);
 				section_map[metasection] = ms;
 
-				const String text = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], name_style);
-				const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], tooltip_style);
+				const String text = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], name_style, pi.name);
+				const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], tooltip_style, pi.name);
 
 				ms->set_text(0, text);
 				ms->set_tooltip_text(0, tooltip);

+ 2 - 2
editor/editor_settings_dialog.cpp

@@ -452,8 +452,8 @@ void EditorSettingsDialog::_update_shortcuts() {
 
 		TreeItem *section = shortcuts->create_item(root);
 
-		const String item_name = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, name_style);
-		const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, tooltip_style);
+		const String item_name = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, name_style, E);
+		const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(section_name, tooltip_style, E);
 
 		section->set_text(0, item_name);
 		section->set_tooltip_text(0, tooltip);

+ 1 - 1
editor/plugins/tiles/tile_data_editors.cpp

@@ -1280,7 +1280,7 @@ void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, const St
 	property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, p_type, p_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
 	property_editor->set_object_and_property(dummy_object, p_property);
 	if (p_label.is_empty()) {
-		property_editor->set_label(EditorPropertyNameProcessor::get_singleton()->process_name(p_property, EditorPropertyNameProcessor::get_default_inspector_style()));
+		property_editor->set_label(EditorPropertyNameProcessor::get_singleton()->process_name(p_property, EditorPropertyNameProcessor::get_default_inspector_style(), p_property));
 	} else {
 		property_editor->set_label(p_label);
 	}

+ 1 - 1
editor/scene_tree_dock.cpp

@@ -3156,7 +3156,7 @@ void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to,
 			const EditorPropertyNameProcessor::Style style = InspectorDock::get_singleton()->get_property_name_style();
 			menu_properties->clear();
 			for (const String &p : valid_properties) {
-				menu_properties->add_item(EditorPropertyNameProcessor::get_singleton()->process_name(p, style));
+				menu_properties->add_item(EditorPropertyNameProcessor::get_singleton()->process_name(p, style, p, node->get_class_name()));
 				menu_properties->set_item_metadata(-1, p);
 			}
 

+ 0 - 2
scene/theme/default_theme.cpp

@@ -936,8 +936,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 	theme->set_stylebox("separator", "VSeparator", separator_vertical);
 
 	theme->set_icon("close", "Icons", icons["close"]);
-	theme->set_font("normal", "Fonts", Ref<Font>());
-	theme->set_font("large", "Fonts", Ref<Font>());
 
 	theme->set_constant("separation", "HSeparator", Math::round(4 * scale));
 	theme->set_constant("separation", "VSeparator", Math::round(4 * scale));