mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-12-07 19:49:30 +00:00
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.
This commit is contained in:
parent
e53b72a8d1
commit
68e7ffa452
34 changed files with 648 additions and 44 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
strict_variables: true
|
||||
|
|
|
|||
|
|
@ -188,6 +188,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
|
||||
####################################################################################################################
|
||||
|
|
|
|||
|
|
@ -272,8 +272,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.
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
103
src/Form/Type/DataSourceJsonType.php
Normal file
103
src/Form/Type/DataSourceJsonType.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Settings\BehaviorSettings\DataSourceSynonymsSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* A form type that generates multiple JSON input fields for different data sources.
|
||||
*/
|
||||
class DataSourceJsonType extends AbstractType
|
||||
{
|
||||
public function __construct(private DataSourceSynonymsSettings $settings)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$dataSources = $options['data_sources'];
|
||||
$defaultValues = $options['default_values'];
|
||||
$existingData = $options['data'] ?? [];
|
||||
|
||||
if ($existingData === []) {
|
||||
$existingData = $this->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
|
||||
*/
|
||||
private static function isValidJson(string $json): bool
|
||||
{
|
||||
json_decode($json);
|
||||
return json_last_error() === JSON_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
|
|
@ -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'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,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;
|
||||
|
|
@ -50,8 +51,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,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -139,7 +146,7 @@ class ToolsTreeBuilder
|
|||
$this->translator->trans('info_providers.search.title'),
|
||||
$this->urlGenerator->generate('info_providers_search')
|
||||
))->setIcon('fa-treeview fa-fw fa-solid fa-cloud-arrow-down');
|
||||
|
||||
|
||||
$nodes[] = (new TreeViewNode(
|
||||
$this->translator->trans('info_providers.bulk_import.manage_jobs'),
|
||||
$this->urlGenerator->generate('bulk_info_provider_manage')
|
||||
|
|
@ -166,37 +173,37 @@ 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');
|
||||
}
|
||||
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');
|
||||
}
|
||||
|
|
@ -310,4 +317,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,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;
|
||||
|
|
@ -67,6 +68,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;
|
||||
|
|
@ -212,13 +214,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'),
|
||||
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'),
|
||||
};
|
||||
}
|
||||
|
|
@ -274,4 +278,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
73
src/Settings/BehaviorSettings/DataSourceSynonymsSettings.php
Normal file
73
src/Settings/BehaviorSettings/DataSourceSynonymsSettings.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Settings\BehaviorSettings;
|
||||
|
||||
use App\Form\Type\DataSourceJsonType;
|
||||
use App\Settings\SettingsIcon;
|
||||
use Jbtronics\SettingsBundle\ParameterTypes\ArrayType;
|
||||
use Jbtronics\SettingsBundle\ParameterTypes\StringType;
|
||||
use Jbtronics\SettingsBundle\Settings\Settings;
|
||||
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
|
||||
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Translation\TranslatableMessage as TM;
|
||||
|
||||
#[Settings(label: new TM("settings.system.data_source_synonyms"))]
|
||||
#[SettingsIcon("fa-language")]
|
||||
class DataSourceSynonymsSettings
|
||||
{
|
||||
use SettingsTrait;
|
||||
|
||||
#[SettingsParameter(ArrayType::class,
|
||||
label: new TM("settings.system.data_source_synonyms.configuration"),
|
||||
description: new TM("settings.system.data_source_synonyms.configuration.help", ['%format%' => '{"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<string, array<string, string>> 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;
|
||||
}
|
||||
|
||||
}
|
||||
37
src/Settings/SystemSettings/PreferredLocales.php
Normal file
37
src/Settings/SystemSettings/PreferredLocales.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2024 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\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';
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
|||
namespace App\Settings\SystemSettings;
|
||||
|
||||
use Jbtronics\SettingsBundle\Settings\EmbeddedSettings;
|
||||
use App\Settings\BehaviorSettings\DataSourceSynonymsSettings;
|
||||
use Jbtronics\SettingsBundle\Settings\Settings;
|
||||
use Symfony\Component\Translation\TranslatableMessage as TM;
|
||||
|
||||
|
|
@ -33,6 +34,9 @@ class SystemSettings
|
|||
#[EmbeddedSettings()]
|
||||
public ?LocalizationSettings $localization = null;
|
||||
|
||||
#[EmbeddedSettings]
|
||||
public ?DataSourceSynonymsSettings $dataSourceSynonyms = null;
|
||||
|
||||
#[EmbeddedSettings()]
|
||||
public ?CustomizationSettings $customization = null;
|
||||
|
||||
|
|
|
|||
43
src/Twig/DataSourceNameExtension.php
Normal file
43
src/Twig/DataSourceNameExtension.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Settings\BehaviorSettings\DataSourceSynonymsSettings;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class DataSourceNameExtension extends AbstractExtension
|
||||
{
|
||||
private TranslatorInterface $translator;
|
||||
private array $dataSourceSynonyms;
|
||||
|
||||
public function __construct(TranslatorInterface $translator, DataSourceSynonymsSettings $dataSourceSynonymsSettings)
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
{% extends "admin/base_admin.html.twig" %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-tags fa-fw"></i> {% trans %}category.labelp{% endtrans %}
|
||||
{% set dataSourceName = get_data_source_name('category', 'category.labelp') %}
|
||||
{% set translatedSource = 'category.labelp'|trans %}
|
||||
<i class="fas fa-tags fa-fw"></i> {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_pills %}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
{% extends "admin/base_admin.html.twig" %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-microchip fa-fw"></i> {% trans %}footprint.labelp{% endtrans %}
|
||||
{% set dataSourceName = get_data_source_name('footprint', 'footprint.labelp') %}
|
||||
{% set translatedSource = 'footprint.labelp'|trans %}
|
||||
<i class="fas fa-microchip fa-fw"></i> {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block master_picture_block %}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
{% extends "admin/base_company_admin.html.twig" %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-industry fa-fw"></i> {% trans %}manufacturer.caption{% endtrans %}
|
||||
{% set dataSourceName = get_data_source_name('manufacturer', 'manufacturer.caption') %}
|
||||
{% set translatedSource = 'manufacturer.caption'|trans %}
|
||||
<i class="fas fa-industry fa-fw"></i> {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block edit_title %}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
{# @var entity App\Entity\ProjectSystem\Project #}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-archive fa-fw"></i> {% trans %}project.caption{% endtrans %}
|
||||
{% set dataSourceName = get_data_source_name('project', 'project.caption') %}
|
||||
{% set translatedSource = 'project.caption'|trans %}
|
||||
<i class="fas fa-archive fa-fw"></i> {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block edit_title %}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
{% import "label_system/dropdown_macro.html.twig" as dropdown %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-cube fa-fw"></i> {% trans %}storelocation.labelp{% endtrans %}
|
||||
{% set dataSourceName = get_data_source_name('storagelocation', 'storelocation.labelp') %}
|
||||
{% set translatedSource = 'storelocation.labelp'|trans %}
|
||||
<i class="fas fa-cube fa-fw"></i> {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_controls %}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
{% extends "admin/base_company_admin.html.twig" %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-truck fa-fw"></i> {% trans %}supplier.caption{% endtrans %}
|
||||
{% set dataSourceName = get_data_source_name('supplier', 'supplier.caption') %}
|
||||
{% set translatedSource = 'supplier.caption'|trans %}
|
||||
<i class="fas fa-truck fa-fw"></i> {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_panes %}
|
||||
|
|
|
|||
|
|
@ -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.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'],
|
||||
] %}
|
||||
|
||||
<li class="dropdown-header">{% trans %}actions{% endtrans %}</li>
|
||||
|
|
@ -18,9 +20,9 @@
|
|||
|
||||
{% 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 }}"
|
||||
<li><button class="tree-btns dropdown-item" data-mode="{{ source[0] }}" data-url="{{ source[1] }}" data-text="{{ get_data_source_name(source[4], source[2]) }}"
|
||||
{{ stimulus_action('elements/sidebar_tree', 'changeDataSource') }}
|
||||
>{{ source[2] | trans }}</button></li>
|
||||
>{{ get_data_source_name(source[4], source[2]) }}</button></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
|
@ -61,4 +63,4 @@
|
|||
|
||||
<div class="treeview-sm mt-2" {{ stimulus_target('elements/tree', 'tree') }}></div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,34 @@
|
|||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input tristate permission-checkbox permission_multicheckbox" id="mulit_check_{{ form.vars.label }}">
|
||||
<label class="form-check-label" for="mulit_check_{{ form.vars.label }}">
|
||||
<b>{{ form.vars.label | trans }}</b>
|
||||
{% set dataSource = '' %}
|
||||
{% if (form.vars.label == 'perm.part.categories') %}
|
||||
{% set dataSource = 'category' %}
|
||||
{% elseif (form.vars.label == 'perm.storelocations') %}
|
||||
{% set dataSource = 'storagelocation' %}
|
||||
{% elseif (form.vars.label == 'perm.part.footprints') %}
|
||||
{% set dataSource = 'footprint' %}
|
||||
{% elseif (form.vars.label == 'perm.part.manufacturers') %}
|
||||
{% set dataSource = 'manufacturer' %}
|
||||
{% elseif (form.vars.label == 'perm.part.supplier') %}
|
||||
{% set dataSource = 'supplier' %}
|
||||
{% elseif (form.vars.label == 'perm.projects') %}
|
||||
{% set dataSource = 'project' %}
|
||||
{% endif %}
|
||||
|
||||
{% set dataSourceName = get_data_source_name(dataSource, form.vars.label) %}
|
||||
{% set translatedSource = form.vars.label|trans %}
|
||||
{% if dataSourceName != translatedSource %}
|
||||
{{ translatedSource }}
|
||||
<i class="fas fa-fw fa-info" title="{{ 'datasource.synonym'|trans({'%name%': '', '%synonym%': dataSourceName })|raw }}"></i>
|
||||
{% else %}
|
||||
{{ translatedSource }}
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<b>{{ form.vars.label | trans }}</b>
|
||||
<b>def{{ form.vars.label | trans }}</b>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12801,6 +12801,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></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="ferwk9E" name="settings.system.localization.preferred_languages">
|
||||
<segment state="translated">
|
||||
<source>settings.system.localization.preferred_languages</source>
|
||||
<target>Preferované jazyky</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="idme4VU" name="settings.system.localization.preferred_languages.help">
|
||||
<segment state="translated">
|
||||
<source>settings.system.localization.preferred_languages.help</source>
|
||||
<target>Jazyky, které se zobrazují v uživatelské rozbalovací nabídce</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="o6zcqUt" name="settings.system.data_source_synonyms">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms</source>
|
||||
<target>Synonyma zdrojů dat</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kue6Tga" name="settings.system.data_source_synonyms.configuration">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms.configuration</source>
|
||||
<target>Zdroj</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="lEcz6N1" name="settings.system.data_source_synonyms.configuration.help">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms.configuration.help</source>
|
||||
<target>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%.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kd8nBt4" name="settings.behavior.data_source_synonyms.category">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.category</source>
|
||||
<target>Kategorie</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="id3hnbC" name="settings.behavior.data_source_synonyms.storagelocation">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.storagelocation</source>
|
||||
<target>Skladové umístění</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kdz5RcV" name="settings.behavior.data_source_synonyms.footprint">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.footprint</source>
|
||||
<target>Pouzdro</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="uBeDc7l" name="settings.behavior.data_source_synonyms.manufacturer">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.manufacturer</source>
|
||||
<target>Výrobce</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="OewynB4" name="settings.behavior.data_source_synonyms.supplier">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.supplier</source>
|
||||
<target>Dodavatel</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="n7ze4lk" name="settings.behavior.data_source_synonyms.project">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.project</source>
|
||||
<target>Projekt</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cvpTUeY" name="settings.system.privacy">
|
||||
<segment state="translated">
|
||||
<source>settings.system.privacy</source>
|
||||
|
|
@ -13659,5 +13725,11 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz
|
|||
<target>Minimální šířka náhledu (px)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Váš synonymum: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -12328,5 +12328,11 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver
|
|||
<target>Du forsøgte at fjerne/tilføje en mængde sat til nul! Der blev ikke foretaget nogen handling.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Dit synonym: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -12881,6 +12881,60 @@ 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>]]></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="o6zcqUt" name="settings.system.data_source_synonyms">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms</source>
|
||||
<target>Datenquellen-Synonyme</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kue6Tga" name="settings.system.data_source_synonyms.configuration">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms.configuration</source>
|
||||
<target>Quelle</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="lEcz6N1" name="settings.system.data_source_synonyms.configuration.help">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms.configuration.help</source>
|
||||
<target>Definieren Sie Ihre eigenen Synonyme für die angegebenen Datenquellen. Erwartet wird ein JSON-Format mit Ihren bevorzugten Sprache-ISO-Codes. Beispiel: %format%.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kd8nBt4" name="settings.behavior.data_source_synonyms.category">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.category</source>
|
||||
<target>Kategorie</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="id3hnbC" name="settings.behavior.data_source_synonyms.storagelocation">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.storagelocation</source>
|
||||
<target>Lagerort</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kdz5RcV" name="settings.behavior.data_source_synonyms.footprint">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.footprint</source>
|
||||
<target>Footprint</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="uBeDc7l" name="settings.behavior.data_source_synonyms.manufacturer">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.manufacturer</source>
|
||||
<target>Hersteller</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="OewynB4" name="settings.behavior.data_source_synonyms.supplier">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.supplier</source>
|
||||
<target>Lieferant</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="n7ze4lk" name="settings.behavior.data_source_synonyms.project">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.project</source>
|
||||
<target>Projekt</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cvpTUeY" name="settings.system.privacy">
|
||||
<segment state="translated">
|
||||
<source>settings.system.privacy</source>
|
||||
|
|
@ -14436,5 +14490,11 @@ Dies ist auf der Informationsquellen Übersichtsseite möglich.</target>
|
|||
<target>Wenn aktiviert, wird die Bauteil-Beschreibung verwendet, um vorhandene Teile mit derselben Beschreibung zu finden und die nächste verfügbare IPN für die Vorschlagsliste zu ermitteln, indem der numerische Suffix entsprechend erhöht wird.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Ihr Synonym: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -1667,5 +1667,11 @@
|
|||
<target>Δημιουργήστε πρώτα ένα εξάρτημα και αντιστοιχίστε το σε μια κατηγορία: με τις υπάρχουσες κατηγορίες και τα δικά τους προθέματα IPN, η ονομασία IPN για το εξάρτημα μπορεί να προταθεί αυτόματα</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Το συνώνυμό σας: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -12882,6 +12882,60 @@ 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>]]></target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="o6zcqUt" name="settings.system.data_source_synonyms">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms</source>
|
||||
<target>Data source synonyms</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kue6Tga" name="settings.system.data_source_synonyms.configuration">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms.configuration</source>
|
||||
<target>Source</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="lEcz6N1" name="settings.system.data_source_synonyms.configuration.help">
|
||||
<segment state="translated">
|
||||
<source>settings.system.data_source_synonyms.configuration.help</source>
|
||||
<target>Define your own synonyms for the given data sources. Expected in JSON-format with your preferred language iso-codes. Example: %format%.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kd8nBt4" name="settings.behavior.data_source_synonyms.category">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.category</source>
|
||||
<target>Category</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="id3hnbC" name="settings.behavior.data_source_synonyms.storagelocation">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.storagelocation</source>
|
||||
<target>Storage location</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kdz5RcV" name="settings.behavior.data_source_synonyms.footprint">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.footprint</source>
|
||||
<target>Footprint</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="uBeDc7l" name="settings.behavior.data_source_synonyms.manufacturer">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.manufacturer</source>
|
||||
<target>Manufacturer</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="OewynB4" name="settings.behavior.data_source_synonyms.supplier">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.supplier</source>
|
||||
<target>Supplier</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="n7ze4lk" name="settings.behavior.data_source_synonyms.project">
|
||||
<segment state="translated">
|
||||
<source>settings.behavior.data_source_synonyms.project</source>
|
||||
<target>Project</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cvpTUeY" name="settings.system.privacy">
|
||||
<segment state="translated">
|
||||
<source>settings.system.privacy</source>
|
||||
|
|
@ -14468,5 +14522,11 @@ You can do this in the provider info list.</target>
|
|||
<target>A PCRE-compatible regular expression every IPN has to fulfill. Leave empty to allow all everything as IPN. </target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Your synonym: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -12500,5 +12500,11 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S
|
|||
<target>Este componente contiene más de un stock. Cambie la ubicación manualmente para seleccionar el stock deseado.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Tu sinónimo: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -9229,5 +9229,11 @@ exemple de ville</target>
|
|||
<target>Un préfixe suggéré lors de la saisie de l'IPN d'une pièce.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Votre synonyme : %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -12502,5 +12502,11 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a
|
|||
<target>Questo componente contiene più di uno stock. Cambia manualmente la posizione per selezionare quale stock scegliere.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Il tuo sinonimo: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -8966,5 +8966,11 @@ Exampletown</target>
|
|||
<target>部品のIPN入力時に提案される接頭辞。</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (あなたの同義語: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -856,5 +856,11 @@
|
|||
<target>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</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Uw synoniem: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -12355,5 +12355,11 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli
|
|||
<target>Wygenerowany kod</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Twój synonim: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -737,7 +737,7 @@
|
|||
</notes>
|
||||
<segment state="translated">
|
||||
<source>user.edit.tfa.disable_tfa_message</source>
|
||||
<target>Это выключит <b>все активные двухфакторной способы аутентификации пользователя</b>и удалит <b>резервные коды</b>!
|
||||
<target>Это выключит <b>все активные двухфакторной способы аутентификации пользователя</b>и удалит <b>резервные коды</b>!
|
||||
<br>
|
||||
Пользователь должен будет снова настроить все методы двухфакторной аутентификации и распечатать новые резервные коды! <br><br>
|
||||
<b>Делайте это только в том случае, если вы абсолютно уверены в личности пользователя (обращающегося за помощью), в противном случае учетная запись может быть взломана злоумышленником!</b></target>
|
||||
|
|
@ -3806,7 +3806,7 @@
|
|||
</notes>
|
||||
<segment state="translated">
|
||||
<source>tfa_backup.reset_codes.confirm_message</source>
|
||||
<target>Это удалит все предыдущие коды и создаст набор новых. Это не может быть отменено.
|
||||
<target>Это удалит все предыдущие коды и создаст набор новых. Это не может быть отменено.
|
||||
Не забудьте распечатать новы кода и хранить их в безопасном месте!</target>
|
||||
</segment>
|
||||
</unit>
|
||||
|
|
@ -12455,5 +12455,11 @@
|
|||
<target>Профиль сохранен!</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (Ваш синоним: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
|
|
@ -12340,5 +12340,11 @@ Element 3</target>
|
|||
<target>成功创建 %COUNT% 个元素。</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="a7uOieC" name="datasource.synonym">
|
||||
<segment state="translated">
|
||||
<source>datasource.synonym</source>
|
||||
<target>%name% (您的同义词: %synonym%)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue