mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-13 13:49:33 +00:00
Regex-Konfigurationsmöglichkeit für IPN-Vorschläge einführen
Die Einstellungen für die IPN-Vorschlagslogik wurden um eine Regex-Validierung und eine Hilfetext-Konfiguration erweitert. Tests und Änderungen an den Formularoptionen wurden implementiert.
This commit is contained in:
parent
654c2ed2af
commit
6589e0d725
8 changed files with 395 additions and 11 deletions
|
|
@ -116,6 +116,8 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
|
|||
value should be handled as confidential data and not shared publicly.
|
||||
* `SHOW_PART_IMAGE_OVERLAY`: Set to 0 to disable the part image overlay, which appears if you hover over an image in the
|
||||
part image gallery
|
||||
* `IPN_SUGGEST_REGEX`: A global regular expression, that part IPNs have to fullfill. Enforce your own format for your users.
|
||||
* `IPN_SUGGEST_REGEX_HELP`: Define your own user help text for the Regex format specification.
|
||||
* `IPN_AUTO_APPEND_SUFFIX`: When enabled, an incremental suffix will be added to the user input when entering an existing
|
||||
* IPN again upon saving.
|
||||
* `IPN_SUGGEST_PART_DIGITS`: Defines the fixed number of digits used as the increment at the end of an IPN (Internal Part Number).
|
||||
|
|
|
|||
|
|
@ -84,6 +84,28 @@ class PartBaseType extends AbstractType
|
|||
$descriptionAttr['data-ipn-suggestion'] = 'descriptionField';
|
||||
}
|
||||
|
||||
$ipnAttr = [
|
||||
'class' => 'ipn-suggestion-field',
|
||||
'data-elements--ipn-suggestion-target' => 'input',
|
||||
'autocomplete' => 'off',
|
||||
];
|
||||
|
||||
if ($this->ipnSuggestSettings->regex !== null && $this->ipnSuggestSettings->regex !== '') {
|
||||
$ipnAttr['pattern'] = $this->ipnSuggestSettings->regex;
|
||||
$ipnAttr['placeholder'] = $this->ipnSuggestSettings->regex;
|
||||
}
|
||||
|
||||
$ipnOptions = [
|
||||
'required' => false,
|
||||
'empty_data' => null,
|
||||
'label' => 'part.edit.ipn',
|
||||
'attr' => $ipnAttr,
|
||||
];
|
||||
|
||||
if (isset($ipnAttr['pattern']) && $this->ipnSuggestSettings->regexHelp !== null && $this->ipnSuggestSettings->regexHelp !== '') {
|
||||
$ipnOptions['help'] = $this->ipnSuggestSettings->regexHelp;
|
||||
}
|
||||
|
||||
//Common section
|
||||
$builder
|
||||
->add('name', TextType::class, [
|
||||
|
|
@ -186,16 +208,7 @@ class PartBaseType extends AbstractType
|
|||
'disable_not_selectable' => true,
|
||||
'label' => 'part.edit.partUnit',
|
||||
])
|
||||
->add('ipn', TextType::class, [
|
||||
'required' => false,
|
||||
'empty_data' => null,
|
||||
'label' => 'part.edit.ipn',
|
||||
'attr' => [
|
||||
'class' => 'ipn-suggestion-field',
|
||||
'data-elements--ipn-suggestion-target' => 'input',
|
||||
'autocomplete' => 'off',
|
||||
]
|
||||
]);
|
||||
->add('ipn', TextType::class, $ipnOptions);
|
||||
|
||||
//Comment section
|
||||
$builder->add('comment', RichTextEditorType::class, [
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ class PartRepository extends NamedDBElementRepository
|
|||
continue;
|
||||
}
|
||||
|
||||
if ($part->getId() === $currentPart->getId()) {
|
||||
if ($part->getId() === $currentPart->getId() && $currentPart->getID() !== null) {
|
||||
// Extract and return the current part's increment directly
|
||||
$incrementPart = substr($part->getIpn(), -$suggestPartDigits);
|
||||
if (is_numeric($incrementPart)) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace App\Settings\MiscSettings;
|
|||
|
||||
use App\Settings\SettingsIcon;
|
||||
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
|
||||
use Jbtronics\SettingsBundle\ParameterTypes\StringType;
|
||||
use Jbtronics\SettingsBundle\Settings\Settings;
|
||||
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
|
||||
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
|
||||
|
|
@ -38,6 +39,23 @@ class IpnSuggestSettings
|
|||
{
|
||||
use SettingsTrait;
|
||||
|
||||
#[SettingsParameter(
|
||||
label: new TM("settings.misc.ipn_suggest.regex"),
|
||||
options: ['type' => StringType::class],
|
||||
formOptions: ['attr' => ['placeholder' => '^[A-Za-z0-9]{3,4}(?:-[A-Za-z0-9]{3,4})*-\d{4}$']],
|
||||
envVar: "IPN_SUGGEST_REGEX", envVarMode: EnvVarMode::OVERWRITE,
|
||||
)]
|
||||
public ?string $regex = null;
|
||||
|
||||
#[SettingsParameter(
|
||||
label: new TM("settings.misc.ipn_suggest.regex_help"),
|
||||
description: new TM("settings.misc.ipn_suggest.regex_help_description"),
|
||||
options: ['type' => StringType::class],
|
||||
formOptions: ['attr' => ['placeholder' => 'Format: 3–4 alphanumeric segments (any number) separated by "-", followed by "-" and 4 digits, e.g., PCOM-RES-0001']],
|
||||
envVar: "IPN_SUGGEST_REGEX_HELP", envVarMode: EnvVarMode::OVERWRITE,
|
||||
)]
|
||||
public ?string $regexHelp = null;
|
||||
|
||||
#[SettingsParameter(
|
||||
label: new TM("settings.misc.ipn_suggest.autoAppendSuffix"),
|
||||
envVar: "bool:IPN_AUTO_APPEND_SUFFIX", envVarMode: EnvVarMode::OVERWRITE,
|
||||
|
|
|
|||
297
tests/Repository/PartRepositoryTest.php
Normal file
297
tests/Repository/PartRepositoryTest.php
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2020 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\Tests\Repository;
|
||||
|
||||
use App\Entity\Parts\Category;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Settings\MiscSettings\IpnSuggestSettings;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use App\Repository\PartRepository;
|
||||
|
||||
final class PartRepositoryTest extends TestCase
|
||||
{
|
||||
public function test_autocompleteSearch_builds_expected_query_without_db(): void
|
||||
{
|
||||
$qb = $this->getMockBuilder(QueryBuilder::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([
|
||||
'select', 'leftJoin', 'where', 'orWhere',
|
||||
'setParameter', 'setMaxResults', 'orderBy', 'getQuery'
|
||||
])->getMock();
|
||||
|
||||
$qb->expects(self::once())->method('select')->with('part')->willReturnSelf();
|
||||
|
||||
$qb->expects(self::exactly(2))->method('leftJoin')->with($this->anything(), $this->anything())->willReturnSelf();
|
||||
|
||||
$qb->expects(self::atLeastOnce())->method('where')->with($this->anything())->willReturnSelf();
|
||||
$qb->method('orWhere')->with($this->anything())->willReturnSelf();
|
||||
|
||||
$searchQuery = 'res';
|
||||
$qb->expects(self::once())->method('setParameter')->with('query', '%'.$searchQuery.'%')->willReturnSelf();
|
||||
$qb->expects(self::once())->method('setMaxResults')->with(10)->willReturnSelf();
|
||||
$qb->expects(self::once())->method('orderBy')->with('NATSORT(part.name)', 'ASC')->willReturnSelf();
|
||||
|
||||
$emMock = $this->createMock(EntityManagerInterface::class);
|
||||
$classMetadata = new ClassMetadata(Part::class);
|
||||
$emMock->method('getClassMetadata')->with(Part::class)->willReturn($classMetadata);
|
||||
|
||||
$translatorMock = $this->createMock(TranslatorInterface::class);
|
||||
$ipnSuggestSettings = $this->createMock(IpnSuggestSettings::class);
|
||||
|
||||
$repo = $this->getMockBuilder(PartRepository::class)
|
||||
->setConstructorArgs([$emMock, $translatorMock, $ipnSuggestSettings])
|
||||
->onlyMethods(['createQueryBuilder'])
|
||||
->getMock();
|
||||
|
||||
$repo->expects(self::once())
|
||||
->method('createQueryBuilder')
|
||||
->with('part')
|
||||
->willReturn($qb);
|
||||
|
||||
$part = new Part(); // create found part, because it is not saved in DB
|
||||
$part->setName('Resistor');
|
||||
|
||||
$queryMock = $this->getMockBuilder(Query::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getResult'])
|
||||
->getMock();
|
||||
$queryMock->expects(self::once())->method('getResult')->willReturn([$part]);
|
||||
|
||||
$qb->method('getQuery')->willReturn($queryMock);
|
||||
|
||||
$result = $repo->autocompleteSearch($searchQuery, 10);
|
||||
|
||||
// Check one part found and returned
|
||||
self::assertIsArray($result);
|
||||
self::assertCount(1, $result);
|
||||
self::assertSame($part, $result[0]);
|
||||
}
|
||||
|
||||
public function test_autoCompleteIpn_with_unsaved_part_and_category_without_part_description(): void
|
||||
{
|
||||
$qb = $this->getMockBuilder(QueryBuilder::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([
|
||||
'select', 'leftJoin', 'where', 'andWhere', 'orWhere',
|
||||
'setParameter', 'setMaxResults', 'orderBy', 'getQuery'
|
||||
])->getMock();
|
||||
|
||||
$qb->method('select')->willReturnSelf();
|
||||
$qb->method('leftJoin')->willReturnSelf();
|
||||
$qb->method('where')->willReturnSelf();
|
||||
$qb->method('andWhere')->willReturnSelf();
|
||||
$qb->method('orWhere')->willReturnSelf();
|
||||
$qb->method('setParameter')->willReturnSelf();
|
||||
$qb->method('setMaxResults')->willReturnSelf();
|
||||
$qb->method('orderBy')->willReturnSelf();
|
||||
|
||||
$emMock = $this->createMock(EntityManagerInterface::class);
|
||||
$classMetadata = new ClassMetadata(Part::class);
|
||||
$emMock->method('getClassMetadata')->with(Part::class)->willReturn($classMetadata);
|
||||
|
||||
$translatorMock = $this->createMock(TranslatorInterface::class);
|
||||
$translatorMock->method('trans')
|
||||
->willReturnCallback(static function (string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string {
|
||||
return $id;
|
||||
});
|
||||
|
||||
$ipnSuggestSettings = $this->createMock(IpnSuggestSettings::class);
|
||||
|
||||
$ipnSuggestSettings->suggestPartDigits = 4;
|
||||
$ipnSuggestSettings->useDuplicateDescription = false;
|
||||
|
||||
$repo = $this->getMockBuilder(PartRepository::class)
|
||||
->setConstructorArgs([$emMock, $translatorMock, $ipnSuggestSettings])
|
||||
->onlyMethods(['createQueryBuilder'])
|
||||
->getMock();
|
||||
|
||||
$repo->expects(self::atLeastOnce())
|
||||
->method('createQueryBuilder')
|
||||
->with('part')
|
||||
->willReturn($qb);
|
||||
|
||||
$queryMock = $this->getMockBuilder(Query::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getResult'])
|
||||
->getMock();
|
||||
|
||||
$categoryParent = new Category();
|
||||
$categoryParent->setName('Passive components');
|
||||
$categoryParent->setPartIpnPrefix('PCOM');
|
||||
|
||||
$categoryChild = new Category();
|
||||
$categoryChild->setName('Resistors');
|
||||
$categoryChild->setPartIpnPrefix('RES');
|
||||
$categoryChild->setParent($categoryParent);
|
||||
|
||||
$partForSuggestGeneration = new Part(); // create found part, because it is not saved in DB
|
||||
$partForSuggestGeneration->setIpn('RES-0001');
|
||||
$partForSuggestGeneration->setCategory($categoryChild);
|
||||
|
||||
$queryMock->method('getResult')->willReturn([$partForSuggestGeneration]);
|
||||
$qb->method('getQuery')->willReturn($queryMock);
|
||||
$suggestions = $repo->autoCompleteIpn($partForSuggestGeneration, '', 4);
|
||||
|
||||
// Check structure available
|
||||
self::assertIsArray($suggestions);
|
||||
self::assertArrayHasKey('commonPrefixes', $suggestions);
|
||||
self::assertArrayHasKey('prefixesPartIncrement', $suggestions);
|
||||
self::assertNotEmpty($suggestions['commonPrefixes']);
|
||||
self::assertNotEmpty($suggestions['prefixesPartIncrement']);
|
||||
|
||||
// Check expected values
|
||||
self::assertSame('RES-', $suggestions['commonPrefixes'][0]['title']);
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.direct_category', $suggestions['commonPrefixes'][0]['description']);
|
||||
self::assertSame('PCOM-RES-', $suggestions['commonPrefixes'][1]['title']);
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment', $suggestions['commonPrefixes'][1]['description']);
|
||||
|
||||
self::assertSame('RES-0002', $suggestions['prefixesPartIncrement'][0]['title']); // next possible free increment for given part category
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.direct_category.increment', $suggestions['prefixesPartIncrement'][0]['description']);
|
||||
self::assertSame('PCOM-RES-0002', $suggestions['prefixesPartIncrement'][1]['title']); // next possible free increment for given part category
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.hierarchical.increment', $suggestions['prefixesPartIncrement'][1]['description']);
|
||||
}
|
||||
|
||||
public function test_autoCompleteIpn_with_unsaved_part_and_category_with_part_description(): void
|
||||
{
|
||||
$qb = $this->getMockBuilder(QueryBuilder::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([
|
||||
'select', 'leftJoin', 'where', 'andWhere', 'orWhere',
|
||||
'setParameter', 'setMaxResults', 'orderBy', 'getQuery'
|
||||
])->getMock();
|
||||
|
||||
$qb->method('select')->willReturnSelf();
|
||||
$qb->method('leftJoin')->willReturnSelf();
|
||||
$qb->method('where')->willReturnSelf();
|
||||
$qb->method('andWhere')->willReturnSelf();
|
||||
$qb->method('orWhere')->willReturnSelf();
|
||||
$qb->method('setParameter')->willReturnSelf();
|
||||
$qb->method('setMaxResults')->willReturnSelf();
|
||||
$qb->method('orderBy')->willReturnSelf();
|
||||
|
||||
$emMock = $this->createMock(EntityManagerInterface::class);
|
||||
$classMetadata = new ClassMetadata(Part::class);
|
||||
$emMock->method('getClassMetadata')->with(Part::class)->willReturn($classMetadata);
|
||||
|
||||
$translatorMock = $this->createMock(TranslatorInterface::class);
|
||||
$translatorMock->method('trans')
|
||||
->willReturnCallback(static function (string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string {
|
||||
return $id;
|
||||
});
|
||||
|
||||
$ipnSuggestSettings = $this->createMock(IpnSuggestSettings::class);
|
||||
|
||||
$ipnSuggestSettings->suggestPartDigits = 4;
|
||||
$ipnSuggestSettings->useDuplicateDescription = false;
|
||||
|
||||
$repo = $this->getMockBuilder(PartRepository::class)
|
||||
->setConstructorArgs([$emMock, $translatorMock, $ipnSuggestSettings])
|
||||
->onlyMethods(['createQueryBuilder'])
|
||||
->getMock();
|
||||
|
||||
$repo->expects(self::atLeastOnce())
|
||||
->method('createQueryBuilder')
|
||||
->with('part')
|
||||
->willReturn($qb);
|
||||
|
||||
$queryMock = $this->getMockBuilder(Query::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getResult'])
|
||||
->getMock();
|
||||
|
||||
$categoryParent = new Category();
|
||||
$categoryParent->setName('Passive components');
|
||||
$categoryParent->setPartIpnPrefix('PCOM');
|
||||
|
||||
$categoryChild = new Category();
|
||||
$categoryChild->setName('Resistors');
|
||||
$categoryChild->setPartIpnPrefix('RES');
|
||||
$categoryChild->setParent($categoryParent);
|
||||
|
||||
$partForSuggestGeneration = new Part(); // create found part, because it is not saved in DB
|
||||
$partForSuggestGeneration->setCategory($categoryChild);
|
||||
$partForSuggestGeneration->setIpn('1810-1679_1');
|
||||
$partForSuggestGeneration->setDescription('NETWORK-RESISTOR 4 0 OHM +5PCT 0.063W TKF SMT');
|
||||
|
||||
$queryMock->method('getResult')->willReturn([$partForSuggestGeneration]);
|
||||
$qb->method('getQuery')->willReturn($queryMock);
|
||||
$suggestions = $repo->autoCompleteIpn($partForSuggestGeneration, 'NETWORK-RESISTOR 4 0 OHM +5PCT 0.063W TKF SMT', 4);
|
||||
|
||||
// Check structure available
|
||||
self::assertIsArray($suggestions);
|
||||
self::assertArrayHasKey('commonPrefixes', $suggestions);
|
||||
self::assertArrayHasKey('prefixesPartIncrement', $suggestions);
|
||||
self::assertNotEmpty($suggestions['commonPrefixes']);
|
||||
self::assertCount(2, $suggestions['commonPrefixes']);
|
||||
self::assertNotEmpty($suggestions['prefixesPartIncrement']);
|
||||
self::assertCount(2, $suggestions['prefixesPartIncrement']);
|
||||
|
||||
// Check expected values without any increment, for user to decide
|
||||
self::assertSame('RES-', $suggestions['commonPrefixes'][0]['title']);
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.direct_category', $suggestions['commonPrefixes'][0]['description']);
|
||||
self::assertSame('PCOM-RES-', $suggestions['commonPrefixes'][1]['title']);
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment', $suggestions['commonPrefixes'][1]['description']);
|
||||
|
||||
// Check expected values with next possible increment at category level
|
||||
self::assertSame('RES-0001', $suggestions['prefixesPartIncrement'][0]['title']); // next possible free increment for given part category
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.direct_category.increment', $suggestions['prefixesPartIncrement'][0]['description']);
|
||||
self::assertSame('PCOM-RES-0001', $suggestions['prefixesPartIncrement'][1]['title']); // next possible free increment for given part category
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.hierarchical.increment', $suggestions['prefixesPartIncrement'][1]['description']);
|
||||
|
||||
$ipnSuggestSettings->useDuplicateDescription = true;
|
||||
|
||||
$suggestionsWithSameDescription = $repo->autoCompleteIpn($partForSuggestGeneration, 'NETWORK-RESISTOR 4 0 OHM +5PCT 0.063W TKF SMT', 4);
|
||||
|
||||
// Check structure available
|
||||
self::assertIsArray($suggestionsWithSameDescription);
|
||||
self::assertArrayHasKey('commonPrefixes', $suggestionsWithSameDescription);
|
||||
self::assertArrayHasKey('prefixesPartIncrement', $suggestionsWithSameDescription);
|
||||
self::assertNotEmpty($suggestionsWithSameDescription['commonPrefixes']);
|
||||
self::assertCount(2, $suggestionsWithSameDescription['commonPrefixes']);
|
||||
self::assertNotEmpty($suggestionsWithSameDescription['prefixesPartIncrement']);
|
||||
self::assertCount(4, $suggestionsWithSameDescription['prefixesPartIncrement']);
|
||||
|
||||
// Check expected values without any increment, for user to decide
|
||||
self::assertSame('RES-', $suggestionsWithSameDescription['commonPrefixes'][0]['title']);
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.direct_category', $suggestionsWithSameDescription['commonPrefixes'][0]['description']);
|
||||
self::assertSame('PCOM-RES-', $suggestionsWithSameDescription['commonPrefixes'][1]['title']);
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment', $suggestionsWithSameDescription['commonPrefixes'][1]['description']);
|
||||
|
||||
// Check expected values with next possible increment at part description level
|
||||
self::assertSame('1810-1679_1', $suggestionsWithSameDescription['prefixesPartIncrement'][0]['title']); // current given value
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.description.current-increment', $suggestionsWithSameDescription['prefixesPartIncrement'][0]['description']);
|
||||
self::assertSame('1810-1679_2', $suggestionsWithSameDescription['prefixesPartIncrement'][1]['title']); // next possible value
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.description.increment', $suggestionsWithSameDescription['prefixesPartIncrement'][1]['description']);
|
||||
|
||||
// Check expected values with next possible increment at category level
|
||||
self::assertSame('RES-0001', $suggestionsWithSameDescription['prefixesPartIncrement'][2]['title']); // next possible free increment for given part category
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.direct_category.increment', $suggestionsWithSameDescription['prefixesPartIncrement'][2]['description']);
|
||||
self::assertSame('PCOM-RES-0001', $suggestionsWithSameDescription['prefixesPartIncrement'][3]['title']); // next possible free increment for given part category
|
||||
self::assertSame('part.edit.tab.advanced.ipn.prefix.hierarchical.increment', $suggestionsWithSameDescription['prefixesPartIncrement'][3]['description']);
|
||||
}
|
||||
}
|
||||
|
|
@ -13059,6 +13059,24 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz
|
|||
<target>Seznam návrhů IPN součástek</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="adi7Zv4" name="settings.misc.ipn_suggest.regex">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex</source>
|
||||
<target>Regex</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kidR4vm" name="settings.misc.ipn_suggest.regex_help">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex_help</source>
|
||||
<target>Nápověda text</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="jch7Bn5" name="settings.misc.ipn_suggest.regex_help_description">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex_help_description</source>
|
||||
<target>Definujte svůj vlastní text nápovědy pro specifikaci formátu Regex.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kdi8mT4" name="settings.misc.ipn_suggest.autoAppendSuffix">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.autoAppendSuffix</source>
|
||||
|
|
|
|||
|
|
@ -13139,6 +13139,24 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
|
|||
<target>Bauteil IPN-Vorschlagsliste</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="adi7Zv4" name="settings.misc.ipn_suggest.regex">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex</source>
|
||||
<target>Regex</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kidR4vm" name="settings.misc.ipn_suggest.regex_help">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex_help</source>
|
||||
<target>Hilfetext</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="jch7Bn5" name="settings.misc.ipn_suggest.regex_help_description">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex_help_description</source>
|
||||
<target>Definieren Sie Ihren eigenen Nuter-Hilfetext zur Regex Formatvorgabe.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kdi8mT4" name="settings.misc.ipn_suggest.autoAppendSuffix">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.autoAppendSuffix</source>
|
||||
|
|
|
|||
|
|
@ -13140,6 +13140,24 @@ Please note, that you can not impersonate a disabled user. If you try you will g
|
|||
<target>Part IPN Suggest</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="adi7Zv4" name="settings.misc.ipn_suggest.regex">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex</source>
|
||||
<target>Regex</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kidR4vm" name="settings.misc.ipn_suggest.regex_help">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex_help</source>
|
||||
<target>Help text</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="jch7Bn5" name="settings.misc.ipn_suggest.regex_help_description">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.regex_help_description</source>
|
||||
<target>Define your own user help text for the Regex format specification.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="kdi8mT4" name="settings.misc.ipn_suggest.autoAppendSuffix">
|
||||
<segment state="translated">
|
||||
<source>settings.misc.ipn_suggest.autoAppendSuffix</source>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue