diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml index cbc1cd7e..a3f529e3 100644 --- a/config/packages/translation.yaml +++ b/config/packages/translation.yaml @@ -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: diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 674aa317..78956026 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -7,7 +7,7 @@ twig: globals: allow_email_pw_reset: '%partdb.users.email_pw_reset%' - locale_menu: '%partdb.locale_menu%' + location_settings: '@App\Settings\SystemSettings\LocalizationSettings' attachment_manager: '@App\Services\Attachments\AttachmentManager' label_profile_dropdown_helper: '@App\Services\LabelSystem\LabelProfileDropdownHelper' error_page_admin_email: '%partdb.error_pages.admin_email%' @@ -20,4 +20,4 @@ twig: when@test: twig: - strict_variables: true \ No newline at end of file + strict_variables: true diff --git a/config/parameters.yaml b/config/parameters.yaml index ec80e052..916d9755 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -8,7 +8,6 @@ parameters: # This is used as workaround for places where we can not access the settings directly (like the 2FA application names) partdb.title: '%env(string:settings:customization:instanceName)%' # The title shown inside of Part-DB (e.g. in the navbar and on homepage) - partdb.locale_menu: ['en', 'de', 'it', 'fr', 'ru', 'ja', 'cs', 'da', 'zh', 'pl'] # The languages that are shown in user drop down menu partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails diff --git a/config/services.yaml b/config/services.yaml index c701ff9e..fb658488 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -190,6 +190,13 @@ services: $fontDirectory: '%kernel.project_dir%/var/dompdf/fonts/' $tmpDirectory: '%kernel.project_dir%/var/dompdf/tmp/' + #################################################################################################################### + # Twig Extensions + #################################################################################################################### + + App\Twig\DataSourceNameExtension: + tags: [ 'twig.extension' ] + #################################################################################################################### # Part info provider system #################################################################################################################### diff --git a/docs/configuration.md b/docs/configuration.md index e4eed141..e6279981 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -281,8 +281,6 @@ command `bin/console cache:clear`. The following options are available: -* `partdb.locale_menu`: The codes of the languages, which should be shown in the language chooser menu (the one with the - user icon in the navbar). The first language in the list will be the default language. * `partdb.gdpr_compliance`: When set to true (default value), IP addresses which are saved in the database will be anonymized, by removing the last byte of the IP. This is required by the GDPR (General Data Protection Regulation) in the EU. diff --git a/src/Controller/ToolsController.php b/src/Controller/ToolsController.php index d78aff62..5d353615 100644 --- a/src/Controller/ToolsController.php +++ b/src/Controller/ToolsController.php @@ -61,7 +61,7 @@ class ToolsController extends AbstractController 'default_timezone' => $settings->system->localization->timezone, 'default_currency' => $settings->system->localization->baseCurrency, 'default_theme' => $settings->system->customization->theme, - 'enabled_locales' => $this->getParameter('partdb.locale_menu'), + 'enabled_locales' => array_column($settings->system->localization->preferredLanguages, 'value'), 'demo_mode' => $this->getParameter('partdb.demo_mode'), 'use_gravatar' => $settings->system->privacy->useGravatar, 'gdpr_compliance' => $this->getParameter('partdb.gdpr_compliance'), diff --git a/src/Form/Type/DataSourceJsonType.php b/src/Form/Type/DataSourceJsonType.php new file mode 100644 index 00000000..6d11058a --- /dev/null +++ b/src/Form/Type/DataSourceJsonType.php @@ -0,0 +1,103 @@ +settings->dataSourceSynonyms; + } + + foreach ($dataSources as $key => $label) { + $initialData = $existingData[$key] ?? $defaultValues[$key] ?? '{}'; + + $builder->add($key, TextareaType::class, [ + 'label' => $label, + 'required' => false, + 'data' => $initialData, + 'attr' => [ + 'rows' => 3, + 'style' => 'font-family: monospace;', + 'placeholder' => sprintf('%s translations in JSON format', ucfirst($key)), + ], + 'constraints' => [ + new Assert\Callback(function ($value, $context) { + if ($value && !static::isValidJson($value)) { + $context->buildViolation('The field must contain valid JSON.')->addViolation(); + } + }), + ], + ]); + } + + $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) use ($defaultValues) { + $data = $event->getData(); + + if (!$data) { + $event->setData($defaultValues); + return; + } + + foreach ($defaultValues as $key => $defaultValue) { + if (empty($data[$key])) { + $data[$key] = $defaultValue; + } else { + $decodedValue = json_decode($data[$key], true); + if (json_last_error() === JSON_ERROR_NONE) { + $data[$key] = json_encode($decodedValue, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + } + } + } + + $event->setData($data); + }); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_sources' => [], + 'default_values' => [], + ]); + + $resolver->setAllowedTypes('data_sources', 'array'); + $resolver->setAllowedTypes('default_values', 'array'); + } + + /** + * Validates if a string is a valid JSON format. + * + * @param string $json + * @return bool + */ + public static function isValidJson(string $json): bool + { + json_decode($json); + return json_last_error() === JSON_ERROR_NONE; + } +} diff --git a/src/Form/Type/LocaleSelectType.php b/src/Form/Type/LocaleSelectType.php index d47fb57f..b87932d1 100644 --- a/src/Form/Type/LocaleSelectType.php +++ b/src/Form/Type/LocaleSelectType.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace App\Form\Type; -use Symfony\Component\DependencyInjection\Attribute\Autowire; +use App\Settings\SystemSettings\LocalizationSettings; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\LocaleType; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -35,7 +35,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class LocaleSelectType extends AbstractType { - public function __construct(#[Autowire(param: 'partdb.locale_menu')] private readonly array $preferred_languages) + public function __construct(private LocalizationSettings $localizationSetting) { } @@ -47,7 +47,7 @@ class LocaleSelectType extends AbstractType public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'preferred_choices' => $this->preferred_languages, + 'preferred_choices' => array_column($this->localizationSetting->preferredLanguages, 'value'), ]); } } diff --git a/src/Services/Trees/ToolsTreeBuilder.php b/src/Services/Trees/ToolsTreeBuilder.php index f454804f..843aa8f7 100644 --- a/src/Services/Trees/ToolsTreeBuilder.php +++ b/src/Services/Trees/ToolsTreeBuilder.php @@ -39,6 +39,7 @@ use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; use App\Helpers\Trees\TreeViewNode; use App\Services\Cache\UserCacheKeyGenerator; +use App\Settings\BehaviorSettings\DataSourceSynonymsSettings; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Cache\ItemInterface; @@ -51,8 +52,14 @@ use Symfony\Contracts\Translation\TranslatorInterface; */ class ToolsTreeBuilder { - public function __construct(protected TranslatorInterface $translator, protected UrlGeneratorInterface $urlGenerator, protected TagAwareCacheInterface $cache, protected UserCacheKeyGenerator $keyGenerator, protected Security $security) - { + public function __construct( + protected TranslatorInterface $translator, + protected UrlGeneratorInterface $urlGenerator, + protected TagAwareCacheInterface $cache, + protected UserCacheKeyGenerator $keyGenerator, + protected Security $security, + protected DataSourceSynonymsSettings $dataSourceSynonymsSettings, + ) { } /** @@ -167,13 +174,13 @@ class ToolsTreeBuilder } if ($this->security->isGranted('read', new Category())) { $nodes[] = (new TreeViewNode( - $this->translator->trans('tree.tools.edit.categories'), + $this->getTranslatedDataSourceOrSynonym('category', 'tree.tools.edit.categories', $this->translator->getLocale()), $this->urlGenerator->generate('category_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-tags'); } if ($this->security->isGranted('read', new Project())) { $nodes[] = (new TreeViewNode( - $this->translator->trans('tree.tools.edit.projects'), + $this->getTranslatedDataSourceOrSynonym('project', 'tree.tools.edit.projects', $this->translator->getLocale()), $this->urlGenerator->generate('project_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-archive'); } @@ -185,25 +192,25 @@ class ToolsTreeBuilder } if ($this->security->isGranted('read', new Supplier())) { $nodes[] = (new TreeViewNode( - $this->translator->trans('tree.tools.edit.suppliers'), + $this->getTranslatedDataSourceOrSynonym('supplier', 'tree.tools.edit.suppliers', $this->translator->getLocale()), $this->urlGenerator->generate('supplier_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-truck'); } if ($this->security->isGranted('read', new Manufacturer())) { $nodes[] = (new TreeViewNode( - $this->translator->trans('tree.tools.edit.manufacturer'), + $this->getTranslatedDataSourceOrSynonym('manufacturer', 'tree.tools.edit.manufacturer', $this->translator->getLocale()), $this->urlGenerator->generate('manufacturer_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-industry'); } if ($this->security->isGranted('read', new StorageLocation())) { $nodes[] = (new TreeViewNode( - $this->translator->trans('tree.tools.edit.storelocation'), + $this->getTranslatedDataSourceOrSynonym('storagelocation', 'tree.tools.edit.storelocation', $this->translator->getLocale()), $this->urlGenerator->generate('store_location_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-cube'); } if ($this->security->isGranted('read', new Footprint())) { $nodes[] = (new TreeViewNode( - $this->translator->trans('tree.tools.edit.footprint'), + $this->getTranslatedDataSourceOrSynonym('footprint', 'tree.tools.edit.footprint', $this->translator->getLocale()), $this->urlGenerator->generate('footprint_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-microchip'); } @@ -317,4 +324,24 @@ class ToolsTreeBuilder return $nodes; } + + protected function getTranslatedDataSourceOrSynonym(string $dataSource, string $translationKey, string $locale): string + { + $currentTranslation = $this->translator->trans($translationKey); + + $synonyms = $this->dataSourceSynonymsSettings->getSynonymsAsArray(); + + // Call alternatives from DataSourcesynonyms (if available) + if (!empty($synonyms[$dataSource][$locale])) { + $alternativeTranslation = $synonyms[$dataSource][$locale]; + + // Use alternative translation when it deviates from the standard translation + if ($alternativeTranslation !== $currentTranslation) { + return $alternativeTranslation; + } + } + + // Otherwise return the standard translation + return $currentTranslation; + } } diff --git a/src/Services/Trees/TreeViewGenerator.php b/src/Services/Trees/TreeViewGenerator.php index 3a097902..9f26b26a 100644 --- a/src/Services/Trees/TreeViewGenerator.php +++ b/src/Services/Trees/TreeViewGenerator.php @@ -39,6 +39,7 @@ use App\Repository\StructuralDBElementRepository; use App\Services\Cache\ElementCacheTagGenerator; use App\Services\Cache\UserCacheKeyGenerator; use App\Services\EntityURLGenerator; +use App\Settings\BehaviorSettings\DataSourceSynonymsSettings; use App\Settings\BehaviorSettings\SidebarSettings; use Doctrine\ORM\EntityManagerInterface; use InvalidArgumentException; @@ -68,6 +69,7 @@ class TreeViewGenerator protected TranslatorInterface $translator, private readonly UrlGeneratorInterface $router, private readonly SidebarSettings $sidebarSettings, + protected DataSourceSynonymsSettings $dataSourceSynonymsSettings, ) { $this->rootNodeEnabled = $this->sidebarSettings->rootNodeEnabled; $this->rootNodeExpandedByDefault = $this->sidebarSettings->rootNodeExpanded; @@ -226,14 +228,15 @@ class TreeViewGenerator protected function entityClassToRootNodeString(string $class): string { + $locale = $this->translator->getLocale(); + return match ($class) { - Category::class => $this->translator->trans('category.labelp'), - StorageLocation::class => $this->translator->trans('storelocation.labelp'), - Footprint::class => $this->translator->trans('footprint.labelp'), - Manufacturer::class => $this->translator->trans('manufacturer.labelp'), - Supplier::class => $this->translator->trans('supplier.labelp'), - Project::class => $this->translator->trans('project.labelp'), - Assembly::class => $this->translator->trans('assembly.labelp'), + Category::class => $this->getTranslatedOrSynonym('category', $locale), + StorageLocation::class => $this->getTranslatedOrSynonym('storelocation', $locale), + Footprint::class => $this->getTranslatedOrSynonym('footprint', $locale), + Manufacturer::class => $this->getTranslatedOrSynonym('manufacturer', $locale), + Supplier::class => $this->getTranslatedOrSynonym('supplier', $locale), + Project::class => $this->getTranslatedOrSynonym('project', $locale), default => $this->translator->trans('tree.root_node.text'), }; } @@ -290,4 +293,24 @@ class TreeViewGenerator return $repo->getGenericNodeTree($parent); //@phpstan-ignore-line }); } + + protected function getTranslatedOrSynonym(string $key, string $locale): string + { + $currentTranslation = $this->translator->trans($key . '.labelp'); + + $synonyms = $this->dataSourceSynonymsSettings->getSynonymsAsArray(); + + // Call alternatives from DataSourcesynonyms (if available) + if (!empty($synonyms[$key][$locale])) { + $alternativeTranslation = $synonyms[$key][$locale]; + + // Use alternative translation when it deviates from the standard translation + if ($alternativeTranslation !== $currentTranslation) { + return $alternativeTranslation; + } + } + + // Otherwise return the standard translation + return $currentTranslation; + } } diff --git a/src/Settings/BehaviorSettings/DataSourceSynonymsSettings.php b/src/Settings/BehaviorSettings/DataSourceSynonymsSettings.php new file mode 100644 index 00000000..74b9a2a1 --- /dev/null +++ b/src/Settings/BehaviorSettings/DataSourceSynonymsSettings.php @@ -0,0 +1,73 @@ + '{"en":"", "de":""}']), + options: ['type' => StringType::class], + formType: DataSourceJsonType::class, + formOptions: [ + 'required' => false, + 'data_sources' => [ + 'category' => new TM("settings.behavior.data_source_synonyms.category"), + 'storagelocation' => new TM("settings.behavior.data_source_synonyms.storagelocation"), + 'footprint' => new TM("settings.behavior.data_source_synonyms.footprint"), + 'manufacturer' => new TM("settings.behavior.data_source_synonyms.manufacturer"), + 'supplier' => new TM("settings.behavior.data_source_synonyms.supplier"), + 'project' => new TM("settings.behavior.data_source_synonyms.project"), + ], + 'default_values' => [ + 'category' => '{"en":"Categories", "de":"Kategorien"}', + 'storagelocation' => '{"en":"Storage locations", "de":"Lagerorte"}', + 'footprint' => '{"en":"Footprints", "de":"Footprints"}', + 'manufacturer' => '{"en":"Manufacturers", "de":"Hersteller"}', + 'supplier' => '{"en":"Suppliers", "de":"Lieferanten"}', + 'project' => '{"en":"Projects", "de":"Projekte"}', + ], + ], + )] + #[Assert\Type('array')] + public array $dataSourceSynonyms = [ + 'category' => '{"en":"Categories", "de":"Kategorien"}', + 'storagelocation' => '{"en":"Storage locations", "de":"Lagerorte"}', + 'footprint' => '{"en":"Footprints", "de":"Footprints"}', + 'manufacturer' => '{"en":"Manufacturers", "de":"Hersteller"}', + 'supplier' => '{"en":"Suppliers", "de":"Lieferanten"}', + 'project' => '{"en":"Projects", "de":"Projekte"}', + ]; + + /** + * Get the synonyms data as a structured array. + * + * @return array> The data source synonyms parsed from JSON to array. + */ + public function getSynonymsAsArray(): array + { + $result = []; + foreach ($this->dataSourceSynonyms as $key => $jsonString) { + $result[$key] = json_decode($jsonString, true, 512, JSON_THROW_ON_ERROR) ?? []; + } + + return $result; + } + +} diff --git a/src/Settings/SystemSettings.php b/src/Settings/SystemSettings.php index 83d00afc..a81f5871 100644 --- a/src/Settings/SystemSettings.php +++ b/src/Settings/SystemSettings.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace App\Settings; +use App\Settings\BehaviorSettings\DataSourceSynonymsSettings; use App\Settings\SystemSettings\AttachmentsSettings; use App\Settings\SystemSettings\CustomizationSettings; use App\Settings\SystemSettings\HistorySettings; @@ -37,6 +38,9 @@ class SystemSettings #[EmbeddedSettings()] public ?LocalizationSettings $localization = null; + #[EmbeddedSettings] + public ?DataSourceSynonymsSettings $dataSourceSynonyms = null; + #[EmbeddedSettings()] public ?CustomizationSettings $customization = null; @@ -48,4 +52,4 @@ class SystemSettings #[EmbeddedSettings()] public ?HistorySettings $history = null; -} \ No newline at end of file +} diff --git a/src/Settings/SystemSettings/LocalizationSettings.php b/src/Settings/SystemSettings/LocalizationSettings.php index 434a4e69..bc52f488 100644 --- a/src/Settings/SystemSettings/LocalizationSettings.php +++ b/src/Settings/SystemSettings/LocalizationSettings.php @@ -26,6 +26,8 @@ namespace App\Settings\SystemSettings; use App\Form\Type\LocaleSelectType; use App\Settings\SettingsIcon; use Jbtronics\SettingsBundle\Metadata\EnvVarMode; +use Jbtronics\SettingsBundle\ParameterTypes\ArrayType; +use Jbtronics\SettingsBundle\ParameterTypes\EnumType; use Jbtronics\SettingsBundle\Settings\Settings; use Jbtronics\SettingsBundle\Settings\SettingsParameter; use Jbtronics\SettingsBundle\Settings\SettingsTrait; @@ -60,4 +62,19 @@ class LocalizationSettings envVar: "string:BASE_CURRENCY", envVarMode: EnvVarMode::OVERWRITE )] public string $baseCurrency = 'EUR'; -} \ No newline at end of file + + /** @var PreferredLocales[] */ + #[SettingsParameter(ArrayType::class, + label: new TM("settings.system.localization.preferred_languages"), + description: new TM("settings.system.localization.preferred_languages.help"), + options: ['type' => EnumType::class, 'options' => ['class' => PreferredLocales::class]], + formType: \Symfony\Component\Form\Extension\Core\Type\EnumType::class, + formOptions: ['class' => PreferredLocales::class, 'multiple' => true, 'ordered' => true] + )] + #[Assert\NotBlank()] + #[Assert\Unique()] + #[Assert\All([new Assert\Type(PreferredLocales::class)])] + public array $preferredLanguages = [PreferredLocales::EN, PreferredLocales::DE, + PreferredLocales::IT, PreferredLocales::FR, PreferredLocales::RU, PreferredLocales::JA, + PreferredLocales::CS, PreferredLocales::DA, PreferredLocales::ZH, PreferredLocales::PL]; +} diff --git a/src/Settings/SystemSettings/PreferredLocales.php b/src/Settings/SystemSettings/PreferredLocales.php new file mode 100644 index 00000000..1fe38a54 --- /dev/null +++ b/src/Settings/SystemSettings/PreferredLocales.php @@ -0,0 +1,37 @@ +. + */ + +declare(strict_types=1); + +namespace App\Settings\SystemSettings; + +enum PreferredLocales: string +{ + case EN = 'en'; + case DE = 'de'; + case IT = 'it'; + case FR = 'fr'; + case RU = 'ru'; + case JA = 'ja'; + case CS = 'cs'; + case DA = 'da'; + case ZH = 'zh'; + case PL = 'pl'; +} diff --git a/src/Twig/DataSourceNameExtension.php b/src/Twig/DataSourceNameExtension.php new file mode 100644 index 00000000..d0d8b4b5 --- /dev/null +++ b/src/Twig/DataSourceNameExtension.php @@ -0,0 +1,43 @@ +translator = $translator; + $this->dataSourceSynonyms = $dataSourceSynonymsSettings->getSynonymsAsArray(); + } + + public function getFunctions(): array + { + return [ + new TwigFunction('get_data_source_name', [$this, 'getDataSourceName']), + ]; + } + + /** + * Based on the locale and data source names, gives the right synonym value back or the default translator value. + */ + public function getDataSourceName(string $dataSourceName, string $defaultKey): string + { + $locale = $this->translator->getLocale(); + + // Use alternative dataSource synonym (if available) + if (isset($this->dataSourceSynonyms[$dataSourceName][$locale])) { + return $this->dataSourceSynonyms[$dataSourceName][$locale]; + } + + // Otherwise return the standard translation + return $this->translator->trans($defaultKey); + } +} diff --git a/templates/_turbo_control.html.twig b/templates/_turbo_control.html.twig index 4c178038..46637587 100644 --- a/templates/_turbo_control.html.twig +++ b/templates/_turbo_control.html.twig @@ -22,9 +22,9 @@
- {% for locale in locale_menu %} + {% for locale in location_settings.preferredLanguages %} - {{ locale|language_name }} ({{ locale|upper }}) + app.request.query.all|merge(app.request.attributes.get('_route_params'))|merge({'_locale': locale.value})) }}"> + {{ locale.value|language_name }} ({{ locale.value|upper }}) {% endfor %} -
\ No newline at end of file + diff --git a/templates/admin/category_admin.html.twig b/templates/admin/category_admin.html.twig index f77188c7..569cd2eb 100644 --- a/templates/admin/category_admin.html.twig +++ b/templates/admin/category_admin.html.twig @@ -1,7 +1,9 @@ {% extends "admin/base_admin.html.twig" %} {% block card_title %} - {% trans %}category.labelp{% endtrans %} + {% set dataSourceName = get_data_source_name('category', 'category.labelp') %} + {% set translatedSource = 'category.labelp'|trans %} + {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %} {% endblock %} {% block additional_pills %} diff --git a/templates/admin/footprint_admin.html.twig b/templates/admin/footprint_admin.html.twig index bdbf83cb..3099b207 100644 --- a/templates/admin/footprint_admin.html.twig +++ b/templates/admin/footprint_admin.html.twig @@ -1,7 +1,9 @@ {% extends "admin/base_admin.html.twig" %} {% block card_title %} - {% trans %}footprint.labelp{% endtrans %} + {% set dataSourceName = get_data_source_name('footprint', 'footprint.labelp') %} + {% set translatedSource = 'footprint.labelp'|trans %} + {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %} {% endblock %} {% block master_picture_block %} diff --git a/templates/admin/manufacturer_admin.html.twig b/templates/admin/manufacturer_admin.html.twig index 8555d5e4..df597487 100644 --- a/templates/admin/manufacturer_admin.html.twig +++ b/templates/admin/manufacturer_admin.html.twig @@ -1,7 +1,9 @@ {% extends "admin/base_company_admin.html.twig" %} {% block card_title %} - {% trans %}manufacturer.caption{% endtrans %} + {% set dataSourceName = get_data_source_name('manufacturer', 'manufacturer.caption') %} + {% set translatedSource = 'manufacturer.caption'|trans %} + {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %} {% endblock %} {% block edit_title %} diff --git a/templates/admin/project_admin.html.twig b/templates/admin/project_admin.html.twig index e12e58ae..c4174bc1 100644 --- a/templates/admin/project_admin.html.twig +++ b/templates/admin/project_admin.html.twig @@ -3,7 +3,9 @@ {# @var entity App\Entity\ProjectSystem\Project #} {% block card_title %} - {% trans %}project.caption{% endtrans %} + {% set dataSourceName = get_data_source_name('project', 'project.caption') %} + {% set translatedSource = 'project.caption'|trans %} + {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %} {% endblock %} {% block edit_title %} diff --git a/templates/admin/storelocation_admin.html.twig b/templates/admin/storelocation_admin.html.twig index 706538f2..43779e31 100644 --- a/templates/admin/storelocation_admin.html.twig +++ b/templates/admin/storelocation_admin.html.twig @@ -2,7 +2,9 @@ {% import "label_system/dropdown_macro.html.twig" as dropdown %} {% block card_title %} - {% trans %}storelocation.labelp{% endtrans %} + {% set dataSourceName = get_data_source_name('storagelocation', 'storelocation.labelp') %} + {% set translatedSource = 'storelocation.labelp'|trans %} + {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %} {% endblock %} {% block additional_controls %} diff --git a/templates/admin/supplier_admin.html.twig b/templates/admin/supplier_admin.html.twig index 598f4c27..1898fdf3 100644 --- a/templates/admin/supplier_admin.html.twig +++ b/templates/admin/supplier_admin.html.twig @@ -1,7 +1,9 @@ {% extends "admin/base_company_admin.html.twig" %} {% block card_title %} - {% trans %}supplier.caption{% endtrans %} + {% set dataSourceName = get_data_source_name('supplier', 'supplier.caption') %} + {% set translatedSource = 'supplier.caption'|trans %} + {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %} {% endblock %} {% block additional_panes %} diff --git a/templates/components/tree_macros.html.twig b/templates/components/tree_macros.html.twig index acc5090f..e82cd3b4 100644 --- a/templates/components/tree_macros.html.twig +++ b/templates/components/tree_macros.html.twig @@ -1,14 +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')], - ['assembly', path('tree_assembly_root'), 'assembly.labelp', is_granted('@assemblies.read')], - ['tools', path('tree_tools'), 'tools.label', true], + ['categories', path('tree_category_root'), 'category.labelp', is_granted('@categories.read') and is_granted('@parts.read'), 'category'], + ['locations', path('tree_location_root'), 'storelocation.labelp', is_granted('@storelocations.read') and is_granted('@parts.read'), 'storagelocation'], + ['footprints', path('tree_footprint_root'), 'footprint.labelp', is_granted('@footprints.read') and is_granted('@parts.read'), 'footprint'], + ['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read'), 'manufacturer'], + ['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read'), 'supplier'], + ['projects', path('tree_device_root'), 'project.labelp', is_granted('@projects.read'), 'project'], + ['tools', path('tree_tools'), 'tools.label', true, 'tool'], ] %} @@ -19,9 +20,9 @@ {% for source in data_sources %} {% if source[3] %} {# show_condition #} -
  • + >{{ get_data_source_name(source[4], source[2]) }} {% endif %} {% endfor %} {% endmacro %} diff --git a/templates/form/permission_layout.html.twig b/templates/form/permission_layout.html.twig index 45525d02..3c7f799e 100644 --- a/templates/form/permission_layout.html.twig +++ b/templates/form/permission_layout.html.twig @@ -6,12 +6,34 @@
    {% else %} - {{ form.vars.label | trans }} + def{{ form.vars.label | trans }} {% endif %} diff --git a/translations/messages.cs.xlf b/translations/messages.cs.xlf index 2eb08383..854f7aac 100644 --- a/translations/messages.cs.xlf +++ b/translations/messages.cs.xlf @@ -13186,6 +13186,72 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz <b>Upozorňujeme, že při změně této hodnoty nedochází k převodu měn. Změna výchozí měny po přidání informací o cenách tedy povede k nesprávným cenám!</b> + + + settings.system.localization.preferred_languages + Preferované jazyky + + + + + settings.system.localization.preferred_languages.help + Jazyky, které se zobrazují v uživatelské rozbalovací nabídce + + + + + settings.system.data_source_synonyms + Synonyma zdrojů dat + + + + + settings.system.data_source_synonyms.configuration + Zdroj + + + + + settings.system.data_source_synonyms.configuration.help + Definujte vlastní synonyma pro dané zdroje dat. Očekává se formát JSON s vašimi preferovanými jazykovými ISO kódy. Příklad: %format%. + + + + + settings.behavior.data_source_synonyms.category + Kategorie + + + + + settings.behavior.data_source_synonyms.storagelocation + Skladové umístění + + + + + settings.behavior.data_source_synonyms.footprint + Pouzdro + + + + + settings.behavior.data_source_synonyms.manufacturer + Výrobce + + + + + settings.behavior.data_source_synonyms.supplier + Dodavatel + + + + + settings.behavior.data_source_synonyms.project + Projekt + + settings.system.privacy @@ -15135,5 +15201,11 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz Upravit + + + datasource.synonym + %name% (Váš synonymum: %synonym%) + + diff --git a/translations/messages.da.xlf b/translations/messages.da.xlf index 001fd70a..1caa65e9 100644 --- a/translations/messages.da.xlf +++ b/translations/messages.da.xlf @@ -13732,5 +13732,11 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver Rediger + + + datasource.synonym + %name% (Dit synonym: %synonym%) + + diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf index 1d44416f..b54dd7c6 100644 --- a/translations/messages.de.xlf +++ b/translations/messages.de.xlf @@ -13284,6 +13284,72 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön <b>Bitte beachten Sie, dass die Währungen bei einer Änderung dieses Wertes nicht umgerechnet werden. Wenn Sie also die Basiswährung ändern, nachdem Sie bereits Preisinformationen hinzugefügt haben, führt dies zu falschen Preisen!</b> + + + settings.system.localization.preferred_languages + Bevorzugte Sprachen + + + + + settings.system.localization.preferred_languages.help + Die Sprachen, die im Dropdown-Menü der Nutzer angezeigt werden + + + + + settings.system.data_source_synonyms + Datenquellen-Synonyme + + + + + settings.system.data_source_synonyms.configuration + Quelle + + + + + settings.system.data_source_synonyms.configuration.help + Definieren Sie Ihre eigenen Synonyme für die angegebenen Datenquellen. Erwartet wird ein JSON-Format mit Ihren bevorzugten Sprache-ISO-Codes. Beispiel: %format%. + + + + + settings.behavior.data_source_synonyms.category + Kategorie + + + + + settings.behavior.data_source_synonyms.storagelocation + Lagerort + + + + + settings.behavior.data_source_synonyms.footprint + Footprint + + + + + settings.behavior.data_source_synonyms.manufacturer + Hersteller + + + + + settings.behavior.data_source_synonyms.supplier + Lieferant + + + + + settings.behavior.data_source_synonyms.project + Projekt + + settings.system.privacy @@ -15809,5 +15875,11 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön Bearbeiten + + + datasource.synonym + %name% (Ihr Synonym: %synonym%) + + diff --git a/translations/messages.el.xlf b/translations/messages.el.xlf index 0a0be4ba..206787dd 100644 --- a/translations/messages.el.xlf +++ b/translations/messages.el.xlf @@ -3645,5 +3645,11 @@ Επεξεργασία + + + datasource.synonym + %name% (Το συνώνυμό σας: %synonym%) + + diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index a1bcc327..e8a3f532 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -13285,6 +13285,72 @@ Please note, that you can not impersonate a disabled user. If you try you will g <b>Please note that the currencies are not converted, when changing this value. So changing the default currency after you already added price information, will result in wrong prices!</b> + + + settings.system.localization.preferred_languages + Preferred languages + + + + + settings.system.localization.preferred_languages.help + The languages that are shown in user drop down menu + + + + + settings.system.data_source_synonyms + Data source synonyms + + + + + settings.system.data_source_synonyms.configuration + Source + + + + + settings.system.data_source_synonyms.configuration.help + Define your own synonyms for the given data sources. Expected in JSON-format with your preferred language iso-codes. Example: %format%. + + + + + settings.behavior.data_source_synonyms.category + Category + + + + + settings.behavior.data_source_synonyms.storagelocation + Storage location + + + + + settings.behavior.data_source_synonyms.footprint + Footprint + + + + + settings.behavior.data_source_synonyms.manufacturer + Manufacturer + + + + + settings.behavior.data_source_synonyms.supplier + Supplier + + + + + settings.behavior.data_source_synonyms.project + Project + + settings.system.privacy @@ -15810,5 +15876,11 @@ Please note, that you can not impersonate a disabled user. If you try you will g Edit + + + datasource.synonym + %name% (Your synonym: %synonym%) + + diff --git a/translations/messages.es.xlf b/translations/messages.es.xlf index 643bd74f..b4e1a081 100644 --- a/translations/messages.es.xlf +++ b/translations/messages.es.xlf @@ -13904,5 +13904,11 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S Editar + + + datasource.synonym + %name% (Tu sinónimo: %synonym%) + + diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf index 4c636df3..24ac7ca3 100644 --- a/translations/messages.fr.xlf +++ b/translations/messages.fr.xlf @@ -10314,5 +10314,11 @@ exemple de ville État personnalisé de la pièce + + + datasource.synonym + %name% (Votre synonyme : %synonym%) + + diff --git a/translations/messages.it.xlf b/translations/messages.it.xlf index 147feb13..12daaf86 100644 --- a/translations/messages.it.xlf +++ b/translations/messages.it.xlf @@ -13906,5 +13906,11 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a Modifica + + + datasource.synonym + %name% (Il tuo sinonimo: %synonym%) + + diff --git a/translations/messages.ja.xlf b/translations/messages.ja.xlf index 69934c75..94383f3d 100644 --- a/translations/messages.ja.xlf +++ b/translations/messages.ja.xlf @@ -10039,5 +10039,11 @@ Exampletown 部品のカスタム状態 + + + datasource.synonym + %name% (あなたの同義語: %synonym%) + + diff --git a/translations/messages.nl.xlf b/translations/messages.nl.xlf index 32d0dd6e..4dbffbf8 100644 --- a/translations/messages.nl.xlf +++ b/translations/messages.nl.xlf @@ -1959,5 +1959,11 @@ Maak eerst een component en wijs het toe aan een categorie: met de bestaande categorieën en hun eigen IPN-prefixen kan de IPN voor het component automatisch worden voorgesteld + + + datasource.synonym + %name% (Uw synoniem: %synonym%) + + diff --git a/translations/messages.pl.xlf b/translations/messages.pl.xlf index 3ea91275..5d175e0a 100644 --- a/translations/messages.pl.xlf +++ b/translations/messages.pl.xlf @@ -13759,5 +13759,11 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli Edytuj + + + datasource.synonym + %name% (Twój synonim: %synonym%) + + diff --git a/translations/messages.ru.xlf b/translations/messages.ru.xlf index 1ca26f7a..6b9a215e 100644 --- a/translations/messages.ru.xlf +++ b/translations/messages.ru.xlf @@ -13859,5 +13859,11 @@ Редактировать + + + datasource.synonym + %name% (Ваш синоним: %synonym%) + + diff --git a/translations/messages.zh.xlf b/translations/messages.zh.xlf index ca09263e..6a3521ee 100644 --- a/translations/messages.zh.xlf +++ b/translations/messages.zh.xlf @@ -13744,5 +13744,11 @@ Element 3 编辑 + + + datasource.synonym + %name% (您的同义词: %synonym%) + +