chevere/chevere

View on GitHub
src/Translator/PoMaker.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

/*
 * This file is part of Chevere.
 *
 * (c) Rodolfo Berrios <rodolfo@chevere.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace Chevere\Translator;

use Chevere\Filesystem\File;
use Chevere\Filesystem\Interfaces\DirInterface;
use function Chevere\Iterator\recursiveDirectoryIteratorFor;
use Chevere\Iterator\RecursiveFileFilterIterator;
use Chevere\Message\Message;
use Chevere\Throwable\Exceptions\BadMethodCallException;
use Chevere\Throwable\Exceptions\LogicException;
use Chevere\Translator\Interfaces\PoMakerInterface;
use Chevere\Writer\NullWriter;
use Chevere\Writer\Traits\WriterTrait;
use Gettext\Generator\PoGenerator;
use Gettext\Scanner\PhpScanner;
use Gettext\Translations;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Throwable;

/**
 * @method self withWriter(WriterInterface $writer)
 */
final class PoMaker implements PoMakerInterface
{
    use WriterTrait;

    public const FUNCTIONS = [
        '__' => 'gettext',
        '__f' => 'gettext',
        '__t' => 'gettext',
        '__n' => 'ngettext',
        '__nf' => 'ngettext',
        '__nt' => 'ngettext',
    ];

    private DirInterface $sourceDir;

    private PhpScanner $phpScanner;

    public function __construct(
        private string $locale,
        private string $domain
    ) {
        $this->writer = new NullWriter();
    }

    public function withScanFor(DirInterface $sourceDir): self
    {
        $new = clone $this;
        $sourceDir->assertExists();
        $new->sourceDir = $sourceDir;
        $new->phpScanner = new PhpScanner(Translations::create($new->domain));
        $new->phpScanner->setDefaultDomain($new->domain);
        $new->phpScanner->setFunctions(self::FUNCTIONS);
        $iterator = $new->getIterator();
        $this->writer->write(
            sprintf("📂 Starting dir %s iteration\n", $new->sourceDir->path()->__toString())
        );
        $iterator->rewind();
        while ($iterator->valid()) {
            $pathName = $iterator->current()->getPathName();
            $new->writer->write("- File ${pathName}\n");

            try {
                $new->phpScanner->scanFile($pathName);
            }
            // @codeCoverageIgnoreStart
            catch (Throwable $e) {
                throw new LogicException(
                    previous: $e,
                    message: new Message('Unable to scan file.')
                );
            }
            // @codeCoverageIgnoreEnd
            $iterator->next();
        }
        $this->writer->write("💯 Done!\n");

        return $new;
    }

    public function make(DirInterface $targetDir): void
    {
        if (!isset($this->phpScanner)) {
            throw new BadMethodCallException(
                (new Message('Unable to call %method% without a %type% instance'))
                    ->code('%method%', __METHOD__)
                    ->code('%type%', PhpScanner::class)
            );
        }
        $generator = new PoGenerator();
        $targetDir = $targetDir->getChild($this->locale . '/');
        $targetDir->createIfNotExists();
        $poFile = new File($targetDir->path()->getChild($this->domain . '.po'));
        $poFile->removeIfExists();
        /**
         * @var Translations $translations
         */
        foreach ($this->phpScanner->getTranslations() as $translations) {
            $translations->setLanguage($this->locale);

            try {
                $generator->generateFile($translations, $poFile->path()->__toString());
            }
            // @codeCoverageIgnoreStart
            catch (\InvalidArgumentException $e) {
                throw new LogicException(
                    previous: $e,
                    message: new Message('Unable to make translation.')
                );
            }
            // @codeCoverageIgnoreEnd

            break;
        }
    }

    private function getIterator(): RecursiveIteratorIterator
    {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveFileFilterIterator(
                recursiveDirectoryIteratorFor($this->sourceDir, RecursiveDirectoryIterator::SKIP_DOTS),
                '.php'
            )
        );
        $iterator->rewind();

        return $iterator;
    }
}