Skip to content

Commit 2f2f930

Browse files
committed
itk_translation_extractor
1 parent 6584705 commit 2f2f930

14 files changed

+242
-95
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"license": "MIT",
55
"type": "drupal-module",
66
"require": {
7+
"php": "^8.4",
78
"drupal/core": "^10 || ^11",
89
"symfony/translation": "^6 || ^7 || ^8",
910
"symfony/twig-bridge": "^6 || ^7 || ^8"

drush.services.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ services:
55
- "@twig"
66
- "@extension.path.resolver"
77
- "@language_manager"
8-
- "@file_system"
8+
- "@locale.storage"
9+
- "@Drupal\\itk_translation_extractor\\Translation\\Dumper\\PoFileDumper"
910
tags:
1011
- { name: console.command }

itk_translation_extractor.services.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ services:
22
_defaults:
33
autowire: true
44

5+
Drupal\itk_translation_extractor\Translation\Dumper\PoFileDumper:
6+
57
Drupal\itk_translation_extractor\ItkTranslationExtractorTwigExtension:
68
tags:
79
- { name: twig.extension }

src/Command/TranslationExtractCommand.php

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
namespace Drupal\itk_translation_extractor\Command;
66

77
use Drupal\Core\Extension\ExtensionPathResolver;
8-
use Drupal\Core\File\FileSystemInterface;
98
use Drupal\Core\Language\LanguageManagerInterface;
109
use Drupal\itk_translation_extractor\Translation\Dumper\PoFileDumper;
10+
use Drupal\itk_translation_extractor\Translation\Helper;
1111
use Drupal\itk_translation_extractor\Translation\TwigExtractor;
12+
use Drupal\locale\StringStorageInterface;
1213
use Symfony\Component\Console\Attribute\AsCommand;
1314
use Symfony\Component\Console\Command\Command;
1415
use Symfony\Component\Console\Input\InputArgument;
@@ -18,10 +19,8 @@
1819
use Symfony\Component\Console\Style\SymfonyStyle;
1920
use Symfony\Component\Translation\Catalogue\MergeOperation;
2021
use Symfony\Component\Translation\Catalogue\TargetOperation;
21-
use Symfony\Component\Translation\Loader\PoFileLoader;
2222
use Symfony\Component\Translation\MessageCatalogue;
2323
use Symfony\Component\Translation\MessageCatalogueInterface;
24-
use Symfony\Component\Translation\Reader\TranslationReader;
2524
use Symfony\Component\Translation\Writer\TranslationWriter;
2625
use Twig\Environment;
2726

@@ -57,27 +56,17 @@ final class TranslationExtractCommand extends Command
5756
*/
5857
private TwigExtractor $extractor;
5958

60-
/**
61-
* The reader.
62-
*/
63-
private TranslationReader $reader;
64-
6559
public function __construct(
66-
// #[Autowire(service: 'twig')]
6760
private readonly Environment $twig,
6861
private readonly ExtensionPathResolver $extensionPathResolver,
6962
private readonly LanguageManagerInterface $languageManager,
70-
FileSystemInterface $fileSystem,
63+
private readonly StringStorageInterface $stringStorage,
64+
PoFileDumper $poFileDumper,
7165
) {
7266
$this->extractor = new TwigExtractor($this->twig);
7367

74-
$this->reader = new TranslationReader();
75-
$this->reader->addLoader('po', new PoFileLoader());
76-
7768
$this->writer = new TranslationWriter();
78-
$this->writer->addDumper('po', new PoFileDumper(
79-
fileSystem: $fileSystem,
80-
));
69+
$this->writer->addDumper('po', $poFileDumper);
8170

8271
parent::__construct();
8372
}
@@ -212,7 +201,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
212201
// $currentCatalogue = $this->loadCurrentMessages($input->getArgument('locale'), $transPaths);
213202

214203
$io->comment('Loading translated messages...');
215-
$currentCatalogue = $this->loadTranslatedMessages($input->getArgument('locale'), $extractedCatalogue);
204+
$currentCatalogue = $this->loadTranslatedMessages($extractedCatalogue);
216205

217206
// if (null !== $domain = $input->getOption('domain')) {
218207
// $currentCatalogue = $this->filterCatalogue($currentCatalogue, $domain);
@@ -447,23 +436,29 @@ private function filterDuplicateTransPaths(array $transPaths): array
447436
return $filteredPaths;
448437
}
449438

