Skip to content

Commit

Permalink
Merge fe541bb into 1fc7ea9
Browse files Browse the repository at this point in the history
  • Loading branch information
thenotsoft committed Jan 30, 2020
2 parents 1fc7ea9 + fe541bb commit 4215b90
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 49 deletions.
100 changes: 67 additions & 33 deletions src/Translator/Translator.php
@@ -1,7 +1,10 @@
<?php

declare(strict_types=1);

namespace Yiisoft\I18n\Translator;

use Yiisoft\I18n\MessageFormatterInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Yiisoft\I18n\Event\MissingTranslationEvent;
use Yiisoft\I18n\Locale;
Expand All @@ -10,41 +13,62 @@

class Translator implements TranslatorInterface
{
/**
* @var \Yiisoft\I18n\MessageReaderInterface
*/
private $messageReader;
/**
* @var \Psr\EventDispatcher\EventDispatcherInterface
*/
private $eventDispatcher;

/**
* @var array
*/
private $messages = [];
private EventDispatcherInterface $eventDispatcher;
private MessageReaderInterface $messageReader;
private ?MessageFormatterInterface $messageFormatter;
private array $messages = [];
private ?string $locale = null;
private ?string $defaultLocale = null;

public function __construct(
EventDispatcherInterface $eventDispatcher,
MessageReaderInterface $messageReader
MessageReaderInterface $messageReader,
MessageFormatterInterface $messageFormatter = null
) {
$this->messageReader = $messageReader;
$this->eventDispatcher = $eventDispatcher;
$this->messageFormatter = $messageFormatter;
}

/**
* Translates a message to the specified language.
* If a translation is not found, a {{@see \Yiisoft\I18n\Event\MissingTranslationEvent} event will be triggered.
* Sets the current locale.
*
* @param string $message the message to be translated
* @param string $category the message category
* @param string $localeString the target locale
* @return string|null the translated message or false if translation wasn't found or isn't required
* @param string $locale The locale
*/
public function setLocale(string $locale): void
{
$this->locale = $locale;
}

/**
* Returns the current locale.
*
* @return string The locale
*/
public function getLocale(): string
{
return $this->locale ?? $this->getDefaultLocale();
}

/**
* @param string $locale
*/
public function translate(?string $message, string $category = null, string $localeString = null): ?string
public function setDefaultLocale(string $locale): void
{
$this->defaultLocale = $locale;
}

/**
* {@inheritdoc}
*/
public function translate(
?string $message,
array $parameters = [],
string $category = null,
string $localeString = null
): ?string {
if ($localeString === null) {
$localeString = $this->getDefaultLocale();
$localeString = $this->getLocale();
}

if ($category === null) {
Expand All @@ -53,21 +77,31 @@ public function translate(?string $message, string $category = null, string $loc

$messages = $this->getMessages($category, $localeString);

if (array_key_exists($message, $messages)) {
return $messages[$message];
}
if (!array_key_exists($message, $messages)) {
$missingTranslation = new MissingTranslationEvent($category, $localeString, $message);
$this->eventDispatcher->dispatch($missingTranslation);

$locale = new Locale($localeString);
$fallback = $locale->fallbackLocale();

if ($fallback->asString() !== $locale->asString()) {
return $this->translate($message, $parameters, $category, $fallback->asString());
}

$missingTranslation = new MissingTranslationEvent($category, $localeString, $message);
$this->eventDispatcher->dispatch($missingTranslation);
$defaultFallback = (new Locale($this->getDefaultLocale()))->fallbackLocale();

$locale = new Locale($localeString);
$fallback = $locale->fallbackLocale();
if ($defaultFallback->asString() !== $fallback->asString()) {
return $this->translate($message, $parameters, $category, $this->getDefaultLocale());
}

if ($fallback->asString() !== $locale->asString()) {
return $messages[$message] = $this->translate($message, $category, $fallback->asString());
$messages[$message] = $message;
}

if ($this->messageFormatter === null) {
return $messages[$message];
}

return $messages[$message] = $message;
return $this->messageFormatter->format($messages[$message], $parameters, $localeString);
}

private function getMessages(string $category, string $language): array
Expand All @@ -88,6 +122,6 @@ protected function getDefaultCategory(): string

protected function getDefaultLocale(): string
{
return \Locale::getDefault();
return $this->defaultLocale ?? \Locale::getDefault();
}
}
19 changes: 18 additions & 1 deletion src/TranslatorInterface.php
@@ -1,8 +1,25 @@
<?php

declare(strict_types=1);

namespace Yiisoft\I18n;

interface TranslatorInterface
{
public function translate(?string $message, string $category = null, string $locale = null): ?string;
/**
* Translates a message to the specified language.
* If a translation is not found, a {{@see \Yiisoft\I18n\Event\MissingTranslationEvent} event will be triggered.
*
* @param string $message the message to be translated
* @param array $parameters An array of parameters for the message
* @param string $category the message category
* @param string $locale the target locale
* @return string|null the translated message or false if translation wasn't found or isn't required
*/
public function translate(
?string $message,
array $parameters = [],
string $category = null,
string $locale = null
): ?string;
}
99 changes: 84 additions & 15 deletions tests/TranslatorTest.php
Expand Up @@ -3,6 +3,7 @@
namespace Yii\I18n\Tests;

use PHPUnit\Framework\TestCase;
use Yiisoft\I18n\MessageFormatterInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Yiisoft\I18n\Event\MissingTranslationEvent;
use Yiisoft\I18n\MessageReaderInterface;
Expand All @@ -14,25 +15,77 @@ class TranslatorTest extends TestCase
* @dataProvider getTranslations
* @param $message
* @param $translate
* @param $expected
* @param $parameters
* @param $category
*/
public function testTranslation($message, $translate)
public function testTranslation($message, $translate, $expected, $parameters, $category)
{
$messageReader = $this->getMockBuilder(MessageReaderInterface::class)
->getMock();

$messageFormatter = null;
if ([] !== $parameters) {
$messageFormatter = $this->getMockBuilder(MessageFormatterInterface::class)
->getMock();
}

/**
* @var $translator Translator
*/
$translator = $this->getMockBuilder(Translator::class)
->setConstructorArgs([
$this->createMock(EventDispatcherInterface::class),
$messageReader,
])
->setConstructorArgs(
[
$this->createMock(EventDispatcherInterface::class),
$messageReader,
$messageFormatter
]
)
->enableProxyingToOriginalMethods()
->getMock();

$messageReader->expects($this->once())
->method('all')
->willReturn([$message => $translate]);

$this->assertEquals($translate, $translator->translate($message));
if ($messageFormatter instanceof MessageFormatterInterface) {
$messageFormatter->expects($this->once())
->method('format')
->willReturn($this->formatMessage($translate, $parameters));
}

$this->assertEquals($expected, $translator->translate($message, $parameters, $category));
}

public function testFallbackLocale()
{
$category = 'test';
$message = 'test';
$fallbackMessage = 'test de locale';

$messageReader = $this->getMockBuilder(MessageReaderInterface::class)
->getMock();

/**
* @var $translator Translator
*/
$translator = $this->getMockBuilder(Translator::class)
->setConstructorArgs(
[
$this->createMock(EventDispatcherInterface::class),
$messageReader,
]
)
->enableProxyingToOriginalMethods()
->getMock();

$translator->setDefaultLocale('de');

$messageReader
->method('all')
->will($this->onConsecutiveCalls([], ['test' => $fallbackMessage]));

$this->assertEquals($fallbackMessage, $translator->translate($message, [], $category, 'en'));
}

public function testMissingEventTriggered()
Expand All @@ -45,28 +98,44 @@ public function testMissingEventTriggered()
->setMethods(['dispatch'])
->getMock();

/**
* @var $translator Translator
*/
$translator = $this->getMockBuilder(Translator::class)
->setConstructorArgs([
$eventDispatcher,
$this->createMock(MessageReaderInterface::class),
])
->setConstructorArgs(
[
$eventDispatcher,
$this->createMock(MessageReaderInterface::class),
]
)
->enableProxyingToOriginalMethods()
->getMock();

$translator->setDefaultLocale('de');

$eventDispatcher
->expects($this->once())
->expects($this->at(0))
->method('dispatch')
->with(new MissingTranslationEvent($category, $language, $message));

$translator->translate($message, $category, $language);
$translator->translate($message, [], $category, $language);
}

public function getTranslations(): array
{
return [
[null, null],
[1, 1],
['test', 'test'],
[null, null, null, [], null],
['test', 'test', 'test', [], null],
['test {param}', 'translated {param}', 'translated param-value', ['param' => 'param-value'], null],
];
}

private function formatMessage(string $message, array $parameters): string
{
foreach ($parameters as $key => $value) {
$message = str_replace('{' . $key . '}', $value, $message);
}

return $message;
}
}

0 comments on commit 4215b90

Please sign in to comment.