Skip to content

Commit

Permalink
Upgrade translation mechanism
Browse files Browse the repository at this point in the history
Currently, defining translations is quite cumbersome, and the translator
callback is passed to the constructor of multiple classes, which makes
it quite ugly and could make translations inconsistent.

This commit completely changes how translations are done in Validation.
Instead of using a callback, it uses a specific class, and `Validator`
will pass that object through the objects that render the messages.
  • Loading branch information
henriquemoody committed Dec 5, 2024
1 parent 3833ad7 commit 34719fa
Show file tree
Hide file tree
Showing 30 changed files with 556 additions and 593 deletions.
55 changes: 45 additions & 10 deletions docs/04-message-translation.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,52 @@
# Message translation

You're also able to translate your message to another language with Validation.
The only thing one must do is to define the param `translator` as a callable that
will handle the translation overwriting the default factory:
You're able to translate message templates with Validation.

```php
Factory::setDefaultInstance(
(new Factory())->withTranslator('gettext')
);
use Respect\Validation\Message\Translator\GettextTranslator;
use Respect\Validation\ValidatorDefaults;

ValidatorDefaults::setTranslator(new GettextTranslator());
```

After that, if you call the methods `getMessage()`, `getMessages()`, or `getFullMessage()` from the `ValidationException`, the messages will be translated. The example above will work if you have `gettext` properly configured.

For non-static usage, pass the translator directly to the `Validator` constructor:

```php
use Respect\Validation\Factory;
use Respect\Validation\Message\StandardFormatter;
use Respect\Validation\Message\Translator\GettextTranslator;
use Respect\Validation\Validator;

$translator = new GettextTranslator();

$validator = new Validator(new Factory(), new StandardFormatter(), $translator);
```

The example above uses `gettext()` but you can use any other callable value, like
`[$translator, 'trans']` or `you_custom_function()`.
## Supported translators

After that, if you call `getMessage()`, `getMessages()`, or `getFullMessage()`,
the message will be translated.
- `ArrayTranslator`: Translates messages using an array of messages.
- `GettextTranslator`: Translates messages using the `gettext` extension.

## Custom translators

You can implement custom translators by creating a class that implements the `Translator` interface. Here's an example using the `stichoza/google-translate-php` package:

```php
use Respect\Validation\Message\Translator;
use Stichoza\GoogleTranslate\GoogleTranslate;

final class MyTranslator implements Translator
{
public function __construct(
private readonly GoogleTranslate $googleTranslate
) {
}

public function translate(string $message): string
{
return $this->googleTranslate->translate($message);
}
}
```
66 changes: 4 additions & 62 deletions library/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
use ReflectionException;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Exceptions\InvalidClassException;
use Respect\Validation\Message\Parameter\Processor;
use Respect\Validation\Message\Parameter\Raw;
use Respect\Validation\Message\Parameter\Stringify;
use Respect\Validation\Message\Parameter\Trans;
use Respect\Validation\Transformers\Aliases;
use Respect\Validation\Transformers\DeprecatedAge;
use Respect\Validation\Transformers\DeprecatedAttribute;
Expand All @@ -42,22 +38,8 @@ final class Factory
*/
private array $rulesNamespaces = ['Respect\\Validation\\Rules'];

/**
* @var callable
*/
private $translator;

private Processor $processor;

private Transformer $transformer;

private static Factory $defaultInstance;

public function __construct()
{
$this->translator = static fn (string $message) => $message;
$this->processor = new Raw(new Trans($this->translator, new Stringify()));
$this->transformer = new DeprecatedAttribute(
public function __construct(
private readonly Transformer $transformer = new DeprecatedAttribute(
new DeprecatedKey(
new DeprecatedKeyValue(
new DeprecatedMinAndMax(
Expand All @@ -67,16 +49,8 @@ public function __construct()
)
)
)
);
}

public static function getDefaultInstance(): self
{
if (!isset(self::$defaultInstance)) {
self::$defaultInstance = new self();
}

return self::$defaultInstance;
)
) {
}

public function withRuleNamespace(string $rulesNamespace): self
Expand All @@ -87,33 +61,6 @@ public function withRuleNamespace(string $rulesNamespace): self
return $clone;
}

public function withTranslator(callable $translator): self
{
$clone = clone $this;
$clone->translator = $translator;
$clone->processor = new Raw(new Trans($translator, new Stringify()));

return $clone;
}

public function withParameterProcessor(Processor $processor): self
{
$clone = clone $this;
$clone->processor = $processor;

return $clone;
}

public function getTranslator(): callable
{
return $this->translator;
}

public function getParameterProcessor(): Processor
{
return $this->processor;
}

/**
* @param mixed[] $arguments
*/
Expand All @@ -122,11 +69,6 @@ public function rule(string $ruleName, array $arguments = []): Validatable
return $this->createRuleSpec($this->transformer->transform(new RuleSpec($ruleName, $arguments)));
}

