mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-07-01 08:59:42 +02:00
API Add i18n pluralisation
This commit is contained in:
parent
954f364264
commit
05973cee55
|
@ -234,6 +234,7 @@ JSON;
|
||||||
],
|
],
|
||||||
'ID' => $changeSet->ID,
|
'ID' => $changeSet->ID,
|
||||||
'Name' => $changeSet->Name,
|
'Name' => $changeSet->Name,
|
||||||
|
'Description' => $changeSet->getDescription(),
|
||||||
'Created' => $changeSet->Created,
|
'Created' => $changeSet->Created,
|
||||||
'LastEdited' => $changeSet->LastEdited,
|
'LastEdited' => $changeSet->LastEdited,
|
||||||
'State' => $changeSet->State,
|
'State' => $changeSet->State,
|
||||||
|
@ -258,6 +259,7 @@ JSON;
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function getChangeSetItemResource(ChangeSetItem $changeSetItem) {
|
protected function getChangeSetItemResource(ChangeSetItem $changeSetItem) {
|
||||||
|
$objectSingleton = DataObject::singleton($changeSetItem->ObjectClass);
|
||||||
$hal = [
|
$hal = [
|
||||||
'_links' => [
|
'_links' => [
|
||||||
'self' => [
|
'self' => [
|
||||||
|
@ -270,6 +272,10 @@ JSON;
|
||||||
'Title' => $changeSetItem->getTitle(),
|
'Title' => $changeSetItem->getTitle(),
|
||||||
'ChangeType' => $changeSetItem->getChangeType(),
|
'ChangeType' => $changeSetItem->getChangeType(),
|
||||||
'Added' => $changeSetItem->Added,
|
'Added' => $changeSetItem->Added,
|
||||||
|
'ObjectClass' => $changeSetItem->ObjectClass,
|
||||||
|
'ObjectID' => $changeSetItem->ObjectID,
|
||||||
|
'ObjectSingular' => $objectSingleton->i18n_singular_name(),
|
||||||
|
'ObjectPlural' => $objectSingleton->i18n_plural_name(),
|
||||||
];
|
];
|
||||||
// Depending on whether the object was added implicitly or explicitly, set
|
// Depending on whether the object was added implicitly or explicitly, set
|
||||||
// other related objects.
|
// other related objects.
|
||||||
|
|
124
i18n/i18n.php
124
i18n/i18n.php
|
@ -2053,43 +2053,21 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get current locale (either default or user preference)
|
// Find best translation
|
||||||
$locale = i18n::get_locale();
|
$locale = i18n::get_locale();
|
||||||
$lang = i18n::get_lang_from_locale($locale);
|
$returnValue = static::with_translators(function(Zend_Translate_Adapter $adapter) use ($entity, $locale) {
|
||||||
|
// Return translation only if we found a match thats not the entity itself (Zend fallback)
|
||||||
// Only call getter if static isn't already defined (for performance reasons)
|
$translation = $adapter->translate($entity, $locale);
|
||||||
$translatorsByPrio = self::$translators;
|
if($translation && $translation != $entity) {
|
||||||
if(!$translatorsByPrio) $translatorsByPrio = self::get_translators();
|
return $translation;
|
||||||
|
|
||||||
$returnValue = (is_string($string)) ? $string : ''; // Fall back to default string argument
|
|
||||||
|
|
||||||
foreach($translatorsByPrio as $priority => $translators) {
|
|
||||||
foreach($translators as $name => $translator) {
|
|
||||||
$adapter = $translator->getAdapter();
|
|
||||||
|
|
||||||
// at this point, we need to ensure the language and locale are loaded
|
|
||||||
// as include_by_locale() doesn't load a fallback.
|
|
||||||
|
|
||||||
// TODO Remove reliance on global state, by refactoring into an i18nTranslatorManager
|
|
||||||
// which is instanciated by core with a $clean instance variable.
|
|
||||||
|
|
||||||
if(!$adapter->isAvailable($lang)) {
|
|
||||||
i18n::include_by_locale($lang);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$adapter->isAvailable($locale)) {
|
|
||||||
i18n::include_by_locale($locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
$translation = $adapter->translate($entity, $locale);
|
|
||||||
|
|
||||||
// Return translation only if we found a match thats not the entity itself (Zend fallback)
|
|
||||||
if($translation && $translation != $entity) {
|
|
||||||
$returnValue = $translation;
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fall back to default string argument
|
||||||
|
if($returnValue === null) {
|
||||||
|
$returnValue = (is_string($string)) ? $string : '';
|
||||||
|
}
|
||||||
|
|
||||||
// inject the variables from injectionArray (if present)
|
// inject the variables from injectionArray (if present)
|
||||||
if($injectionArray) {
|
if($injectionArray) {
|
||||||
|
@ -2136,6 +2114,82 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
|
||||||
return $returnValue;
|
return $returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pluralise an item or items.
|
||||||
|
*
|
||||||
|
* @param string $singular Singular form
|
||||||
|
* @param string $plural Plural form
|
||||||
|
* @param int $number Number of items (natural number only)
|
||||||
|
* @param bool $prependNumber Include number in result
|
||||||
|
* @return string Result with the number and pluralised form appended. E.g. '1 page'
|
||||||
|
*/
|
||||||
|
public static function pluralise($singular, $plural, $number, $prependNumber = true) {
|
||||||
|
$locale = static::get_locale();
|
||||||
|
$form = static::with_translators(
|
||||||
|
function(Zend_Translate_Adapter $adapter) use ($singular, $plural, $number, $locale) {
|
||||||
|
// Return translation only if we found a match thats not the entity itself (Zend fallback)
|
||||||
|
$result = $adapter->plural($singular, $plural, $number, $locale);
|
||||||
|
if($result) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if($prependNumber) {
|
||||||
|
return _t('i18n.PLURAL', '{number} {form}', [
|
||||||
|
'number' => $number,
|
||||||
|
'form' => $form
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop over all translators in order of precedence, and return the first non-null value
|
||||||
|
* returned via $callback
|
||||||
|
*
|
||||||
|
* @param callable $callback Callback which is given the translator
|
||||||
|
* @return mixed First non-null result from $callback, or null if none matched
|
||||||
|
*/
|
||||||
|
protected static function with_translators($callback) {
|
||||||
|
// get current locale (either default or user preference)
|
||||||
|
$locale = i18n::get_locale();
|
||||||
|
$lang = i18n::get_lang_from_locale($locale);
|
||||||
|
|
||||||
|
// Only call getter if static isn't already defined (for performance reasons)
|
||||||
|
$translatorsByPrio = self::$translators ?: self::get_translators();
|
||||||
|
|
||||||
|
foreach($translatorsByPrio as $priority => $translators) {
|
||||||
|
/** @var Zend_Translate $translator */
|
||||||
|
foreach($translators as $name => $translator) {
|
||||||
|
$adapter = $translator->getAdapter();
|
||||||
|
|
||||||
|
// at this point, we need to ensure the language and locale are loaded
|
||||||
|
// as include_by_locale() doesn't load a fallback.
|
||||||
|
|
||||||
|
// TODO Remove reliance on global state, by refactoring into an i18nTranslatorManager
|
||||||
|
// which is instanciated by core with a $clean instance variable.
|
||||||
|
|
||||||
|
if(!$adapter->isAvailable($lang)) {
|
||||||
|
i18n::include_by_locale($lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$adapter->isAvailable($locale)) {
|
||||||
|
i18n::include_by_locale($locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = call_user_func($callback, $adapter);
|
||||||
|
if($result !== null) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing matched
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array Array of priority keys to instances of Zend_Translate, mapped by name.
|
* @return array Array of priority keys to instances of Zend_Translate, mapped by name.
|
||||||
|
|
|
@ -722,6 +722,24 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pluralise this item given a specific count.
|
||||||
|
*
|
||||||
|
* E.g. "0 Pages", "1 File", "3 Images"
|
||||||
|
*
|
||||||
|
* @param string $count
|
||||||
|
* @param bool $prependNumber Include number in result. Defaults to true.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function i18n_pluralise($count, $prependNumber = true) {
|
||||||
|
return i18n::pluralise(
|
||||||
|
$this->i18n_singular_name(),
|
||||||
|
$this->i18n_plural_name(),
|
||||||
|
$count,
|
||||||
|
$prependNumber
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the user friendly singular name of this DataObject.
|
* Get the user friendly singular name of this DataObject.
|
||||||
* If the name is not defined (by redefining $singular_name in the subclass),
|
* If the name is not defined (by redefining $singular_name in the subclass),
|
||||||
|
|
|
@ -338,4 +338,117 @@ class ChangeSet extends DataObject {
|
||||||
$this->extend('updateCMSFields', $fields);
|
$this->extend('updateCMSFields', $fields);
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets summary of items in changeset
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDescription() {
|
||||||
|
// Initialise list of items to count
|
||||||
|
$counted = [];
|
||||||
|
$countedOther = 0;
|
||||||
|
foreach($this->config()->important_classes as $type) {
|
||||||
|
if(class_exists($type)) {
|
||||||
|
$counted[$type] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each change item
|
||||||
|
/** @var ChangeSetItem $change */
|
||||||
|
foreach($this->Changes() as $change) {
|
||||||
|
$found = false;
|
||||||
|
foreach($counted as $class => $num) {
|
||||||
|
if(is_a($change->ObjectClass, $class, true)) {
|
||||||
|
$counted[$class]++;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!$found) {
|
||||||
|
$countedOther++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe set based on this output
|
||||||
|
$counted = array_filter($counted);
|
||||||
|
|
||||||
|
// Empty state
|
||||||
|
if(empty($counted) && empty($countedOther)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put all parts together
|
||||||
|
$parts = [];
|
||||||
|
foreach($counted as $class => $count) {
|
||||||
|
$parts[] = DataObject::singleton($class)->i18n_pluralise($count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe non-important items
|
||||||
|
if($countedOther) {
|
||||||
|
if ($counted) {
|
||||||
|
$parts[] = i18n::pluralise(
|
||||||
|
_t('ChangeSet.DESCRIPTION_OTHER_ITEM', 'other item'),
|
||||||
|
_t('ChangeSet.DESCRIPTION_OTHER_ITEMS', 'other items'),
|
||||||
|
$countedOther
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$parts[] = i18n::pluralise(
|
||||||
|
_t('ChangeSet.DESCRIPTION_ITEM', 'item'),
|
||||||
|
_t('ChangeSet.DESCRIPTION_ITEMS', 'items'),
|
||||||
|
$countedOther
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out how to join everything together
|
||||||
|
if(empty($parts)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if(count($parts) === 1) {
|
||||||
|
return $parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-comma list
|
||||||
|
if(count($parts) === 2) {
|
||||||
|
return _t(
|
||||||
|
'ChangeSet.DESCRIPTION_AND',
|
||||||
|
'{first} and {second}',
|
||||||
|
[
|
||||||
|
'first' => $parts[0],
|
||||||
|
'second' => $parts[1],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First item
|
||||||
|
$string = _t(
|
||||||
|
'ChangeSet.DESCRIPTION_LIST_FIRST',
|
||||||
|
'{item}',
|
||||||
|
['item' => $parts[0]]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Middle items
|
||||||
|
for($i = 1; $i < count($parts) - 1; $i++) {
|
||||||
|
$string = _t(
|
||||||
|
'ChangeSet.DESCRIPTION_LIST_MID',
|
||||||
|
'{list}, {item}',
|
||||||
|
[
|
||||||
|
'list' => $string,
|
||||||
|
'item' => $parts[$i]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oxford comma
|
||||||
|
$string = _t(
|
||||||
|
'ChangeSet.DESCRIPTION_LIST_LAST',
|
||||||
|
'{list}, and {item}',
|
||||||
|
[
|
||||||
|
'list' => $string,
|
||||||
|
'item' => end($parts)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user