Translation: added pluralization support

This commit is contained in:
Igor Scheller 2018-10-18 23:34:18 +02:00 committed by msquare
parent 90e1a94962
commit b443b53919
9 changed files with 130 additions and 26 deletions

View File

@ -23,10 +23,15 @@ function user_angeltypes_unconfirmed_hint()
. '</a>'; . '</a>';
} }
return sprintf(ngettext('There is %d unconfirmed angeltype.', 'There are %d unconfirmed angeltypes.', $count = count($unconfirmed_user_angeltypes);
count($unconfirmed_user_angeltypes)), return _e(
count($unconfirmed_user_angeltypes)) . ' ' . __('Angel types which need approvals:') . ' ' . join(', ', 'There is %d unconfirmed angeltype.',
$unconfirmed_links); 'There are %d unconfirmed angeltypes.',
$count,
[$count]
)
. ' ' . __('Angel types which need approvals:')
. ' ' . join(', ', $unconfirmed_links);
} }
/** /**

View File

@ -175,10 +175,8 @@ class ShiftCalendarShiftRenderer
$angeltype, $angeltype,
$shift_entries $shift_entries
); );
$inner_text = sprintf( $freeEntriesCount = $shift_signup_state->getFreeEntries();
ngettext('%d helper needed', '%d helpers needed', $shift_signup_state->getFreeEntries()), $inner_text = _e('%d helper needed', '%d helpers needed', $freeEntriesCount, [$freeEntriesCount]);
$shift_signup_state->getFreeEntries()
);
switch ($shift_signup_state->getState()) { switch ($shift_signup_state->getState()) {
case ShiftSignupState::ADMIN: case ShiftSignupState::ADMIN:

View File

@ -727,10 +727,7 @@ function User_view_state_admin($freeloader, $user_source)
if ($user_source['got_voucher'] > 0) { if ($user_source['got_voucher'] > 0) {
$state[] = '<span class="text-success">' $state[] = '<span class="text-success">'
. glyph('cutlery') . glyph('cutlery')
. sprintf( . _e('Got %s voucher', 'Got %s vouchers', $user_source['got_voucher'], [$user_source['got_voucher']])
ngettext('Got %s voucher', 'Got %s vouchers', $user_source['got_voucher']),
$user_source['got_voucher']
)
. '</span>'; . '</span>';
} else { } else {
$state[] = '<span class="text-danger">' . __('Got no vouchers') . '</span>'; $state[] = '<span class="text-danger">' . __('Got no vouchers') . '</span>';

View File

@ -39,11 +39,39 @@ class Translator
{ {
$translated = $this->translateGettext($key); $translated = $this->translateGettext($key);
if (!empty($replace)) { return $this->replaceText($translated, $replace);
$translated = call_user_func_array('sprintf', array_merge([$translated], $replace)); }
/**
* Get the translation for a given key
*
* @param string $key
* @param string $pluralKey
* @param int $number
* @param array $replace
* @return string
*/
public function translatePlural(string $key, string $pluralKey, int $number, array $replace = []): string
{
$translated = $this->translateGettextPlural($key, $pluralKey, $number);
return $this->replaceText($translated, $replace);
}
/**
* Replace placeholders
*
* @param string $key
* @param array $replace
* @return mixed|string
*/
protected function replaceText(string $key, array $replace = [])
{
if (empty($replace)) {
return $key;
} }
return $translated; return call_user_func_array('sprintf', array_merge([$key], $replace));
} }
/** /**
@ -55,7 +83,21 @@ class Translator
*/ */
protected function translateGettext(string $key): string protected function translateGettext(string $key): string
{ {
return _($key); return gettext($key);
}
/**
* Translate the key via gettext
*
* @param string $key
* @param string $keyPlural
* @param int $number
* @return string
* @codeCoverageIgnore
*/
protected function translateGettextPlural(string $key, string $keyPlural, int $number): string
{
return ngettext($key, $keyPlural, $number);
} }
/** /**

View File

@ -44,6 +44,7 @@ class Translation extends TwigExtension
{ {
return [ return [
new TwigFunction('__', [$this->translator, 'translate']), new TwigFunction('__', [$this->translator, 'translate']),
new TwigFunction('_e', [$this->translator, 'translatePlural']),
]; ];
} }

View File

@ -153,6 +153,23 @@ function __($key, $replace = [])
return $translator->translate($key, $replace); return $translator->translate($key, $replace);
} }
/**
* Translate the given message
*
* @param string $key
* @param string $keyPlural
* @param int $number
* @param array $replace
* @return string
*/
function _e($key, $keyPlural, $number, $replace = [])
{
/** @var Translator $translator */
$translator = app('translator');
return $translator->translatePlural($key, $keyPlural, $number, $replace);
}
/** /**
* @param string $path * @param string $path
* @param array $parameters * @param array $parameters

View File

@ -10,12 +10,12 @@ use stdClass;
class TranslatorTest extends ServiceProviderTest class TranslatorTest extends ServiceProviderTest
{ {
/** /**
* @covers \Engelsystem\Helpers\Translator::__construct() * @covers \Engelsystem\Helpers\Translator::__construct
* @covers \Engelsystem\Helpers\Translator::setLocale() * @covers \Engelsystem\Helpers\Translator::setLocale
* @covers \Engelsystem\Helpers\Translator::setLocales() * @covers \Engelsystem\Helpers\Translator::setLocales
* @covers \Engelsystem\Helpers\Translator::getLocale() * @covers \Engelsystem\Helpers\Translator::getLocale
* @covers \Engelsystem\Helpers\Translator::getLocales() * @covers \Engelsystem\Helpers\Translator::getLocales
* @covers \Engelsystem\Helpers\Translator::hasLocale() * @covers \Engelsystem\Helpers\Translator::hasLocale
*/ */
public function testInit() public function testInit()
{ {
@ -47,7 +47,8 @@ class TranslatorTest extends ServiceProviderTest
} }
/** /**
* @covers \Engelsystem\Helpers\Translator::translate() * @covers \Engelsystem\Helpers\Translator::translate
* @covers \Engelsystem\Helpers\Translator::replaceText
*/ */
public function testTranslate() public function testTranslate()
{ {
@ -56,14 +57,36 @@ class TranslatorTest extends ServiceProviderTest
->setConstructorArgs(['de_DE.UTF-8', ['de_DE.UTF-8' => 'Deutsch']]) ->setConstructorArgs(['de_DE.UTF-8', ['de_DE.UTF-8' => 'Deutsch']])
->setMethods(['translateGettext']) ->setMethods(['translateGettext'])
->getMock(); ->getMock();
$translator->expects($this->once()) $translator->expects($this->exactly(2))
->method('translateGettext') ->method('translateGettext')
->with('My favourite number is %u!') ->withConsecutive(['Hello!'], ['My favourite number is %u!'])
->willReturn('Meine Lieblingszahl ist die %u!'); ->willReturnOnConsecutiveCalls('Hallo!', 'Meine Lieblingszahl ist die %u!');
$return = $translator->translate('Hello!');
$this->assertEquals('Hallo!', $return);
$return = $translator->translate('My favourite number is %u!', [3]); $return = $translator->translate('My favourite number is %u!', [3]);
$this->assertEquals('Meine Lieblingszahl ist die 3!', $return); $this->assertEquals('Meine Lieblingszahl ist die 3!', $return);
} }
/**
* @covers \Engelsystem\Helpers\Translator::translatePlural
*/
public function testTranslatePlural()
{
/** @var Translator|MockObject $translator */
$translator = $this->getMockBuilder(Translator::class)
->setConstructorArgs(['de_DE.UTF-8', ['de_DE.UTF-8' => 'Deutsch']])
->setMethods(['translateGettextPlural'])
->getMock();
$translator->expects($this->once())
->method('translateGettextPlural')
->with('%s apple', '%s apples', 2)
->willReturn('2 Äpfel');
$return = $translator->translatePlural('%s apple', '%s apples', 2, [2]);
$this->assertEquals('2 Äpfel', $return);
}
} }

View File

@ -218,6 +218,26 @@ class HelpersTest extends TestCase
$this->assertEquals('Lorem foo Ipsum', __('Lorem %s Ipsum', ['foo'])); $this->assertEquals('Lorem foo Ipsum', __('Lorem %s Ipsum', ['foo']));
} }
/**
* @covers \_e
*/
public function testTranslatePlural()
{
/** @var Translator|MockObject $translator */
$translator = $this->getMockBuilder(Translator::class)
->disableOriginalConstructor()
->getMock();
$this->getAppMock('translator', $translator);
$translator->expects($this->once())
->method('translatePlural')
->with('One: %u', 'Multiple: %u', 4, [4])
->willReturn('Multiple: 4');
$this->assertEquals('Multiple: 4', _e('One: %u', 'Multiple: %u', 4, [4]));
}
/** /**
* @covers \url * @covers \url
*/ */

View File

@ -40,6 +40,7 @@ class TranslationTest extends ExtensionTest
$functions = $extension->getFunctions(); $functions = $extension->getFunctions();
$this->assertExtensionExists('__', [$translator, 'translate'], $functions); $this->assertExtensionExists('__', [$translator, 'translate'], $functions);
$this->assertExtensionExists('_e', [$translator, 'translatePlural'], $functions);
} }
/** /**