public static function setDefaultInstance(self $defaultInstance): void
{
self::$defaultInstance = $defaultInstance;
}

private function createRuleSpec(RuleSpec $ruleSpec): Validatable
{
$rule = $this->createRule($ruleSpec->name, $ruleSpec->arguments);
Expand Down
6 changes: 3 additions & 3 deletions library/Message/Formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ interface Formatter
/**
* @param array<string, mixed> $templates
*/
public function main(Result $result, array $templates): string;
public function main(Result $result, array $templates, Translator $translator): string;

/**
* @param array<string, mixed> $templates
*/
public function full(Result $result, array $templates, int $depth = 0): string;
public function full(Result $result, array $templates, Translator $translator, int $depth = 0): string;

/**
* @param array<string, mixed> $templates
*
* @return array<string, mixed>
*/
public function array(Result $result, array $templates): array;
public function array(Result $result, array $templates, Translator $translator): array;
}
15 changes: 0 additions & 15 deletions library/Message/Parameter/Processor.php

This file was deleted.

30 changes: 0 additions & 30 deletions library/Message/Parameter/Raw.php

This file was deleted.

25 changes: 0 additions & 25 deletions library/Message/Parameter/Stringify.php

This file was deleted.

35 changes: 0 additions & 35 deletions library/Message/Parameter/Trans.php

This file was deleted.

2 changes: 1 addition & 1 deletion library/Message/Renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@

interface Renderer
{
public function render(Result $result, ?string $template = null): string;
public function render(Result $result, Translator $translator, ?string $template = null): string;
}
30 changes: 19 additions & 11 deletions library/Message/StandardFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,29 @@
final class StandardFormatter implements Formatter
{
public function __construct(
private readonly Renderer $renderer,
private readonly Renderer $renderer = new StandardRenderer(),
) {
}

/**
* @param array<string, mixed> $templates
*/
public function main(Result $result, array $templates): string
public function main(Result $result, array $templates, Translator $translator): string
{
$selectedTemplates = $this->selectTemplates($result, $templates);
if (!$this->isFinalTemplate($result, $selectedTemplates)) {
foreach ($this->extractDeduplicatedChildren($result) as $child) {
return $this->main($child, $selectedTemplates);
return $this->main($child, $selectedTemplates, $translator);
}
}

return $this->renderer->render($this->getTemplated($result, $selectedTemplates));
return $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator);
}

/**
* @param array<string, mixed> $templates
*/
public function full(Result $result, array $templates, int $depth = 0): string
public function full(Result $result, array $templates, Translator $translator, int $depth = 0): string
{
$selectedTemplates = $this->selectTemplates($result, $templates);
$isFinalTemplate = $this->isFinalTemplate($result, $selectedTemplates);
Expand All @@ -62,14 +62,14 @@ public function full(Result $result, array $templates, int $depth = 0): string
$rendered .= sprintf(
'%s- %s' . PHP_EOL,
$indentation,
$this->renderer->render($this->getTemplated($result, $selectedTemplates)),
$this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator),
);
$depth++;
}

if (!$isFinalTemplate) {
foreach ($this->extractDeduplicatedChildren($result) as $child) {
$rendered .= $this->full($child, $selectedTemplates, $depth);
$rendered .= $this->full($child, $selectedTemplates, $translator, $depth);
$rendered .= PHP_EOL;
}
}
Expand All @@ -82,17 +82,23 @@ public function full(Result $result, array $templates, int $depth = 0): string
*
* @return array<string, mixed>
*/
public function array(Result $result, array $templates): array
public function array(Result $result, array $templates, Translator $translator): array
{
$selectedTemplates = $this->selectTemplates($result, $templates);
$deduplicatedChildren = $this->extractDeduplicatedChildren($result);
if (count($deduplicatedChildren) === 0 || $this->isFinalTemplate($result, $selectedTemplates)) {
return [$result->id => $this->renderer->render($this->getTemplated($result, $selectedTemplates))];
return [
$result->id => $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator),
];
}

$messages = [];
foreach ($deduplicatedChildren as $child) {
$messages[$child->id] = $this->array($child, $this->selectTemplates($child, $selectedTemplates));
$messages[$child->id] = $this->array(
$child,
$this->selectTemplates($child, $selectedTemplates),
$translator
);
if (count($messages[$child->id]) !== 1) {
continue;
}
Expand All @@ -101,7 +107,9 @@ public function array(Result $result, array $templates): array
}

if (count($messages) > 1) {
$self = ['__root__' => $this->renderer->render($this->getTemplated($result, $selectedTemplates))];
$self = [
'__root__' => $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator),
];

return $self + $messages;
}
Expand Down
Loading

0 comments on commit 34719fa

Please sign in to comment.