• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

move-elevator / composer-translation-validator / 16046116502

03 Jul 2025 08:59AM UTC coverage: 93.894% (+0.4%) from 93.478%
16046116502

push

github

web-flow
Merge pull request #15 from move-elevator/duplicate-values

feat: add DuplicateValuesValidator to check for duplicate values in translation files

54 of 55 new or added lines in 3 files covered. (98.18%)

569 of 606 relevant lines covered (93.89%)

4.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.11
/src/Validator/DuplicateValuesValidator.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace MoveElevator\ComposerTranslationValidator\Validator;
6

7
use MoveElevator\ComposerTranslationValidator\Parser\ParserInterface;
8
use MoveElevator\ComposerTranslationValidator\Parser\XliffParser;
9
use MoveElevator\ComposerTranslationValidator\Parser\YamlParser;
10
use Symfony\Component\Console\Helper\Table;
11
use Symfony\Component\Console\Helper\TableSeparator;
12
use Symfony\Component\Console\Helper\TableStyle;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Output\OutputInterface;
15

16
class DuplicateValuesValidator extends AbstractValidator implements ValidatorInterface
17
{
18
    /** @var array<string, array<string, array<int, string>>> */
19
    protected array $valuesArray = [];
20

21
    /**
22
     * @return array<string, int>
23
     */
24
    public function processFile(ParserInterface $file): array
3✔
25
    {
26
        $keys = $file->extractKeys();
3✔
27

28
        if (!$keys) {
3✔
29
            $this->logger?->error('The source file '.$file->getFileName().' is not valid.');
1✔
30

31
            return [];
1✔
32
        }
33

34
        foreach ($keys as $key) {
2✔
35
            $value = $file->getContentByKey($key);
2✔
36
            if (null === $value) {
2✔
NEW
37
                continue;
×
38
            }
39
            $this->valuesArray[$file->getFileName()][$value][] = $key;
2✔
40
        }
41

42
        return [];
2✔
43
    }
44

45
    public function postProcess(): void
2✔
46
    {
47
        foreach ($this->valuesArray as $file => $valueKeyArray) {
2✔
48
            $duplicates = [];
2✔
49
            foreach ($valueKeyArray as $value => $keys) {
2✔
50
                if (count(array_unique($keys)) > 1) {
2✔
51
                    $duplicates[$value] = array_unique($keys);
1✔
52
                }
53
            }
54
            if (!empty($duplicates)) {
2✔
55
                $this->issues[] = [
1✔
56
                    'file' => $file,
1✔
57
                    'issues' => $duplicates,
1✔
58
                ];
1✔
59
            }
60
        }
61
    }
62

63
    /**
64
     * @param array<string, array<int, array{file: string, issues: array<string, array<int, string>>}>> $issueSets
65
     */
66
    public function renderIssueSets(InputInterface $input, OutputInterface $output, array $issueSets): void
1✔
67
    {
68
        $rows = [];
1✔
69
        $currentFile = null;
1✔
70

71
        foreach ($issueSets as $issueSet) {
1✔
72
            foreach ($issueSet as $duplicate) {
1✔
73
                if ($currentFile !== $duplicate['file'] && null !== $currentFile) {
1✔
74
                    $rows[] = new TableSeparator();
1✔
75
                }
76
                $currentFile = $duplicate['file'];
1✔
77
                $fileName = $duplicate['file'];
1✔
78
                foreach ($duplicate['issues'] as $value => $keys) {
1✔
79
                    $rows[] = [
1✔
80
                        '<fg=red>'.$fileName.'</>',
1✔
81
                        implode("\n", $keys),
1✔
82
                        '<fg=yellow>'.$value.'</>',
1✔
83
                    ];
1✔
84
                    $fileName = '';
1✔
85
                }
86
            }
87
        }
88

89
        (new Table($output))
1✔
90
            ->setHeaders(['File', 'Key', 'Value'])
1✔
91
            ->setRows($rows)
1✔
92
            ->setStyle(
1✔
93
                (new TableStyle())
1✔
94
                    ->setCellHeaderFormat('%s')
1✔
95
            )
1✔
96
            ->render();
1✔
97
    }
98

99
    public function explain(): string
1✔
100
    {
101
        return 'This validator checks for duplicate values in translation files. '
1✔
102
            .'If a value is assigned to multiple keys in a file, it will be reported as an issue.';
1✔
103
    }
104

105
    /**
106
     * @return class-string<ParserInterface>[]
107
     */
108
    public function supportsParser(): array
1✔
109
    {
110
        return [XliffParser::class, YamlParser::class];
1✔
111
    }
112

113
    public function resultTypeOnValidationFailure(): ResultType
1✔
114
    {
115
        return ResultType::WARNING;
1✔
116
    }
117
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc