Skip to content

Commit

Permalink
feat: add console commands
Browse files Browse the repository at this point in the history
  • Loading branch information
sitepark-veltrup committed Jan 9, 2025
1 parent f5088a1 commit 0d3a457
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 16 deletions.
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
"php": ">=8.1 <8.5.0",
"deeplcom/deepl-php": "^1.10",
"nyholm/psr7": "^1.8",
"symfony/cache": "^7.2",
"symfony/cache": "^6.3 || ^7.0",
"symfony/config": "^6.3 || ^7.0",
"symfony/console": "^6.3 || ^7.0",
"symfony/dependency-injection": "^6.3 || ^7.0",
"symfony/http-client": "^7.2",
"symfony/http-client": "^6.3 || ^7.0",
"symfony/http-kernel": "^6.3 || ^7.0",
"symfony/yaml": "^6.3 || ^7.0"
},
Expand Down
21 changes: 21 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
services:
_defaults:
autowire: true
autoconfigure: true
_instanceof:
Symfony\Component\Console\Command\Command:
tags: ['command']

Atoolo\Translator\Console\:
resource: '../src/Console'

Atoolo\Translator\Service\:
resource: '../src/Service'

Atoolo\Translator\Adapter\:
resource: '../src/Adapter'

atoolo_translator.textHasher:
class: Atoolo\Translator\Service\TextHasher

atoolo_translator.translator.adapter:
class: Atoolo\Translator\Adapter\DeeplAdapter
17 changes: 15 additions & 2 deletions src/Adapter/DeeplAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Atoolo\Translator\Dto\Format;
use Atoolo\Translator\Dto\TranslationParameter;
use DeepL\DeepLException;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class DeeplAdapter extends AbstractAdapter
{
Expand All @@ -15,7 +16,7 @@ class DeeplAdapter extends AbstractAdapter
/**
* @throws DeepLException
*/
public function __construct(string $authKey)
public function __construct(#[Autowire('%atoolo_translator.adapter.deepl.authKey%')] string $authKey)
{
$this->translator = new \DeepL\Translator($authKey);
}
Expand All @@ -31,7 +32,19 @@ public function translate(array $text, TranslationParameter $parameter): array
if ($parameter->format === Format::HTML) {
$options['tagHandling'] = 'html';
}
$result = $this->translator->translateText($text, $parameter->sourceLang, $parameter->targetLang, $options);
$result = $this->translator->translateText(
$text,
$this->normalizeLang($parameter->sourceLang),
$this->normalizeLang($parameter->targetLang),
$options
);
return array_map(static fn($translation) => $translation->text, $result);
}

private function normalizeLang(string $lang): string{
return match ($lang) {
'en' => 'en-us',
default => $lang,
};
}
}
22 changes: 22 additions & 0 deletions src/Console/Application.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Atoolo\Translator\Console;

use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Command\Command;

class Application extends BaseApplication
{
/**
* @param iterable<Command> $commands
*/
public function __construct(iterable $commands = [])
{
parent::__construct();
foreach ($commands as $command) {
$this->add($command);
}
}
}
86 changes: 86 additions & 0 deletions src/Console/Command/CacheGet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

namespace Atoolo\Translator\Console\Command;

use Atoolo\Translator\Console\Command\Io\TypifiedInput;
use Atoolo\Translator\Dto\Format;
use Atoolo\Translator\Dto\TranslationParameter;
use Atoolo\Translator\Service\TextHasher;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\Cache\CacheInterface;

#[AsCommand(
name: 'translator:cache:get',
description: 'get cached translations',
)]
class CacheGet extends Command
{
public function __construct(
private readonly CacheInterface $translationCache,
#[Autowire(service: 'atoolo_translator.textHasher')]
private readonly TextHasher $textHasher,
) {
parent::__construct();
}

protected function configure(): void
{
$this
->setHelp('Command to list cached translations.')
->addArgument(
'text',
InputArgument::REQUIRED,
'Text whose translation is to be retrieved from the cache.',
)
->addOption(
'sourceLang',
null,
InputOption::VALUE_OPTIONAL,
'Language to be used. (de, en, fr, it, ...)',
'de',
)
->addOption(
'targetLang',
'l',
InputOption::VALUE_REQUIRED,
'Language to be used. (de, en, fr, it, ...)',
);
}

/**
* @throws InvalidArgumentException
*/
protected function execute(
InputInterface $input,
OutputInterface $output,
): int {

$typedInput = new TypifiedInput($input);
$io = new SymfonyStyle($input, $output);

$sourceLang = $typedInput->getStringOption('sourceLang');
$targetLang = $typedInput->getStringOption('targetLang');
$text = $typedInput->getStringArgument('text');

$translationParamter = new TranslationParameter($sourceLang, $targetLang, Format::TEXT);
$hash = $this->textHasher->hash($text, $translationParamter);

$translation = $this->translationCache->get($hash, function () {
return null;
});

$io->text($translation ?? 'No translation found in cache.');

Check failure on line 82 in src/Console/Command/CacheGet.php

View workflow job for this annotation

GitHub Actions / verify / Composer Verify (PHP 8.2)

Variable $translation on left side of ?? always exists and is always null.

Check failure on line 82 in src/Console/Command/CacheGet.php

View workflow job for this annotation

GitHub Actions / verify / Composer Verify (PHP 8.3)

Variable $translation on left side of ?? always exists and is always null.

Check failure on line 82 in src/Console/Command/CacheGet.php

View workflow job for this annotation

GitHub Actions / verify / Composer Verify (PHP 8.4)

Variable $translation on left side of ?? always exists and is always null.

return Command::SUCCESS;
}
}
35 changes: 35 additions & 0 deletions src/Console/Command/Io/TypifiedInput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Atoolo\Translator\Console\Command\Io;

use InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;

class TypifiedInput
{
public function __construct(private readonly InputInterface $input) {}

public function getStringOption(string $name): string
{
$value = $this->input->getOption($name);
if (!is_string($value)) {
throw new InvalidArgumentException(
'option ' . $name . ' must be a string: ' . $value,
);
}
return $value;
}

public function getStringArgument(string $name): string
{
$value = $this->input->getArgument($name);
if (!is_string($value)) {
throw new InvalidArgumentException(
'argument ' . $name . ' must be a string',
);
}
return $value;
}
}
94 changes: 94 additions & 0 deletions src/Console/Command/Translate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace Atoolo\Translator\Console\Command;

use Atoolo\Translator\Console\Command\Io\TypifiedInput;
use Atoolo\Translator\Dto\Format;
use Atoolo\Translator\Dto\TranslationParameter;
use Atoolo\Translator\Service\TextHasher;
use Atoolo\Translator\Service\Translator;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\Cache\CacheInterface;

#[AsCommand(
name: 'translator:translate',
description: 'translate text, and cache the result',
)]
class Translate extends Command
{
public function __construct(
private readonly Translator $translator,
) {
parent::__construct();
}

protected function configure(): void
{
$this
->setHelp('Command to translate a text.')
->addArgument(
'text',
InputArgument::REQUIRED,
'Text to translate.',
)
->addOption(
'sourceLang',
null,
InputOption::VALUE_OPTIONAL,
'Language to be used. (de, en, fr, it, ...)',
'de',
)
->addOption(
'targetLang',
'l',
InputOption::VALUE_REQUIRED,
'Language to be used. (de, en, fr, it, ...)',
)
->addOption(
'format',
null,
InputOption::VALUE_OPTIONAL,
'text | html',
'text',
);
}

/**
* @throws InvalidArgumentException
*/
protected function execute(
InputInterface $input,
OutputInterface $output,
): int {

$typedInput = new TypifiedInput($input);
$io = new SymfonyStyle($input, $output);

$sourceLang = $typedInput->getStringOption('sourceLang');
$targetLang = $typedInput->getStringOption('targetLang');
$text = $typedInput->getStringArgument('text');
$format = Format::from($typedInput->getStringOption('format'));

$parameter = new TranslationParameter(
sourceLang: $sourceLang,
targetLang: $targetLang,
format: $format,
);

$translated = $this->translator->translate([$text], $parameter);

$io->text($translated[0]);

return Command::SUCCESS;
}
}
6 changes: 3 additions & 3 deletions src/Dto/Format.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace Atoolo\Translator\Dto;

enum Format
enum Format: string
{
case TEXT;
case HTML;
case TEXT = 'text';
case HTML = 'html';
}
Loading

0 comments on commit 0d3a457

Please sign in to comment.