Skip to content

Commit

Permalink
Add extraction for i18n attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
usox committed Dec 18, 2023
1 parent 21cbf6a commit acedda5
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/Extractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use DOMXPath;
use Generator;
use Usox\TalI18nExtract\Extractors\ExtractorInterface;
use Usox\TalI18nExtract\Extractors\I18nAttributeExtractor;
use Usox\TalI18nExtract\Extractors\I18nTranslateEmptyExtractor;
use Usox\TalI18nExtract\Extractors\I18nTranslateKeyExtractor;

Expand All @@ -21,6 +22,7 @@ public function __construct()
$this->extractors = [
new I18nTranslateKeyExtractor(),
new I18nTranslateEmptyExtractor(),
new I18nAttributeExtractor(),
];
}

Expand Down
54 changes: 54 additions & 0 deletions src/Extractors/I18nAttributeExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Usox\TalI18nExtract\Extractors;

use DOMNode;
use DOMNodeList;
use DOMXPath;
use Generator;

final class I18nAttributeExtractor implements ExtractorInterface
{
/**
* @return Generator<string>
*/
public function extract(DOMXPath $xpath): Generator
{
$result = $xpath->query('//*[@i18n:attributes]');

if ($result instanceof DOMNodeList) {
/** @var DOMNode $item */
foreach ($result as $item) {
$node = $item->attributes?->getNamedItemNS('http://xml.zope.org/namespaces/i18n', 'attributes');

if ($node === null) {
continue;
}

$tuples = preg_split('/;/', (string) $node->nodeValue);
if ($tuples === false) {
continue;
}

foreach ($tuples as $tuple) {
$tuple = trim($tuple);
$attribute = preg_split('/\s/', $tuple);

if ($attribute === false) {
continue;
}

if (count($attribute) === 2) {
[$_, $translationKey] = $attribute;

yield $translationKey;
} else {
yield (string) $item->attributes?->getNamedItem($tuple)?->nodeValue;
}
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/Extractors/I18nTranslateKeyExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function extract(DOMXPath $xpath): Generator
if ($result instanceof DOMNodeList) {
/** @var DOMNode $item */
foreach ($result as $item) {
$node = $item->attributes?->getNamedItem('translate');
$node = $item->attributes?->getNamedItemNS('http://xml.zope.org/namespaces/i18n', 'translate');

if ($node !== null) {
yield (string) $node->nodeValue;
Expand Down
68 changes: 68 additions & 0 deletions tests/Extractors/I18nAttributeExtractorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Usox\TalI18nExtract\Extractors;

use DOMDocument;
use DOMXPath;
use Generator;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class I18nAttributeExtractorTest extends TestCase
{
private I18nAttributeExtractor $subject;

protected function setUp(): void
{
$this->subject = new I18nAttributeExtractor();
}

#[DataProvider(methodName: 'translationKeyDataProvider')]
public function testExtractExtractsKeys(string $html, array $expectedKeys): void
{
$body = <<<HTML
<html xmlns:i18n="http://xml.zope.org/namespaces/i18n">%s</html>
HTML;

$dom = new DOMDocument();
$dom->loadXML(sprintf($body, $html));

$xpath = new DOMXPath($dom);
$xpath->registerNamespace('i18n', 'http://xml.zope.org/namespaces/i18n');

static::assertSame(
$expectedKeys,
iterator_to_array(
$this->subject->extract($xpath)
)
);
}

/**
* @return Generator<array{string, list<string>}>
*/
public static function translationKeyDataProvider(): Generator
{
yield [
'<div i18n:translate="">snafu</div>',
[],
];

yield [
'<div i18n:attributes="alt snafu">foobar</div>',
['snafu'],
];

yield [
'<img i18n:attributes="alt" alt="snafu">foobar</img>',
['snafu'],
];

yield [
'<img i18n:attributes="alt; title" title="bar" alt="baz">foobar</img>',
['baz', 'bar'],
];
}
}

0 comments on commit acedda5

Please sign in to comment.