Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
68e7ffa
Implementiere bevorzugte Sprachauswahl und Datenquellen-Synonyme
Oct 15, 2025
d80ec94
Anpassung aus Analyse
Oct 15, 2025
a8b3dce
Entferne alten JSON-basierten Datenquellen-Synonym-Handler
Nov 5, 2025
8c139fb
Ermögliche Rückgabe aller möglichen Sprachoptionen in Verbindung mit …
Nov 5, 2025
15e9d9e
Removed unnecessary service definition
jbtronics Nov 9, 2025
c6ea46b
Use default translations for the NotBlank constraint
jbtronics Nov 9, 2025
0d0effb
Started refactoring ElementTypeNameGenerator
jbtronics Nov 9, 2025
92ca462
Made ElementTypeNameGenerator class readonly
jbtronics Nov 9, 2025
1ec34a2
Modified form to work properly with new datastructure
jbtronics Nov 9, 2025
88d3444
Made the form more beautiful and space saving
jbtronics Nov 9, 2025
2c55669
Made synonym form even more space saving
jbtronics Nov 9, 2025
96418db
Allow to define overrides for any element label there is
jbtronics Nov 9, 2025
e95197b
Use defined synonyms in ElementTypeNameGenerator
jbtronics Nov 9, 2025
c372109
Use ElementTypeNameGenerator where possible
jbtronics Nov 9, 2025
d0a65cb
Register synonyms for element types as global translation parameters
jbtronics Nov 11, 2025
cb27101
Revert changes done to permission layout
jbtronics Nov 11, 2025
f61ecc9
Use new synonym system for admin page titles
jbtronics Nov 11, 2025
a0a12b8
Removed now unnecessary services
jbtronics Nov 11, 2025
446f4a6
Reworked settings name and translation
jbtronics Nov 11, 2025
1234f44
Renamed all files to Synonyms
jbtronics Nov 11, 2025
230ae0f
Removed unnecessary translations
jbtronics Nov 11, 2025
ac8b119
Removed unnecessary translations
jbtronics Nov 11, 2025
1816585
Fixed duplicate check
jbtronics Nov 11, 2025
8c63444
Renamed synoynms translations
jbtronics Nov 11, 2025
551c7f7
Use our synonyms for permission translations
jbtronics Nov 11, 2025
8f2ff50
Fixed phpstan issue
jbtronics Nov 11, 2025
e49048b
Added tests
jbtronics Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions assets/controllers/pages/synonyms_collection_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
static targets = ['items'];
static values = {
prototype: String,
prototypeName: { type: String, default: '__name__' },
index: { type: Number, default: 0 },
};

connect() {
if (!this.hasIndexValue || Number.isNaN(this.indexValue)) {
this.indexValue = this.itemsTarget?.children.length || 0;
}
}

add(event) {
event.preventDefault();

const encodedProto = this.prototypeValue || '';
const placeholder = this.prototypeNameValue || '__name__';
if (!encodedProto || !this.itemsTarget) return;

const protoHtml = this._decodeHtmlAttribute(encodedProto);

const idx = this.indexValue;
const html = protoHtml.replace(new RegExp(placeholder, 'g'), String(idx));

const wrapper = document.createElement('div');
wrapper.innerHTML = html;
const newItem = wrapper.firstElementChild;
if (newItem) {
this.itemsTarget.appendChild(newItem);
this.indexValue = idx + 1;
}
}

remove(event) {
event.preventDefault();
const row = event.currentTarget.closest('.tc-item');
if (row) row.remove();
}

_decodeHtmlAttribute(str) {
const tmp = document.createElement('textarea');
tmp.innerHTML = str;
return tmp.value || tmp.textContent || '';
}
}
2 changes: 1 addition & 1 deletion config/packages/translation.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
framework:
default_locale: 'en'
# Just enable the locales we need for performance reasons.
enabled_locale: '%partdb.locale_menu%'
enabled_locale: ['en', 'de', 'it', 'fr', 'ru', 'ja', 'cs', 'da', 'zh', 'pl']
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
Expand Down
4 changes: 2 additions & 2 deletions config/packages/twig.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
twig:
default_path: '%kernel.project_dir%/templates'
form_themes: ['bootstrap_5_horizontal_layout.html.twig', 'form/extended_bootstrap_layout.html.twig', 'form/permission_layout.html.twig', 'form/filter_types_layout.html.twig']
form_themes: ['bootstrap_5_horizontal_layout.html.twig', 'form/extended_bootstrap_layout.html.twig', 'form/permission_layout.html.twig', 'form/filter_types_layout.html.twig', 'form/synonyms_collection.html.twig']

paths:
'%kernel.project_dir%/assets/css': css
Expand All @@ -20,4 +20,4 @@ twig:

when@test:
twig:
strict_variables: true
strict_variables: true
24 changes: 12 additions & 12 deletions config/permissions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co

parts: # e.g. this maps to perms_parts in User/Group database
group: "data"
label: "perm.parts"
label: "{{part}}"
operations: # Here are all possible operations are listed => the op name is mapped to bit value
read:
label: "perm.read"
Expand Down Expand Up @@ -71,7 +71,7 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co


storelocations: &PART_CONTAINING
label: "perm.storelocations"
label: "{{storage_location}}"
group: "data"
operations:
read:
Expand Down Expand Up @@ -103,39 +103,39 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co

footprints:
<<: *PART_CONTAINING
label: "perm.part.footprints"
label: "{{footprint}}"

categories:
<<: *PART_CONTAINING
label: "perm.part.categories"
label: "{{category}}"

suppliers:
<<: *PART_CONTAINING
label: "perm.part.supplier"
label: "{{supplier}}"

manufacturers:
<<: *PART_CONTAINING
label: "perm.part.manufacturers"
label: "{{manufacturer}}"

projects:
<<: *PART_CONTAINING
label: "perm.projects"
label: "{{project}}"

attachment_types:
<<: *PART_CONTAINING
label: "perm.part.attachment_types"
label: "{{attachment_type}}"

currencies:
<<: *PART_CONTAINING
label: "perm.currencies"
label: "{{currency}}"

measurement_units:
<<: *PART_CONTAINING
label: "perm.measurement_units"
label: "{{measurement_unit}}"

part_custom_states:
<<: *PART_CONTAINING
label: "perm.part_custom_states"
label: "{{part_custom_state}}"

tools:
label: "perm.part.tools"
Expand Down Expand Up @@ -377,4 +377,4 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
manage_tokens:
label: "perm.api.manage_tokens"
alsoSet: ['access_api']
apiTokenRole: ROLE_API_FULL
apiTokenRole: ROLE_API_FULL
2 changes: 1 addition & 1 deletion src/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function systemSettings(Request $request, TagAwareCacheInterface $cache):
$this->settingsManager->save($settings);

//It might be possible, that the tree settings have changed, so clear the cache
$cache->invalidateTags(['tree_treeview', 'sidebar_tree_update']);
$cache->invalidateTags(['tree_tools', 'tree_treeview', 'sidebar_tree_update', 'synonyms']);

$this->addFlash('success', t('settings.flash.saved'));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

declare(strict_types=1);


namespace App\EventListener;

use App\Services\ElementTypeNameGenerator;
use App\Services\ElementTypes;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Translation\Translator;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

#[AsEventListener]
readonly class RegisterSynonymsAsTranslationParametersListener
{
private Translator $translator;

public function __construct(
#[Autowire(service: 'translator.default')] TranslatorInterface $translator,
private TagAwareCacheInterface $cache,
private ElementTypeNameGenerator $typeNameGenerator)
{
if (!$translator instanceof Translator) {
throw new \RuntimeException('Translator must be an instance of Symfony\Component\Translation\Translator or this listener cannot be used.');
}
$this->translator = $translator;
}

public function getSynonymPlaceholders(): array
{
return $this->cache->get('partdb_synonym_placeholders', function (ItemInterface $item) {
$item->tag('synonyms');


$placeholders = [];

//Generate a placeholder for each element type
foreach (ElementTypes::cases() as $elementType) {
//We have a placeholder for singular
$placeholders['{' . $elementType->value . '}'] = $this->typeNameGenerator->typeLabel($elementType);
//We have a placeholder for plural
$placeholders['{{' . $elementType->value . '}}'] = $this->typeNameGenerator->typeLabelPlural($elementType);

//And we have lowercase versions for both
$placeholders['[' . $elementType->value . ']'] = mb_strtolower($this->typeNameGenerator->typeLabel($elementType));
$placeholders['[[' . $elementType->value . ']]'] = mb_strtolower($this->typeNameGenerator->typeLabelPlural($elementType));
}

return $placeholders;
});
}

public function __invoke(RequestEvent $event): void
{
//If we already added the parameters, skip adding them again
if (isset($this->translator->getGlobalParameters()['@@partdb_synonyms_registered@@'])) {
return;
}

//Register all placeholders for synonyms
$placeholders = $this->getSynonymPlaceholders();
foreach ($placeholders as $key => $value) {
$this->translator->addGlobalParameter($key, $value);
}

//Register the marker parameter to avoid double registration
$this->translator->addGlobalParameter('@@partdb_synonyms_registered@@', 'registered');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@
declare(strict_types=1);


namespace App\Form\Type;
namespace App\Form\Settings;

use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Form\Extension\Core\Type\LocaleType;
use Symfony\Component\Intl\Languages;
use Symfony\Component\OptionsResolver\OptionsResolver;

Expand Down
Loading
Loading