Issue #2111349 by dawehner, tim.plunkett: Move format_plural to the string translation service and format_interval to the date service.
parent
6d0ba6bfd7
commit
4b06b8bd32
|
@ -585,7 +585,7 @@ services:
|
|||
arguments: ['@module_handler']
|
||||
date:
|
||||
class: Drupal\Core\Datetime\Date
|
||||
arguments: ['@entity.manager', '@language_manager']
|
||||
arguments: ['@entity.manager', '@language_manager', '@string_translation']
|
||||
feed.bridge.reader:
|
||||
class: Drupal\Component\Bridge\ZfExtensionManagerSfContainer
|
||||
calls:
|
||||
|
|
|
@ -897,39 +897,11 @@ function format_xml_elements($array) {
|
|||
*
|
||||
* @see t()
|
||||
* @see format_string()
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use \Drupal::translation()->formatPlural()
|
||||
*/
|
||||
function format_plural($count, $singular, $plural, array $args = array(), array $options = array()) {
|
||||
$args['@count'] = $count;
|
||||
// Join both forms to search a translation.
|
||||
$tranlatable_string = implode(LOCALE_PLURAL_DELIMITER, array($singular, $plural));
|
||||
// Translate as usual.
|
||||
$translated_strings = t($tranlatable_string, $args, $options);
|
||||
// Split joined translation strings into array.
|
||||
$translated_array = explode(LOCALE_PLURAL_DELIMITER, $translated_strings);
|
||||
|
||||
if ($count == 1) {
|
||||
return $translated_array[0];
|
||||
}
|
||||
|
||||
// Get the plural index through the gettext formula.
|
||||
// @todo implement static variable to minimize function_exists() usage.
|
||||
$index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
|
||||
if ($index == 0) {
|
||||
// Singular form.
|
||||
return $translated_array[0];
|
||||
}
|
||||
else {
|
||||
if (isset($translated_array[$index])) {
|
||||
// N-th plural form.
|
||||
return $translated_array[$index];
|
||||
}
|
||||
else {
|
||||
// If the index cannot be computed or there's no translation, use
|
||||
// the second plural form as a fallback (which allows for most flexiblity
|
||||
// with the replaceable @count value).
|
||||
return $translated_array[1];
|
||||
}
|
||||
}
|
||||
return \Drupal::translation()->formatPlural($count, $singular, $plural, $args, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1007,31 +979,11 @@ function format_size($size, $langcode = NULL) {
|
|||
*
|
||||
* @return
|
||||
* A translated string representation of the interval.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use \Drupal::service('date')->formatInterval().
|
||||
*/
|
||||
function format_interval($interval, $granularity = 2, $langcode = NULL) {
|
||||
$units = array(
|
||||
'1 year|@count years' => 31536000,
|
||||
'1 month|@count months' => 2592000,
|
||||
'1 week|@count weeks' => 604800,
|
||||
'1 day|@count days' => 86400,
|
||||
'1 hour|@count hours' => 3600,
|
||||
'1 min|@count min' => 60,
|
||||
'1 sec|@count sec' => 1
|
||||
);
|
||||
$output = '';
|
||||
foreach ($units as $key => $value) {
|
||||
$key = explode('|', $key);
|
||||
if ($interval >= $value) {
|
||||
$output .= ($output ? ' ' : '') . format_plural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
|
||||
$interval %= $value;
|
||||
$granularity--;
|
||||
}
|
||||
|
||||
if ($granularity == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $output ? $output : t('0 sec', array(), array('langcode' => $langcode));
|
||||
return \Drupal::service('date')->formatInterval($interval, $granularity, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@ use Drupal\Core\Datetime\DrupalDateTime;
|
|||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
|
||||
/**
|
||||
* Provides a service to handler various date related functionality.
|
||||
|
@ -42,6 +43,25 @@ class Date {
|
|||
protected $country = NULL;
|
||||
protected $dateFormats = array();
|
||||
|
||||
/**
|
||||
* Contains the different date interval units.
|
||||
*
|
||||
* This array is keyed by strings representing the unit (e.g.
|
||||
* '1 year|@count years') and with the amount of values of the unit in
|
||||
* seconds.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $units = array(
|
||||
'1 year|@count years' => 31536000,
|
||||
'1 month|@count months' => 2592000,
|
||||
'1 week|@count weeks' => 604800,
|
||||
'1 day|@count days' => 86400,
|
||||
'1 hour|@count hours' => 3600,
|
||||
'1 min|@count min' => 60,
|
||||
'1 sec|@count sec' => 1,
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructs a Date object.
|
||||
*
|
||||
|
@ -49,10 +69,13 @@ class Date {
|
|||
* The entity manager.
|
||||
* @param \Drupal\Core\Language\LanguageManager $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
|
||||
* The string translation.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, LanguageManager $language_manager) {
|
||||
public function __construct(EntityManagerInterface $entity_manager, LanguageManager $language_manager, TranslationInterface $translation) {
|
||||
$this->dateFormatStorage = $entity_manager->getStorageController('date_format');
|
||||
$this->languageManager = $language_manager;
|
||||
$this->stringTranslation = $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,6 +150,47 @@ class Date {
|
|||
return Xss::filter($date->format($format, $settings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a time interval with the requested granularity.
|
||||
*
|
||||
* @param int $interval
|
||||
* The length of the interval in seconds.
|
||||
* @param int $granularity
|
||||
* (optional) How many different units to display in the string (2 by
|
||||
* default).
|
||||
* @param string $langcode
|
||||
* (optional) Language code to translate to a language other than what is
|
||||
* used to display the page. Defaults to NULL.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the interval.
|
||||
*/
|
||||
public function formatInterval($interval, $granularity = 2, $langcode = NULL) {
|
||||
$output = '';
|
||||
foreach ($this->units as $key => $value) {
|
||||
$key = explode('|', $key);
|
||||
if ($interval >= $value) {
|
||||
$output .= ($output ? ' ' : '') . $this->stringTranslation->formatPlural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
|
||||
$interval %= $value;
|
||||
$granularity--;
|
||||
}
|
||||
|
||||
if ($granularity == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $output ? $output : $this->t('0 sec', array(), array('langcode' => $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string to the current language or to a given language.
|
||||
*
|
||||
* See the t() documentation for details.
|
||||
*/
|
||||
protected function t($string, array $args = array(), array $options = array()) {
|
||||
return $this->stringTranslation->translate($string, $args, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given format pattern for the given langcode.
|
||||
*
|
||||
|
|
|
@ -31,4 +31,53 @@ interface TranslationInterface {
|
|||
*/
|
||||
public function translate($string, array $args = array(), array $options = array());
|
||||
|
||||
/**
|
||||
* Formats a string containing a count of items.
|
||||
*
|
||||
* This function ensures that the string is pluralized correctly. Since t() is
|
||||
* called by this function, make sure not to pass already-localized strings to
|
||||
* it.
|
||||
*
|
||||
* For example:
|
||||
* @code
|
||||
* $output = $string_translation->formatPlural($node->comment_count, '1 comment', '@count comments');
|
||||
* @endcode
|
||||
*
|
||||
* Example with additional replacements:
|
||||
* @code
|
||||
* $output = $string_translation->formatPlural($update_count,
|
||||
* 'Changed the content type of 1 post from %old-type to %new-type.',
|
||||
* 'Changed the content type of @count posts from %old-type to %new-type.',
|
||||
* array('%old-type' => $info->old_type, '%new-type' => $info->new_type));
|
||||
* @endcode
|
||||
*
|
||||
* @param int $count
|
||||
* The item count to display.
|
||||
* @param string $singular
|
||||
* The string for the singular case. Make sure it is clear this is singular,
|
||||
* to ease translation (e.g. use "1 new comment" instead of "1 new"). Do not
|
||||
* use @count in the singular string.
|
||||
* @param string $plural
|
||||
* The string for the plural case. Make sure it is clear this is plural, to
|
||||
* ease translation. Use @count in place of the item count, as in
|
||||
* "@count new comments".
|
||||
* @param array $args
|
||||
* An associative array of replacements to make after translation. Instances
|
||||
* of any key in this array are replaced with the corresponding value.
|
||||
* Based on the first character of the key, the value is escaped and/or
|
||||
* themed. See format_string(). Note that you do not need to include @count
|
||||
* in this array; this replacement is done automatically for the plural case.
|
||||
* @param array $options
|
||||
* An associative array of additional options. See t() for allowed keys.
|
||||
*
|
||||
* @return string
|
||||
* A translated string.
|
||||
*
|
||||
* @see self::translate
|
||||
* @see \Drupal\Component\Utility\String
|
||||
* @see t()
|
||||
* @see format_string()
|
||||
*/
|
||||
public function formatPlural($count, $singular, $plural, array $args = array(), array $options = array());
|
||||
|
||||
}
|
||||
|
|
|
@ -126,6 +126,43 @@ class TranslationManager implements TranslationInterface, TranslatorInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatPlural($count, $singular, $plural, array $args = array(), array $options = array()) {
|
||||
$args['@count'] = $count;
|
||||
// Join both forms to search a translation.
|
||||
$translatable_string = implode(LOCALE_PLURAL_DELIMITER, array($singular, $plural));
|
||||
// Translate as usual.
|
||||
$translated_strings = $this->translate($translatable_string, $args, $options);
|
||||
// Split joined translation strings into array.
|
||||
$translated_array = explode(LOCALE_PLURAL_DELIMITER, $translated_strings);
|
||||
|
||||
if ($count == 1) {
|
||||
return $translated_array[0];
|
||||
}
|
||||
|
||||
// Get the plural index through the gettext formula.
|
||||
// @todo implement static variable to minimize function_exists() usage.
|
||||
$index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
|
||||
if ($index == 0) {
|
||||
// Singular form.
|
||||
return $translated_array[0];
|
||||
}
|
||||
else {
|
||||
if (isset($translated_array[$index])) {
|
||||
// N-th plural form.
|
||||
return $translated_array[$index];
|
||||
}
|
||||
else {
|
||||
// If the index cannot be computed or there's no translation, use
|
||||
// the second plural form as a fallback (which allows for most flexiblity
|
||||
// with the replaceable @count value).
|
||||
return $translated_array[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default langcode.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Datetime\DateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Datetime;
|
||||
|
||||
use Drupal\Core\Datetime\Date;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the date service.
|
||||
*
|
||||
* @group Drupal
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\Date
|
||||
*/
|
||||
class DateTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The mocked entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The mocked language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManager|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The mocked string translation.
|
||||
*
|
||||
* @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $stringTranslation;
|
||||
|
||||
/**
|
||||
* The tested date service class.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\Date
|
||||
*/
|
||||
protected $date;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Date service test.',
|
||||
'description' => 'Tests the date service.',
|
||||
'group' => 'System'
|
||||
);
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
|
||||
|
||||
$this->date = new Date($this->entityManager, $this->languageManager, $this->stringTranslation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the formatPlugin method.
|
||||
*
|
||||
* @dataProvider providerTestFormatInterval
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\Date::formatInterval()
|
||||
*/
|
||||
public function testFormatInterval($interval, $granularity, $expected, $langcode = NULL) {
|
||||
// Mocks a simple formatPlural implementation.
|
||||
$this->stringTranslation->expects($this->any())
|
||||
->method('formatPlural')
|
||||
->with($this->anything(), $this->anything(), $this->anything(), array(), array('langcode' => $langcode))
|
||||
->will($this->returnCallback(function($count, $one, $multiple) {
|
||||
return $count == 1 ? $one : str_replace('@count', $count, $multiple);
|
||||
}));
|
||||
|
||||
// Check if the granularity is specified.
|
||||
if ($granularity) {
|
||||
$result = $this->date->formatInterval($interval, $granularity, $langcode);
|
||||
}
|
||||
else {
|
||||
$result = $this->date->formatInterval($interval);
|
||||
}
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides some test data for the format interval test.
|
||||
*/
|
||||
public function providerTestFormatInterval() {
|
||||
$data = array(
|
||||
// Checks for basic seconds.
|
||||
array(1, 1, '1 sec'),
|
||||
array(1, 2, '1 sec'),
|
||||
array(2, 1, '2 sec'),
|
||||
array(2, 2, '2 sec'),
|
||||
// Checks for minutes with seconds.
|
||||
array(61, 1, '1 min'),
|
||||
array(61, 2, '1 min 1 sec'),
|
||||
array(62, 2, '1 min 2 sec'),
|
||||
array(121, 1, '2 min'),
|
||||
array(121, 2, '2 min 1 sec'),
|
||||
// Check for hours with minutes and seconds.
|
||||
array(3601, 1, '1 hour'),
|
||||
array(3601, 2, '1 hour 1 sec'),
|
||||
// Check for higher units.
|
||||
array(86401, 1, '1 day'),
|
||||
array(604800, 1, '1 week'),
|
||||
array(2592000 * 2, 1, '2 months'),
|
||||
array(31536000 * 2, 1, '2 years'),
|
||||
// Check for a complicated one with months weeks and days.
|
||||
array(2592000 * 2 + 604800 * 3 + 86400 * 4, 3, '2 months 3 weeks 4 days'),
|
||||
// Check for the langcode.
|
||||
array(61, 1, '1 min', 'xxx-lolspeak'),
|
||||
// Check with an unspecified granularity.
|
||||
array(61, NULL, '1 min 1 sec'),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the formatInterval method for 0 second.
|
||||
*/
|
||||
public function testFormatIntervalZeroSecond() {
|
||||
$this->stringTranslation->expects($this->once())
|
||||
->method('translate')
|
||||
->with('0 sec', array(), array('langcode' => 'xxx-lolspeak'))
|
||||
->will($this->returnValue('0 sec'));
|
||||
|
||||
$result = $this->date->formatInterval(0, 1, 'xxx-lolspeak');
|
||||
|
||||
$this->assertEquals('0 sec', $result);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\StringTranslation\TranslationManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\StringTranslation {
|
||||
|
||||
use Drupal\Core\StringTranslation\TranslationManager;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the translation manager.
|
||||
*
|
||||
* @see \Drupal\Core\StringTranslation\TranslationManager
|
||||
*/
|
||||
class TranslationManagerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested translation manager.
|
||||
*
|
||||
* @var \Drupal\Core\StringTranslation\TranslationManager
|
||||
*/
|
||||
protected $translationManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Translation manager',
|
||||
'description' => 'Tests the translation manager.',
|
||||
'group' => 'Translation',
|
||||
);
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
$this->translationManager = new TestTranslationManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides some test data for formatPlural()
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestFormatPlural() {
|
||||
return array(
|
||||
array(1, 'Singular', '@count plural', array(), array(), 'Singular'),
|
||||
array(2, 'Singular', '@count plural', array(), array(), '2 plural'),
|
||||
// @todo support locale_get_plural
|
||||
array(2, 'Singular', '@count plural @arg', array('@arg' => 3), array(), '2 plural 3'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerTestFormatPlural
|
||||
*/
|
||||
public function testFormatPlural($count, $singular, $plural, array $args = array(), array $options = array(), $expected) {
|
||||
$translator = $this->getMock('\Drupal\Core\StringTranslation\Translator\TranslatorInterface');
|
||||
$translator->expects($this->once())
|
||||
->method('getStringTranslation')
|
||||
->will($this->returnCallback(function ($langcode, $string) {
|
||||
return $string;
|
||||
}));
|
||||
$this->translationManager->addTranslator($translator);
|
||||
$result = $this->translationManager->formatPlural($count, $singular, $plural, $args, $options);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestTranslationManager extends TranslationManager {
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
if (!defined('LOCALE_PLURAL_DELIMITER')) {
|
||||
define('LOCALE_PLURAL_DELIMITER', "\03");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue