Implemented the ability to set user-defined synonyms/labels for internal element types

* Implementiere bevorzugte Sprachauswahl und Datenquellen-Synonyme

Die Spracheinstellungen/System-Settings wurden um die Möglichkeit ergänzt, bevorzugte Sprachen für die Dropdown-Menüs festzulegen. Zudem wurde ein Datenquellen-Synonymsystem implementiert, um benutzerfreundlichere Bezeichnungen anzuzeigen und zu personalisieren.

* Anpassung aus Analyse

* Entferne alten JSON-basierten Datenquellen-Synonym-Handler

Die Verwaltung der Datenquellen-Synonyme wurde überarbeitet, um ein flexibleres und strukturiertes Konzept zu ermöglichen. Der bestehende JSON-basierte Ansatz wurde durch eine neue Service-basierte Architektur ersetzt, die eine bessere Handhabung und Erweiterbarkeit erlaubt.

* Ermögliche Rückgabe aller möglichen Sprachoptionen in Verbindung mit den vom Nutzer freigeschalteten.

* Removed unnecessary service definition

The tag is applied via autoconfiguration

* Use default translations for the NotBlank constraint

* Started refactoring ElementTypeNameGenerator

* Made ElementTypeNameGenerator class readonly

* Modified form to work properly with new datastructure

* Made the form more beautiful and space saving

* Made synonym form even more space saving

* Allow to define overrides for any element label there is

* Use defined synonyms in ElementTypeNameGenerator

* Use ElementTypeNameGenerator where possible

* Register synonyms for element types as global translation parameters

* Revert changes done to permission layout

* Use new synonym system for admin page titles

* Removed now unnecessary services

* Reworked settings name and translation

* Renamed all files to Synonyms

* Removed unnecessary translations

* Removed unnecessary translations

* Fixed duplicate check

* Renamed synoynms translations

* Use our synonyms for permission translations

* Fixed phpstan issue

* Added tests

---------

Co-authored-by: Marcel Diegelmann <marcel.diegelmann@gmail.com>
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
This commit is contained in:
web-devinition.de 2025-11-12 21:35:02 +01:00 committed by GitHub
parent 5e3bd26e27
commit 54f318ecac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 1504 additions and 335 deletions

View file

@ -15,4 +15,4 @@
{% block new_title %}
{% trans %}attachment_type.new{% endtrans %}
{% endblock %}
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-tags fa-fw"></i> {% trans %}category.labelp{% endtrans %}
<i class="fas fa-tags fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block additional_pills %}
@ -61,4 +61,4 @@
</div>
{{ form_row(form.eda_info.kicad_symbol) }}
</div>
{% endblock %}
{% endblock %}

View file

@ -3,7 +3,7 @@
{% import "vars.macro.twig" as vars %}
{% block card_title %}
<i class="fa-solid fa-coins"></i> {% trans %}currency.caption{% endtrans %}
<i class="fa-solid fa-coins"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block additional_controls %}
@ -41,4 +41,4 @@
{% block new_title %}
{% trans %}currency.new{% endtrans %}
{% endblock %}
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-microchip fa-fw"></i> {% trans %}footprint.labelp{% endtrans %}
<i class="fas fa-microchip fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block master_picture_block %}
@ -34,4 +34,4 @@
</div>
{{ form_row(form.eda_info.kicad_footprint) }}
</div>
{% endblock %}
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-users fa-fw"></i> {% trans %}group.edit.caption{% endtrans %}
<i class="fas fa-users fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
@ -27,4 +27,4 @@
{% block new_title %}
{% trans %}group.new{% endtrans %}
{% endblock %}
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-qrcode fa-fw"></i> {% trans %}label_profile.caption{% endtrans %}
<i class="fas fa-qrcode fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block additional_pills %}
@ -58,4 +58,4 @@
{% block new_title %}
{% trans %}label_profile.new{% endtrans %}
{% endblock %}
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_company_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-industry fa-fw"></i> {% trans %}manufacturer.caption{% endtrans %}
<i class="fas fa-industry fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block edit_title %}
@ -10,4 +10,4 @@
{% block new_title %}
{% trans %}manufacturer.new{% endtrans %}
{% endblock %}
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-balance-scale fa-fw"></i> {% trans %}measurement_unit.caption{% endtrans %}
<i class="fas fa-balance-scale fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block edit_title %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-balance-scale fa-tools"></i> {% trans %}part_custom_state.caption{% endtrans %}
<i class="fas fa-balance-scale fa-tools"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block edit_title %}

View file

@ -3,7 +3,7 @@
{# @var entity App\Entity\ProjectSystem\Project #}
{% block card_title %}
<i class="fas fa-archive fa-fw"></i> {% trans %}project.caption{% endtrans %}
<i class="fas fa-archive fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block edit_title %}
@ -59,4 +59,4 @@
</a>
{% endif %}
</div>
{% endblock %}
{% endblock %}

View file

@ -2,7 +2,7 @@
{% import "label_system/dropdown_macro.html.twig" as dropdown %}
{% block card_title %}
<i class="fas fa-cube fa-fw"></i> {% trans %}storelocation.labelp{% endtrans %}
<i class="fas fa-cube fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block additional_controls %}
@ -38,4 +38,4 @@
{% block new_title %}
{% trans %}storelocation.new{% endtrans %}
{% endblock %}
{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "admin/base_company_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-truck fa-fw"></i> {% trans %}supplier.caption{% endtrans %}
<i class="fas fa-truck fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block additional_panes %}
@ -19,4 +19,4 @@
{% block new_title %}
{% trans %}supplier.new{% endtrans %}
{% endblock %}
{% endblock %}

View file

@ -5,7 +5,7 @@
{# @var entity \App\Entity\UserSystem\User #}
{% block card_title %}
<i class="fas fa-user fa-fw"></i> {% trans %}user.edit.caption{% endtrans %}
<i class="fas fa-user fa-fw"></i> {{ type_label_p(entity) }}
{% endblock %}
{% block comment %}{% endblock %}
@ -111,4 +111,4 @@
{% block preview_picture %}
<img src="{{ avatar_helper.avatarURL(entity) }}" style="height: 50px;">
{% endblock %}
{% endblock %}

View file

@ -1,13 +1,15 @@
{% macro sidebar_dropdown() %}
{% set currentLocale = app.request.locale %}
{# Format is [mode, route, label, show_condition] #}
{% set data_sources = [
['categories', path('tree_category_root'), 'category.labelp', is_granted('@categories.read') and is_granted('@parts.read')],
['locations', path('tree_location_root'), 'storelocation.labelp', is_granted('@storelocations.read') and is_granted('@parts.read')],
['footprints', path('tree_footprint_root'), 'footprint.labelp', is_granted('@footprints.read') and is_granted('@parts.read')],
['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read')],
['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read')],
['projects', path('tree_device_root'), 'project.labelp', is_granted('@projects.read')],
['tools', path('tree_tools'), 'tools.label', true],
['categories', path('tree_category_root'), '@category@@', is_granted('@categories.read') and is_granted('@parts.read')],
['locations', path('tree_location_root'), '@storage_location@@', is_granted('@storelocations.read') and is_granted('@parts.read'), ],
['footprints', path('tree_footprint_root'), '@footprint@@', is_granted('@footprints.read') and is_granted('@parts.read')],
['manufacturers', path('tree_manufacturer_root'), '@manufacturer@@', is_granted('@manufacturers.read') and is_granted('@parts.read'), 'manufacturer'],
['suppliers', path('tree_supplier_root'), '@supplier@@', is_granted('@suppliers.read') and is_granted('@parts.read'), 'supplier'],
['projects', path('tree_device_root'), '@project@@', is_granted('@projects.read'), 'project'],
['tools', path('tree_tools'), 'tools.label', true, 'tool'],
] %}
<li class="dropdown-header">{% trans %}actions{% endtrans %}</li>
@ -18,9 +20,20 @@
{% for source in data_sources %}
{% if source[3] %} {# show_condition #}
<li><button class="tree-btns dropdown-item" data-mode="{{ source[0] }}" data-url="{{ source[1] }}" data-text="{{ source[2] | trans }}"
{{ stimulus_action('elements/sidebar_tree', 'changeDataSource') }}
>{{ source[2] | trans }}</button></li>
<li>
{% if source[2] starts with '@' %}
{% set label = type_label_p(source[2]|replace({'@': ''})) %}
{% else %}
{% set label = source[2]|trans %}
{% endif %}
<button class="tree-btns dropdown-item"
data-mode="{{ source[0] }}"
data-url="{{ source[1] }}"
data-text="{{ label }}"
{{ stimulus_action('elements/sidebar_tree', 'changeDataSource') }}
>{{ label }}</button>
</li>
{% endif %}
{% endfor %}
{% endmacro %}
@ -61,4 +74,4 @@
<div class="treeview-sm mt-2" {{ stimulus_target('elements/tree', 'tree') }}></div>
</div>
{% endmacro %}
{% endmacro %}

View file

@ -0,0 +1,59 @@
{% macro renderForm(child) %}
<div class="tc-item mt-1 px-2 pb-1 border-bottom">
{% form_theme child "form/vertical_bootstrap_layout.html.twig" %}
<div class="row">
<div class="col">{{ form_row(child.dataSource) }}</div>
<div class="col">{{ form_row(child.locale) }}</div>
<div class="col">{{ form_row(child.translation_singular) }}</div>
<div class="col">{{ form_row(child.translation_plural) }}</div>
<div class="col">
<button type="button" class="btn btn-outline-danger btn-sm tc-remove" {{ stimulus_action('pages/synonyms_collection', 'remove' )}}>
<i class="fa fa-trash"></i> {{ 'settings.synonyms.type_synonym.remove_entry'|trans }}
</button>
</div>
</div>
</div>
{% endmacro %}
{% block type_synonyms_collection_widget %}
{% set _attrs = attr|default({}) %}
{% set _attrs = _attrs|merge({
class: (_attrs.class|default('') ~ ' type_synonyms_collection-widget')|trim
}) %}
{% set has_proto = prototype is defined %}
{% if has_proto %}
{% set __proto %}
{{- _self.renderForm(prototype) -}}
{% endset %}
{% set _proto_html = __proto|e('html_attr') %}
{% set _proto_name = form.vars.prototype_name|default('__name__') %}
{% set _index = form|length %}
{% endif %}
<div
{{ stimulus_controller('pages/synonyms_collection', {
prototype: has_proto ? _proto_html : '',
prototypeName: has_proto ? _proto_name : '__name__',
index: has_proto ? _index : (form|length)
}) }}
{{ block('widget_container_attributes')|raw }}{% for k,v in _attrs %} {{ k }}="{{ v }}"{% endfor %}
>
<div class="row">
<div class="col text-center"><strong>{% trans%}settings.synonyms.type_synonym.type{% endtrans%}</strong></div>
<div class="col text-center"><strong>{% trans%}settings.synonyms.type_synonym.language{% endtrans%}</strong></div>
<div class="col text-center"><strong>{% trans%}settings.synonyms.type_synonym.translation_singular{% endtrans%}</strong></div>
<div class="col text-center"><strong>{% trans%}settings.synonyms.type_synonym.translation_plural{% endtrans%}</strong></div>
<div class="col text-center"></div>
</div>
<div class="tc-items" {{ stimulus_target('pages/synonyms_collection', 'items') }}>
{% for child in form %}
{{ _self.renderForm(child) }}
{% endfor %}
</div>
<button type="button" class="btn btn-outline-primary btn-sm mt-2 tc-add" {{ stimulus_action('pages/synonyms_collection', 'add')}}>
<i class="fa fa-plus"></i> {{ 'settings.synonyms.type_synonym.add_entry'|trans }}
</button>
</div>
{% endblock %}

View file

@ -0,0 +1,26 @@
{% extends 'bootstrap_5_layout.html.twig' %}
{%- block choice_widget_collapsed -%}
{# Only add the BS5 form-select class if we dont use bootstrap-selectpicker #}
{# {% if attr["data-controller"] is defined and attr["data-controller"] not in ["elements--selectpicker"] %}
{%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-select')|trim}) -%}
{% else %}
{# If it is an selectpicker add form-control class to fill whole width
{%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) -%}
{% endif %}
#}
{%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-select')|trim}) -%}
{# If no data-controller was explictly defined add data-controller=elements--select #}
{% if attr["data-controller"] is not defined %}
{%- set attr = attr|merge({"data-controller": "elements--select"}) -%}
{% if attr["data-empty-message"] is not defined %}
{%- set attr = attr|merge({"data-empty-message": ("selectpicker.nothing_selected"|trans)}) -%}
{% endif %}
{% endif %}
{{- block("choice_widget_collapsed", "bootstrap_base_layout.html.twig") -}}
{%- endblock choice_widget_collapsed -%}

View file

@ -36,7 +36,7 @@
{% for section_widget in tab_widget %}
{% set settings_object = section_widget.vars.value %}
{% if section_widget.vars.compound ?? false %}
{% if section_widget.vars.embedded_settings_metadata is defined %} {# Check if we have nested embedded settings or not #}
<fieldset>
<legend class="offset-3">
<i class="fa-solid {{ settings_icon(settings_object)|default('fa-sliders') }} fa-fw"></i>