450-
private function loadTranslatedMessages(string $locale, MessageCatalogue $extractedCatalogue)
451-
{
452-
$currentCatalogue = new MessageCatalogue($locale);
453-
454-
return $currentCatalogue;
455-
}
456-
457-
private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue
439+
private function loadTranslatedMessages(MessageCatalogue $extractedCatalogue)
458440
{
459-
// @todo Load existing translations from database.
460-
$currentCatalogue = new MessageCatalogue($locale);
461-
foreach ($transPaths as $path) {
462-
if (is_dir($path)) {
463-
$this->reader->read($path, $currentCatalogue);
441+
$currentCatalogue = new MessageCatalogue($extractedCatalogue->getLocale());
442+
443+
foreach ($extractedCatalogue->getDomains() as $domain) {
444+
$translations = $this->stringStorage->getTranslations([
445+
'language' => $currentCatalogue->getLocale(),
446+
'context' => Helper::getContext($domain),
447+
]);
448+
// Index by source
449+
$translations = array_column($translations, null, 'source');
450+
foreach ($extractedCatalogue->all($domain) as $source => $_) {
451+
if ($translation = ($translations[$source] ?? null)) {
452+
if ($string = $translation->getString()) {
453+
$currentCatalogue->set($source, $string, $domain);
454+
$currentCatalogue->setMetadata($source, ['plurals' => $translation->getPlurals()], $domain);
455+
}
456+
}
464457
}
465458
}
466459

460+
// Load from database
461+
467462
return $currentCatalogue;
468463
}
469464

src/NodeVisitor/TranslationNodeVisitor.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Drupal\Core\Template\TwigNodeTrans;
66
use Drupal\Core\Template\TwigNodeTrans as TransNode;
7+
use Drupal\itk_translation_extractor\Translation\Helper;
78
use Twig\Environment;
89
use Twig\Error\SyntaxError;
910
use Twig\Node\CheckToStringNode;
@@ -28,8 +29,6 @@
2829
*/
2930
final class TranslationNodeVisitor implements NodeVisitorInterface
3031
{
31-
public const UNDEFINED_DOMAIN = '';
32-
3332
private bool $enabled = false;
3433
private array $messages = [];
3534

@@ -86,7 +85,7 @@ public function enterNode(Node $node, Environment $env): Node
8685
// {% trans '…' %}
8786
$this->messages[] = [
8887
$this->getConcatValueFromNode($body, ''),
89-
$node->hasNode('options') ? $this->getReadDomainFromNode($node->getNode('options')) : null,
88+
$node->hasNode('options') ? $this->getReadDomainFromNode($node->getNode('options')) : Helper::UNDEFINED_DOMAIN,
9089
];
9190
} else {
9291
// {% trans %}…{% endtrans %}
@@ -95,13 +94,13 @@ public function enterNode(Node $node, Environment $env): Node
9594
$plural = $this->getStringValue($node->getNode('plural'));
9695
$this->messages[] = [
9796
$body->getAttribute('data'),
98-
$node->hasNode('options') ? $this->getReadDomainFromNode($node->getNode('options')) : null,
97+
$node->hasNode('options') ? $this->getReadDomainFromNode($node->getNode('options')) : Helper::UNDEFINED_DOMAIN,
9998
['plurals' => [$singular, $plural]],
10099
];
101100
} else {
102101
$this->messages[] = [
103102
$body->getAttribute('data'),
104-
$node->hasNode('options') ? $this->getReadDomainFromNode($node->getNode('options')) : null,
103+
$node->hasNode('options') ? $this->getReadDomainFromNode($node->getNode('options')) : Helper::UNDEFINED_DOMAIN,
105104
];
106105
}
107106
}
@@ -152,31 +151,31 @@ private function getReadMessageFromNode(Node $node): ?string
152151
return null;
153152
}
154153

155-
private function getReadDomainFromArguments(Node $arguments, int $index): ?string
154+
private function getReadDomainFromArguments(Node $arguments, int $index): string
156155
{
157156
if ($arguments->hasNode('options')) {
158157
$argument = $arguments->getNode('options');
159158
} elseif ($arguments->hasNode($index)) {
160159
$argument = $arguments->getNode($index);
161160
} else {
162-
return null;
161+
return Helper::UNDEFINED_DOMAIN;
163162
}
164163

165164
return $this->getReadDomainFromNode($argument);
166165
}
167166

168-
private function getReadDomainFromNode(Node $node): ?string
167+
private function getReadDomainFromNode(Node $node): string
169168
{
170169
if ($node instanceof ArrayExpression) {
171170
foreach ($node->getKeyValuePairs() as $pair) {
172171
$key = $this->getConcatValueFromNode($pair['key'], '');
173172
if ('context' === $key) {
174-
return $this->getConcatValueFromNode($pair['value'], '');
173+
return $this->getConcatValueFromNode($pair['value'], '') ?: Helper::UNDEFINED_DOMAIN;
175174
}
176175
}
177176
}
178177

179-
return self::UNDEFINED_DOMAIN;
178+
return Helper::UNDEFINED_DOMAIN;
180179
}
181180

182181
private function getConcatValueFromNode(Node $node, ?string $value): ?string

src/Translation/Dumper/PoFileDumper.php

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace Drupal\itk_translation_extractor\Translation\Dumper;
44

5-
use Drupal\Component\Gettext\PoHeader;
65
use Drupal\Component\Gettext\PoItem;
76
use Drupal\Component\Gettext\PoStreamWriter;
7+
use Drupal\itk_translation_extractor\Translation\Helper;
88
use Symfony\Component\Translation\Dumper\PoFileDumper as BasePoFileDumper;
99
use Symfony\Component\Translation\Exception\InvalidArgumentException;
1010
use Symfony\Component\Translation\MessageCatalogue;
@@ -33,38 +33,35 @@ public function formatCatalogue(
3333
string $domain,
3434
array $options = [],
3535
): string {
36-
$header = new PoHeader();
37-
// Set Plural-Forms
38-
$spec = '';
39-
switch ($messages->getLocale()) {
40-
case 'da':
41-
$spec = 'Plural-Forms: nplurals=2; plural=(n != 1);';
36+
$locale = $messages->getLocale();
37+
$pluralForm = Helper::getPluralForm($locale);
38+
if (null === $pluralForm) {
39+
throw new InvalidArgumentException(sprintf('Invalid locale: %s', $locale));
4240
}
43-
$header->setFromString($spec);
44-
if ($languageName = ($options['language_name'] ?? null)) {
41+
$numberOfPlurals = Helper::getNumberOfPlurals($locale);
42+
43+
$header = new PoHeader($locale);
44+
$header->setPluralForms($pluralForm);
45+
if ($languageName = Helper::getLanguageName($locale)) {
4546
$header->setLanguageName($languageName);
4647
}
4748
if ($projectName = ($options['project_name'] ?? null)) {
4849
$header->setProjectName($projectName);
4950
}
50-
$uri = tempnam(sys_get_temp_dir(), 'po_');
5151

52+
$uri = tempnam(sys_get_temp_dir(), 'po_');
5253
$writer = new PoStreamWriter();
5354
$writer->setURI($uri);
5455
$writer->setHeader($header);
5556
$writer->open();
5657
foreach ($messages->getDomains() as $domain) {
5758
foreach ($messages->all($domain) as $source => $translation) {
5859
$metadata = $messages->getMetadata($source, $domain);
59-
// MessageCatalog has a special case for the empty domain.
60-
if ('' === $domain) {
61-
$metadata = $metadata[$domain][$source] ?? null;
62-
}
6360
$item = new PoItem();
61+
$item->setContext(Helper::getContext($domain));
6462
if ($plurals = ($metadata['plurals'] ?? null)) {
6563
$item->setPlural(true);
6664
$item->setSource($plurals);
67-
// @todo!!!
6865
$item->setTranslation($plurals);
6966
} else {
7067
$item->setSource($source);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Drupal\itk_translation_extractor\Translation\Dumper;
4+
5+
class PoHeader extends \Drupal\Component\Gettext\PoHeader
6+
{
7+
public function __construct(string $langcode)
8+
{
9+
parent::__construct($langcode);
10+
}
11+
12+
/**
13+
* The parent's missing setter.
14+
*/
15+
public function setPluralForms(string $pluralForms): void
16+
{
17+
$this->pluralForms = $pluralForms;
18+
}
19+
20+
public function __toString()
21+
{
22+
$output = trim(parent::__toString())."\n";
23+
$output .= '"Language: '.$this->langcode."\\n\"\n";
24+
$output .= "\n";
25+
26+
return $output;
27+
}
28+
}

src/Translation/Extractor/Visitor/TransMethodVisitor.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Drupal\itk_translation_extractor\Translation\Extractor\Visitor;
44

5+
use Drupal\itk_translation_extractor\Translation\Helper;
56
use PhpParser\Node;
67
use PhpParser\NodeVisitor;
78
use Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor;
@@ -44,13 +45,13 @@ public function leaveNode(Node $node): ?Node
4445
return null;
4546
}
4647

47-
$context = '';
48+
$context = null;
4849
if ($options = $this->getArrayArgument($node, 2 < $firstNamedArgumentIndex ? 2 : 'options')) {
49-
$context = $this->getArrayStringValue($options, 'context') ?? '';
50+
$context = $this->getArrayStringValue($options, 'context');
5051
}
5152

5253
foreach ($messages as $message) {
53-
$this->addMessageToCatalogue($message, $context, $node->getStartLine());
54+
$this->addMessageToCatalogue($message, $context ?? Helper::UNDEFINED_DOMAIN, $node->getStartLine());
5455
}
5556
}
5657

src/Translation/Extractor/Visitor/TranslatableMarkupVisitor.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Drupal\itk_translation_extractor\Translation\Extractor\Visitor;
44

5+
use Drupal\itk_translation_extractor\Translation\Helper;
56
use PhpParser\Node;
67
use PhpParser\NodeVisitor;
78
use Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor;
@@ -45,13 +46,13 @@ public function leaveNode(Node $node): ?Node
4546
return null;
4647
}
4748

48-
$context = '';
49+
$context = null;
4950
if ($options = $this->getArrayArgument($node, 2 < $firstNamedArgumentIndex ? 2 : 'options')) {
50-
$context = $this->getArrayStringValue($options, 'context') ?? '';
51+
$context = $this->getArrayStringValue($options, 'context');
5152
}
5253

5354
foreach ($messages as $message) {
54-
$this->addMessageToCatalogue($message, $context, $node->getStartLine());
55+
$this->addMessageToCatalogue($message, $context ?? Helper::UNDEFINED_DOMAIN, $node->getStartLine());
5556
}
5657

5758
return null;

0 commit comments

Comments
 (0)