Merge pull request #82852 from timothyqiu/property-context

Add context support for editor property name i18n
This commit is contained in:
Rémi Verschelde 2024-04-04 14:30:29 +02:00
commit 84b3d14c9a
No known key found for this signature in database
GPG key ID: C3336907360768E1
11 changed files with 65 additions and 22 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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));

View file

@ -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() {

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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));