Merge pull request #21741 from owncloud/l10n-improvements

Move methods to the factory that are not related to translating, but to guessing/finding the language
This commit is contained in:
Thomas Müller 2016-01-27 09:37:00 +01:00
commit 1594371c8c
16 changed files with 1169 additions and 318 deletions

View File

@ -41,11 +41,13 @@ class Activity extends \OCA\Files_Sharing\Tests\TestCase {
protected function setUp() {
parent::setUp();
$this->activity = new \OCA\Files_Sharing\Activity(
$this->getMock('\OC\L10N\Factory'),
$this->getMockBuilder('\OCP\IURLGenerator')
$this->getMockBuilder('OCP\L10N\IFactory')
->disableOriginalConstructor()
->getMock(),
$this->getMockBuilder('\OCP\Activity\IManager')
$this->getMockBuilder('OCP\IURLGenerator')
->disableOriginalConstructor()
->getMock(),
$this->getMockBuilder('OCP\Activity\IManager')
->disableOriginalConstructor()
->getMock()
);

View File

@ -67,7 +67,7 @@ $array = array(
"oc_isadmin" => OC_User::isAdminUser(OC_User::getUser()) ? 'true' : 'false',
"oc_webroot" => "\"".OC::$WEBROOT."\"",
"oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution
"datepickerFormatDate" => json_encode($l->getDateFormat()),
"datepickerFormatDate" => json_encode($l->l('jsdate', null)),
"dayNames" => json_encode(
array(
(string)$l->t('Sunday'),
@ -133,7 +133,7 @@ $array = array(
(string)$l->t('Dec.')
)
),
"firstDay" => json_encode($l->getFirstWeekDay()) ,
"firstDay" => json_encode($l->l('firstday', null)) ,
"oc_config" => json_encode(
array(
'session_lifetime' => min(\OCP\Config::getSystemValue('session_lifetime', OC::$server->getIniWrapper()->getNumeric('session.gc_maxlifetime')), OC::$server->getIniWrapper()->getNumeric('session.gc_maxlifetime')),

View File

@ -25,16 +25,48 @@
namespace OC\L10N;
use OCP\IConfig;
use OCP\IRequest;
use OCP\L10N\IFactory;
/**
* A factory that generates language instances
*/
class Factory implements IFactory {
/** @var string */
protected $requestLanguage = '';
/**
* cached instances
* @var array Structure: Lang => App => \OCP\IL10N
*/
protected $instances = array();
protected $instances = [];
/**
* @var array Structure: App => string[]
*/
protected $availableLanguages = [];
/**
* @var array Structure: string => callable
*/
protected $pluralFunctions = [];
/** @var IConfig */
protected $config;
/** @var IRequest */
protected $request;
/**
* @param IConfig $config
* @param IRequest $request
*/
public function __construct(IConfig $config, IRequest $request) {
$this->config = $config;
$this->request = $request;
}
/**
* Get a language instance
@ -44,16 +76,269 @@ class Factory implements IFactory {
* @return \OCP\IL10N
*/
public function get($app, $lang = null) {
$app = \OC_App::cleanAppId($app);
if ($lang !== null) {
$lang = str_replace(array('\0', '/', '\\', '..'), '', (string) $lang);
}
$key = $lang;
if ($key === null) {
if ($key === null || !$this->languageExists($app, $lang)) {
$key = 'null';
$lang = $this->findLanguage($app);
}
if (!isset($this->instances[$key][$app])) {
$this->instances[$key][$app] = new \OC_L10N($app, $lang);
$this->instances[$key][$app] = new L10N(
$this, $app, $lang,
$this->getL10nFilesForApp($app, $lang)
);
}
return $this->instances[$key][$app];
}
/**
* Find the best language
*
* @param string|null $app App id or null for core
* @return string language If nothing works it returns 'en'
*/
public function findLanguage($app = null) {
if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
return $this->requestLanguage;
}
$userId = \OC_User::getUser(); // FIXME not available in non-static?
$userLang = $userId !== false ? $this->config->getUserValue($userId, 'core', 'lang') : null;
if ($userLang) {
$this->requestLanguage = $userLang;
if ($this->languageExists($app, $userLang)) {
return $userLang;
}
}
$defaultLanguage = $this->config->getSystemValue('default_language', false);
if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
return $defaultLanguage;
}
$lang = $this->setLanguageFromRequest($app);
if ($userId !== false && $app === null && !$userLang) {
$this->config->setUserValue($userId, 'core', 'lang', $lang);
}
return $lang;
}
/**
* Find all available languages for an app
*
* @param string|null $app App id or null for core
* @return array an array of available languages
*/
public function findAvailableLanguages($app = null) {
$key = $app;
if ($key === null) {
$key = 'null';
}
// also works with null as key
if (!empty($this->availableLanguages[$key])) {
return $this->availableLanguages[$key];
}
$available = ['en']; //english is always available
$dir = $this->findL10nDir($app);
if (is_dir($dir)) {
$files = scandir($dir);
if ($files !== false) {
foreach ($files as $file) {
if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
$available[] = substr($file, 0, -5);
}
}
}
}
$this->availableLanguages[$key] = $available;
return $available;
}
/**
* @param string|null $app App id or null for core
* @param string $lang
* @return bool
*/
public function languageExists($app, $lang) {
if ($lang === 'en') {//english is always available
return true;
}
$languages = $this->findAvailableLanguages($app);
return array_search($lang, $languages) !== false;
}
/**
* @param string|null $app App id or null for core
* @return string
*/
public function setLanguageFromRequest($app = null) {
$header = $this->request->getHeader('ACCEPT_LANGUAGE');
if ($header) {
$available = $this->findAvailableLanguages($app);
// E.g. make sure that 'de' is before 'de_DE'.
sort($available);
$preferences = preg_split('/,\s*/', strtolower($header));
foreach ($preferences as $preference) {
list($preferred_language) = explode(';', $preference);
$preferred_language = str_replace('-', '_', $preferred_language);
foreach ($available as $available_language) {
if ($preferred_language === strtolower($available_language)) {
if ($app === null && !$this->requestLanguage) {
$this->requestLanguage = $available_language;
}
return $available_language;
}
}
// Fallback from de_De to de
foreach ($available as $available_language) {
if (substr($preferred_language, 0, 2) === $available_language) {
if ($app === null && !$this->requestLanguage) {
$this->requestLanguage = $available_language;
}
return $available_language;
}
}
}
}
if (!$this->requestLanguage) {
$this->requestLanguage = 'en';
}
return 'en'; // Last try: English
}
/**
* Get a list of language files that should be loaded
*
* @param string $app
* @param string $lang
* @return string[]
*/
// FIXME This method is only public, until OC_L10N does not need it anymore,
// FIXME This is also the reason, why it is not in the public interface
public function getL10nFilesForApp($app, $lang) {
$languageFiles = [];
$i18nDir = $this->findL10nDir($app);
$transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
if ((\OC_Helper::isSubDirectory($transFile, \OC::$SERVERROOT . '/core/l10n/')
|| \OC_Helper::isSubDirectory($transFile, \OC::$SERVERROOT . '/lib/l10n/')
|| \OC_Helper::isSubDirectory($transFile, \OC::$SERVERROOT . '/settings/l10n/')
|| \OC_Helper::isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
)
&& file_exists($transFile)) {
// load the translations file
$languageFiles[] = $transFile;
// merge with translations from theme
$theme = $this->config->getSystemValue('theme');
if (!empty($theme)) {
$transFile = \OC::$SERVERROOT . '/themes/' . $theme . substr($transFile, strlen(\OC::$SERVERROOT));
if (file_exists($transFile)) {
$languageFiles[] = $transFile;
}
}
}
return $languageFiles;
}
/**
* find the l10n directory
*
* @param string $app App id or empty string for core
* @return string directory
*/
protected function findL10nDir($app = null) {
if (in_array($app, ['core', 'lib', 'settings'])) {
if (file_exists(\OC::$SERVERROOT . '/' . $app . '/l10n/')) {
return \OC::$SERVERROOT . '/' . $app . '/l10n/';
}
} else if ($app && \OC_App::getAppPath($app) !== false) {
// Check if the app is in the app folder
return \OC_App::getAppPath($app) . '/l10n/';
}
return \OC::$SERVERROOT . '/core/l10n/';
}
/**
* Creates a function from the plural string
*
* Parts of the code is copied from Habari:
* https://github.com/habari/system/blob/master/classes/locale.php
* @param string $string
* @return string
*/
public function createPluralFunction($string) {
if (isset($this->pluralFunctions[$string])) {
return $this->pluralFunctions[$string];
}
if (preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
// sanitize
$nplurals = preg_replace( '/[^0-9]/', '', $matches[1] );
$plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] );
$body = str_replace(
array( 'plural', 'n', '$n$plurals', ),
array( '$plural', '$n', '$nplurals', ),
'nplurals='. $nplurals . '; plural=' . $plural
);
// add parents
// important since PHP's ternary evaluates from left to right
$body .= ';';
$res = '';
$p = 0;
for($i = 0; $i < strlen($body); $i++) {
$ch = $body[$i];
switch ( $ch ) {
case '?':
$res .= ' ? (';
$p++;
break;
case ':':
$res .= ') : (';
break;
case ';':
$res .= str_repeat( ')', $p ) . ';';
$p = 0;
break;
default:
$res .= $ch;
}
}
$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
$function = create_function('$n', $body);
$this->pluralFunctions[$string] = $function;
return $function;
} else {
// default: one plural form for all cases but n==1 (english)
$function = create_function(
'$n',
'$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
);
$this->pluralFunctions[$string] = $function;
return $function;
}
}
}

216
lib/private/l10n/l10n.php Normal file
View File

@ -0,0 +1,216 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\L10N;
use OCP\IL10N;
use OCP\L10N\IFactory;
use Punic\Calendar;
class L10N implements IL10N {
/** @var IFactory */
protected $factory;
/** @var string App of this object */
protected $app;
/** @var string Language of this object */
protected $lang;
/** @var string Plural forms (string) */
private $pluralFormString = 'nplurals=2; plural=(n != 1);';
/** @var string Plural forms (function) */
private $pluralFormFunction = null;
/** @var string[] */
private $translations = [];
/**
* @param IFactory $factory
* @param string $app
* @param string $lang
* @param array $files
*/
public function __construct(IFactory $factory, $app, $lang, array $files) {
$this->factory = $factory;
$this->app = $app;
$this->lang = $lang;
$this->translations = [];
foreach ($files as $languageFile) {
$this->load($languageFile);
}
}
/**
* The code (en, de, ...) of the language that is used for this instance
*
* @return string language
*/
public function getLanguageCode() {
return $this->lang;
}
/**
* Translating
* @param string $text The text we need a translation for
* @param array $parameters default:array() Parameters for sprintf
* @return string Translation or the same text
*
* Returns the translation. If no translation is found, $text will be
* returned.
*/
public function t($text, $parameters = array()) {
return (string) new \OC_L10N_String($this, $text, $parameters);
}
/**
* Translating
* @param string $text_singular the string to translate for exactly one object
* @param string $text_plural the string to translate for n objects
* @param integer $count Number of objects
* @param array $parameters default:array() Parameters for sprintf
* @return string Translation or the same text
*
* Returns the translation. If no translation is found, $text will be
* returned. %n will be replaced with the number of objects.
*
* The correct plural is determined by the plural_forms-function
* provided by the po file.
*
*/
public function n($text_singular, $text_plural, $count, $parameters = array()) {
$identifier = "_${text_singular}_::_${text_plural}_";
if (isset($this->translations[$identifier])) {
return (string) new \OC_L10N_String($this, $identifier, $parameters, $count);
} else {
if ($count === 1) {
return (string) new \OC_L10N_String($this, $text_singular, $parameters, $count);
} else {
return (string) new \OC_L10N_String($this, $text_plural, $parameters, $count);
}
}
}
/**
* Localization
* @param string $type Type of localization
* @param \DateTime|int|string $data parameters for this localization
* @param array $options
* @return string|int|false
*
* Returns the localized data.
*
* Implemented types:
* - date
* - Creates a date
* - params: timestamp (int/string)
* - datetime
* - Creates date and time
* - params: timestamp (int/string)
* - time
* - Creates a time
* - params: timestamp (int/string)
* - firstday: Returns the first day of the week (0 sunday - 6 saturday)
* - jsdate: Returns the short JS date format
*/
public function l($type, $data = null, $options = array()) {
// Use the language of the instance
$locale = $this->getLanguageCode();
if ($locale === 'sr@latin') {
$locale = 'sr_latn';
}
if ($type === 'firstday') {
return (int) Calendar::getFirstWeekday($locale);
}
if ($type === 'jsdate') {
return (string) Calendar::getDateFormat('short', $locale);
}
$value = new \DateTime();
if ($data instanceof \DateTime) {
$value = $data;
} else if (is_string($data) && !is_numeric($data)) {
$data = strtotime($data);
$value->setTimestamp($data);
} else if ($data !== null) {
$value->setTimestamp($data);
}
$options = array_merge(array('width' => 'long'), $options);
$width = $options['width'];
switch ($type) {
case 'date':
return (string) Calendar::formatDate($value, $width, $locale);
case 'datetime':
return (string) Calendar::formatDatetime($value, $width, $locale);
case 'time':
return (string) Calendar::formatTime($value, $width, $locale);
default:
return false;
}
}
/**
* Returns an associative array with all translations
*
* Called by \OC_L10N_String
* @return array
*/
public function getTranslations() {
return $this->translations;
}
/**
* Returnsed function accepts the argument $n
*
* Called by \OC_L10N_String
* @return string the plural form function
*/
public function getPluralFormFunction() {
if (is_null($this->pluralFormFunction)) {
$this->pluralFormFunction = $this->factory->createPluralFunction($this->pluralFormString);
}
return $this->pluralFormFunction;
}
/**
* @param $translationFile
* @return bool
*/
protected function load($translationFile) {
$json = json_decode(file_get_contents($translationFile), true);
if (!is_array($json)) {
$jsonError = json_last_error();
\OC::$server->getLogger()->warning("Failed to load $translationFile - json error code: $jsonError", ['app' => 'l10n']);
return false;
}
if (!empty($json['pluralForm'])) {
$this->pluralFormString = $json['pluralForm'];
}
$this->translations = array_merge($this->translations, $json['translations']);
return true;
}
}

View File

@ -26,28 +26,23 @@
*/
class OC_L10N_String implements JsonSerializable {
/**
* @var OC_L10N
*/
/** @var \OC_L10N|\OC\L10N\L10N */
protected $l10n;
/**
* @var string
*/
/** @var string */
protected $text;
/**
* @var array
*/
/** @var array */
protected $parameters;
/**
* @var integer
*/
/** @var integer */
protected $count;
/**
* @param OC_L10N $l10n
* @param \OC_L10N|\OC\L10N\L10N $l10n
* @param string|string[] $text
* @param array $parameters
* @param int $count
*/
public function __construct($l10n, $text, $parameters, $count = 1) {
$this->l10n = $l10n;
@ -80,5 +75,4 @@ class OC_L10N_String implements JsonSerializable {
public function jsonSerialize() {
return $this->__toString();
}
}

View File

@ -37,6 +37,7 @@
/**
* This class is for i18n and l10n
* @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->get() instead
*/
class OC_L10N implements \OCP\IL10N {
/**
@ -82,56 +83,29 @@ class OC_L10N implements \OCP\IL10N {
*
* If language is not set, the constructor tries to find the right
* language.
* @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->get() instead
*/
public function __construct($app, $lang = null) {
$app = \OC_App::cleanAppId($app);
$this->app = $app;
if ($lang !== null) {
$lang = str_replace(array('\0', '/', '\\', '..'), '', $lang);
}
// Find the right language
if ($app !== 'test' && !\OC::$server->getL10NFactory()->languageExists($app, $lang)) {
$lang = \OC::$server->getL10NFactory()->findLanguage($app);
}
$this->lang = $lang;
}
/**
* @return string
*/
public static function setLanguageFromRequest() {
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$available = self::findAvailableLanguages();
// E.g. make sure that 'de' is before 'de_DE'.
sort($available);
$preferences = preg_split('/,\s*/', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
foreach ($preferences as $preference) {
list($preferred_language) = explode(';', $preference);
$preferred_language = str_replace('-', '_', $preferred_language);
foreach ($available as $available_language) {
if ($preferred_language === strtolower($available_language)) {
if (!self::$language) {
self::$language = $available_language;
}
return $available_language;
}
}
foreach ($available as $available_language) {
if (substr($preferred_language, 0, 2) === $available_language) {
if (!self::$language) {
self::$language = $available_language;
}
return $available_language;
}
}
}
}
self::$language = 'en';
// Last try: English
return 'en';
}
/**
* @param $transFile
* @param bool $mergeTranslations
* @return bool
*/
public function load($transFile, $mergeTranslations = false) {
public function load($transFile) {
$this->app = true;
$json = json_decode(file_get_contents($transFile), true);
@ -144,11 +118,7 @@ class OC_L10N implements \OCP\IL10N {
$this->pluralFormString = $json['pluralForm'];
$translations = $json['translations'];
if ($mergeTranslations) {
$this->translations = array_merge($this->translations, $translations);
} else {
$this->translations = $translations;
}
$this->translations = array_merge($this->translations, $translations);
return true;
}
@ -157,101 +127,17 @@ class OC_L10N implements \OCP\IL10N {
if ($this->app === true) {
return;
}
$app = OC_App::cleanAppId($this->app);
$lang = str_replace(array('\0', '/', '\\', '..'), '', $this->lang);
$app = $this->app;
$lang = $this->lang;
$this->app = true;
// Find the right language
if(is_null($lang) || $lang == '') {
$lang = self::findLanguage($app);
}
// Use cache if possible
if(array_key_exists($app.'::'.$lang, self::$cache)) {
$this->translations = self::$cache[$app.'::'.$lang]['t'];
} else{
$i18nDir = self::findI18nDir($app);
$transFile = strip_tags($i18nDir).strip_tags($lang).'.json';
// Texts are in $i18ndir
// (Just no need to define date/time format etc. twice)
if((OC_Helper::isSubDirectory($transFile, OC::$SERVERROOT.'/core/l10n/')
|| OC_Helper::isSubDirectory($transFile, OC::$SERVERROOT.'/lib/l10n/')
|| OC_Helper::isSubDirectory($transFile, OC::$SERVERROOT.'/settings')
|| OC_Helper::isSubDirectory($transFile, OC_App::getAppPath($app).'/l10n/')
)
&& file_exists($transFile)) {
// load the translations file
if($this->load($transFile)) {
//merge with translations from theme
$theme = \OC::$server->getConfig()->getSystemValue('theme');
if (!empty($theme)) {
$transFile = OC::$SERVERROOT.'/themes/'.$theme.substr($transFile, strlen(OC::$SERVERROOT));
if (file_exists($transFile)) {
$this->load($transFile, true);
}
}
}
}
/** @var \OC\L10N\Factory $factory */
$factory = \OC::$server->getL10NFactory();
$languageFiles = $factory->getL10nFilesForApp($app, $lang);
self::$cache[$app.'::'.$lang]['t'] = $this->translations;
}
}
/**
* Creates a function that The constructor
*
* If language is not set, the constructor tries to find the right
* language.
*
* Parts of the code is copied from Habari:
* https://github.com/habari/system/blob/master/classes/locale.php
* @param string $string
* @return string
*/
protected function createPluralFormFunction($string){
if(preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
// sanitize
$nplurals = preg_replace( '/[^0-9]/', '', $matches[1] );
$plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] );
$body = str_replace(
array( 'plural', 'n', '$n$plurals', ),
array( '$plural', '$n', '$nplurals', ),
'nplurals='. $nplurals . '; plural=' . $plural
);
// add parents
// important since PHP's ternary evaluates from left to right
$body .= ';';
$res = '';
$p = 0;
for($i = 0; $i < strlen($body); $i++) {
$ch = $body[$i];
switch ( $ch ) {
case '?':
$res .= ' ? (';
$p++;
break;
case ':':
$res .= ') : (';
break;
case ';':
$res .= str_repeat( ')', $p ) . ';';
$p = 0;
break;
default:
$res .= $ch;
}
}
$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
return create_function('$n', $body);
}
else {
// default: one plural form for all cases but n==1 (english)
return create_function(
'$n',
'$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
);
$this->translations = [];
foreach ($languageFiles as $languageFile) {
$this->load($languageFile);
}
}
@ -316,8 +202,8 @@ class OC_L10N implements \OCP\IL10N {
*/
public function getPluralFormFunction() {
$this->init();
if(is_null($this->pluralFormFunction)) {
$this->pluralFormFunction = $this->createPluralFormFunction($this->pluralFormString);
if (is_null($this->pluralFormFunction)) {
$this->pluralFormFunction = \OC::$server->getL10NFactory()->createPluralFunction($this->pluralFormString);
}
return $this->pluralFormFunction;
}
@ -341,6 +227,8 @@ class OC_L10N implements \OCP\IL10N {
* - time
* - Creates a time
* - params: timestamp (int/string)
* - firstday: Returns the first day of the week (0 sunday - 6 saturday)
* - jsdate: Returns the short JS date format
*/
public function l($type, $data, $options = array()) {
if ($type === 'firstday') {
@ -361,12 +249,8 @@ class OC_L10N implements \OCP\IL10N {
$value->setTimestamp($data);
}
// Use the language of the instance, before falling back to the current user's language
$locale = $this->lang;
if ($locale === null) {
$locale = self::findLanguage();
}
$locale = $this->transformToCLDRLocale($locale);
// Use the language of the instance
$locale = $this->transformToCLDRLocale($this->getLanguageCode());
$options = array_merge(array('width' => 'long'), $options);
$width = $options['width'];
@ -388,7 +272,38 @@ class OC_L10N implements \OCP\IL10N {
* @return string language
*/
public function getLanguageCode() {
return $this->lang ? $this->lang : self::findLanguage();
return $this->lang;
}
/**
* @return string
* @throws \Punic\Exception\ValueNotInList
* @deprecated 9.0.0 Use $this->l('jsdate', null) instead
*/
public function getDateFormat() {
$locale = $this->transformToCLDRLocale($this->getLanguageCode());
return Punic\Calendar::getDateFormat('short', $locale);
}
/**
* @return int
* @deprecated 9.0.0 Use $this->l('firstday', null) instead
*/
public function getFirstWeekDay() {
$locale = $this->transformToCLDRLocale($this->getLanguageCode());
return Punic\Calendar::getFirstWeekday($locale);
}
/**
* @param string $locale
* @return string
*/
private function transformToCLDRLocale($locale) {
if ($locale === 'sr@latin') {
return 'sr_latn';
}
return $locale;
}
/**
@ -397,123 +312,37 @@ class OC_L10N implements \OCP\IL10N {
* @return string language
*
* If nothing works it returns 'en'
* @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->findLanguage() instead
*/
public static function findLanguage($app = null) {
if (self::$language != '' && self::languageExists($app, self::$language)) {
return self::$language;
}
$config = \OC::$server->getConfig();
$userId = \OC_User::getUser();
if($userId && $config->getUserValue($userId, 'core', 'lang')) {
$lang = $config->getUserValue($userId, 'core', 'lang');
self::$language = $lang;
if(self::languageExists($app, $lang)) {
return $lang;
}
}
$default_language = $config->getSystemValue('default_language', false);
if($default_language !== false) {
return $default_language;
}
$lang = self::setLanguageFromRequest();
if($userId && !$config->getUserValue($userId, 'core', 'lang')) {
$config->setUserValue($userId, 'core', 'lang', $lang);
}
return $lang;
return \OC::$server->getL10NFactory()->findLanguage($app);
}
/**
* find the l10n directory
* @param string $app App that needs to be translated
* @return string directory
* @return string
* @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->setLanguageFromRequest() instead
*/
protected static function findI18nDir($app) {
// find the i18n dir
$i18nDir = OC::$SERVERROOT.'/core/l10n/';
if($app != '') {
// Check if the app is in the app folder
if(file_exists(OC_App::getAppPath($app).'/l10n/')) {
$i18nDir = OC_App::getAppPath($app).'/l10n/';
}
else{
$i18nDir = OC::$SERVERROOT.'/'.$app.'/l10n/';
}
}
return $i18nDir;
public static function setLanguageFromRequest() {
return \OC::$server->getL10NFactory()->setLanguageFromRequest();
}
/**
* find all available languages for an app
* @param string $app App that needs to be translated
* @return array an array of available languages
* @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->findAvailableLanguages() instead
*/
public static function findAvailableLanguages($app=null) {
// also works with null as key
if(isset(self::$availableLanguages[$app]) && !empty(self::$availableLanguages[$app])) {
return self::$availableLanguages[$app];
}
$available=array('en');//english is always available
$dir = self::findI18nDir($app);
if(is_dir($dir)) {
$files=scandir($dir);
foreach($files as $file) {
if(substr($file, -5, 5) === '.json' && substr($file, 0, 4) !== 'l10n') {
$i = substr($file, 0, -5);
$available[] = $i;
}
}
}
self::$availableLanguages[$app] = $available;
return $available;
return \OC::$server->getL10NFactory()->findAvailableLanguages($app);
}
/**
* @param string $app
* @param string $lang
* @return bool
* @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->languageExists() instead
*/
public static function languageExists($app, $lang) {
if ($lang === 'en') {//english is always available
return true;
}
$dir = self::findI18nDir($app);
if(is_dir($dir)) {
return file_exists($dir.'/'.$lang.'.json');
}
return false;
}
/**
* @return string
* @throws \Punic\Exception\ValueNotInList
*/
public function getDateFormat() {
$locale = $this->getLanguageCode();
$locale = $this->transformToCLDRLocale($locale);
return Punic\Calendar::getDateFormat('short', $locale);
}
/**
* @return int
*/
public function getFirstWeekDay() {
$locale = $this->getLanguageCode();
$locale = $this->transformToCLDRLocale($locale);
return Punic\Calendar::getFirstWeekday($locale);
}
private function transformToCLDRLocale($locale) {
if ($locale === 'sr@latin') {
return 'sr_latn';
}
return $locale;
return \OC::$server->getL10NFactory()->languageExists($app, $lang);
}
}

View File

@ -262,8 +262,11 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService('AppConfig', function (Server $c) {
return new \OC\AppConfig($c->getDatabaseConnection());
});
$this->registerService('L10NFactory', function ($c) {
return new \OC\L10N\Factory();
$this->registerService('L10NFactory', function (Server $c) {
return new \OC\L10N\Factory(
$c->getConfig(),
$c->getRequest()
);
});
$this->registerService('URLGenerator', function (Server $c) {
$config = $c->getConfig();

View File

@ -33,4 +33,47 @@ interface IFactory {
* @since 8.2.0
*/
public function get($app, $lang = null);
/**
* Find the best language
*
* @param string|null $app App id or null for core
* @return string language If nothing works it returns 'en'
* @since 9.0.0
*/
public function findLanguage($app = null);
/**
* Find all available languages for an app
*
* @param string|null $app App id or null for core
* @return string[] an array of available languages
* @since 9.0.0
*/
public function findAvailableLanguages($app = null);
/**
* @param string|null $app App id or null for core
* @param string $lang
* @return bool
* @since 9.0.0
*/
public function languageExists($app, $lang);
/**
* @param string|null $app App id or null for core
* @return string
* @since 9.0.0
*/
public function setLanguageFromRequest($app = null);
/**
* Creates a function from the plural string
*
* @param string $string
* @return string Unique function name
* @since 9.0.0
*/
public function createPluralFunction($string);
}

View File

@ -46,7 +46,7 @@ try {
OC_App::loadApps();
// force language as given in the http request
\OC_L10N::setLanguageFromRequest();
\OC::$server->getL10NFactory()->setLanguageFromRequest();
OC::$server->getRouter()->match('/ocs'.\OC::$server->getRequest()->getRawPathInfo());
} catch (ResourceNotFoundException $e) {

View File

@ -109,7 +109,7 @@ try {
}
// force language as given in the http request
\OC_L10N::setLanguageFromRequest();
\OC::$server->getL10NFactory()->setLanguageFromRequest();
$file=ltrim($file, '/');

View File

@ -31,7 +31,7 @@ OCP\JSON::callCheck();
// Get data
if( isset( $_POST['lang'] ) ) {
$languageCodes=OC_L10N::findAvailableLanguages();
$languageCodes = \OC::$server->getL10NFactory()->findAvailableLanguages();
$lang = (string)$_POST['lang'];
if(array_search($lang, $languageCodes) or $lang === 'en') {
\OC::$server->getConfig()->setUserValue( OC_User::getUser(), 'core', 'lang', $lang );

View File

@ -63,7 +63,7 @@ $user = OC::$server->getUserManager()->get(OC_User::getUser());
$email = $user->getEMailAddress();
$userLang=$config->getUserValue( OC_User::getUser(), 'core', 'lang', OC_L10N::findLanguage() );
$languageCodes=OC_L10N::findAvailableLanguages();
$languageCodes = \OC::$server->getL10NFactory()->findAvailableLanguages();
// array of common languages
$commonLangCodes = array(

View File

@ -0,0 +1,310 @@
<?php
/**
* Copyright (c) 2016 Joas Schilling <nickvergessen@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\L10N;
use OC\L10N\Factory;
use Test\TestCase;
/**
* Class FactoryTest
*
* @package Test\L10N
* @group DB
*/
class FactoryTest extends TestCase {
/** @var \OCP\IConfig|\PHPUnit_Framework_MockObject_MockObject */
protected $config;
/** @var \OCP\IRequest|\PHPUnit_Framework_MockObject_MockObject */
protected $request;
public function setUp() {
parent::setUp();
/** @var \OCP\IConfig $request */
$this->config = $this->getMockBuilder('OCP\IConfig')
->disableOriginalConstructor()
->getMock();
/** @var \OCP\IRequest $request */
$this->request = $this->getMockBuilder('OCP\IRequest')
->disableOriginalConstructor()
->getMock();
}
/**
* @param array $methods
* @return Factory|\PHPUnit_Framework_MockObject_MockObject
*/
protected function getFactory(array $methods = []) {
if (!empty($methods)) {
return $this->getMockBuilder('OC\L10N\Factory')
->setConstructorArgs([
$this->config,
$this->request,
])
->setMethods($methods)
->getMock();
} else {
return new Factory($this->config, $this->request);
}
}
public function dataFindLanguage() {
return [
[null, false, 1, 'de', true, null, null, null, null, null, 'de'],
[null, 'test', 2, 'de', false, 'ru', true, null, null, null, 'ru'],
[null, 'test', 1, '', null, 'ru', true, null, null, null, 'ru'],
[null, 'test', 3, 'de', false, 'ru', false, 'cz', true, null, 'cz'],
[null, 'test', 2, '', null, 'ru', false, 'cz', true, null, 'cz'],
[null, 'test', 1, '', null, '', null, 'cz', true, null, 'cz'],
[null, 'test', 3, 'de', false, 'ru', false, 'cz', false, 'ar', 'ar'],
[null, 'test', 2, '', null, 'ru', false, 'cz', false, 'ar', 'ar'],
[null, 'test', 1, '', null, '', null, 'cz', false, 'ar', 'ar'],
[null, 'test', 0, '', null, '', null, false, null, 'ar', 'ar'],
];
}
/**
* @dataProvider dataFindLanguage
*
* @param string|null $app
* @param string|null $user
* @param int $existsCalls
* @param string $storedRequestLang
* @param bool $srlExists
* @param string|null $userLang
* @param bool $ulExists
* @param string|false $defaultLang
* @param bool $dlExists
* @param string|null $requestLang
* @param string $expected
*/
public function testFindLanguage($app, $user, $existsCalls, $storedRequestLang, $srlExists, $userLang, $ulExists, $defaultLang, $dlExists, $requestLang, $expected) {
$factory = $this->getFactory([
'languageExists',
'setLanguageFromRequest',
]);
$session = $this->getMockBuilder('OCP\ISession')
->disableOriginalConstructor()
->getMock();
$session->expects($this->any())
->method('get')
->with('user_id')
->willReturn($user);
$userSession = $this->getMockBuilder('OC\User\Session')
->disableOriginalConstructor()
->getMock();
$userSession->expects($this->any())
->method('getSession')
->willReturn($session);
$this->invokePrivate($factory, 'requestLanguage', [$storedRequestLang]);
$factory->expects($this->exactly($existsCalls))
->method('languageExists')
->willReturnMap([
[$app, $storedRequestLang, $srlExists],
[$app, $userLang, $ulExists],
[$app, $defaultLang, $dlExists],
]);
$factory->expects($requestLang !== null ? $this->once() : $this->never())
->method('setLanguageFromRequest')
->willReturn($requestLang);
$this->config->expects($userLang !== null ? $this->any() : $this->never())
->method('getUserValue')
->with($this->anything(), 'core', 'lang')
->willReturn($userLang);
$this->config->expects($defaultLang !== null ? $this->once() : $this->never())
->method('getSystemValue')
->with('default_language', false)
->willReturn($defaultLang);
$this->overwriteService('UserSession', $userSession);
$this->assertSame($expected, $factory->findLanguage($app));
$this->restoreService('UserSession');
}
public function dataFindAvailableLanguages() {
return [
[null],
['files'],
];
}
/**
* @dataProvider dataFindAvailableLanguages
*
* @param string|null $app
*/
public function testFindAvailableLanguages($app) {
$factory = $this->getFactory(['findL10nDir']);
$factory->expects($this->once())
->method('findL10nDir')
->with($app)
->willReturn(\OC::$SERVERROOT . '/tests/data/l10n/');
$this->assertEquals(['cs', 'de', 'en', 'ru'], $factory->findAvailableLanguages($app), '', 0.0, 10, true);
}
public function dataLanguageExists() {
return [
[null, 'en', [], true],
[null, 'de', [], false],
[null, 'de', ['ru'], false],
[null, 'de', ['ru', 'de'], true],
['files', 'en', [], true],
['files', 'de', [], false],
['files', 'de', ['ru'], false],
['files', 'de', ['de', 'ru'], true],
];
}
/**
* @dataProvider dataLanguageExists
*
* @param string|null $app
* @param string $lang
* @param string[] $availableLanguages
* @param string $expected
*/
public function testLanguageExists($app, $lang, array $availableLanguages, $expected) {
$factory = $this->getFactory(['findAvailableLanguages']);
$factory->expects(($lang === 'en') ? $this->never() : $this->once())
->method('findAvailableLanguages')
->with($app)
->willReturn($availableLanguages);
$this->assertSame($expected, $factory->languageExists($app, $lang));
}
public function dataSetLanguageFromRequest() {
return [
// Language is available
[null, 'de', null, ['de'], 'de', 'de'],
[null, 'de,en', null, ['de'], 'de', 'de'],
[null, 'de-DE,en-US;q=0.8,en;q=0.6', null, ['de'], 'de', 'de'],
// Language is not available
[null, 'de', null, ['ru'], 'en', 'en'],
[null, 'de,en', null, ['ru', 'en'], 'en', 'en'],
[null, 'de-DE,en-US;q=0.8,en;q=0.6', null, ['ru', 'en'], 'en', 'en'],
// Language is available, but request language is set
[null, 'de', 'ru', ['de'], 'de', 'ru'],
[null, 'de,en', 'ru', ['de'], 'de', 'ru'],
[null, 'de-DE,en-US;q=0.8,en;q=0.6', 'ru', ['de'], 'de', 'ru'],
];
}
/**
* @dataProvider dataSetLanguageFromRequest
*
* @param string|null $app
* @param string $header
* @param string|null $requestLanguage
* @param string[] $availableLanguages
* @param string $expected
* @param string $expectedLang
*/
public function testSetLanguageFromRequest($app, $header, $requestLanguage, array $availableLanguages, $expected, $expectedLang) {
$factory = $this->getFactory(['findAvailableLanguages']);
$factory->expects($this->once())
->method('findAvailableLanguages')
->with($app)
->willReturn($availableLanguages);
$this->request->expects($this->once())
->method('getHeader')
->with('ACCEPT_LANGUAGE')
->willReturn($header);
if ($requestLanguage !== null) {
$this->invokePrivate($factory, 'requestLanguage', [$requestLanguage]);
}
$this->assertSame($expected, $factory->setLanguageFromRequest($app), 'Asserting returned language');
$this->assertSame($expectedLang, $this->invokePrivate($factory, 'requestLanguage'), 'Asserting stored language');
}
public function dataGetL10nFilesForApp() {
return [
[null, 'de', [\OC::$SERVERROOT . '/core/l10n/de.json']],
['core', 'ru', [\OC::$SERVERROOT . '/core/l10n/ru.json']],
['lib', 'ru', [\OC::$SERVERROOT . '/lib/l10n/ru.json']],
['settings', 'de', [\OC::$SERVERROOT . '/settings/l10n/de.json']],
['files', 'de', [\OC::$SERVERROOT . '/apps/files/l10n/de.json']],
['files', '_lang_never_exists_', []],
['_app_never_exists_', 'de', [\OC::$SERVERROOT . '/core/l10n/de.json']],
];
}
/**
* @dataProvider dataGetL10nFilesForApp
*
* @param string|null $app
* @param string $expected
*/
public function testGetL10nFilesForApp($app, $lang, $expected) {
$factory = $this->getFactory();
$this->assertSame($expected, $this->invokePrivate($factory, 'getL10nFilesForApp', [$app, $lang]));
}
public function dataFindL10NDir() {
return [
[null, \OC::$SERVERROOT . '/core/l10n/'],
['core', \OC::$SERVERROOT . '/core/l10n/'],
['lib', \OC::$SERVERROOT . '/lib/l10n/'],
['settings', \OC::$SERVERROOT . '/settings/l10n/'],
['files', \OC::$SERVERROOT . '/apps/files/l10n/'],
['_app_never_exists_', \OC::$SERVERROOT . '/core/l10n/'],
];
}
/**
* @dataProvider dataFindL10NDir
*
* @param string|null $app
* @param string $expected
*/
public function testFindL10NDir($app, $expected) {
$factory = $this->getFactory();
$this->assertSame($expected, $this->invokePrivate($factory, 'findL10nDir', [$app]));
}
public function dataCreatePluralFunction() {
return [
['nplurals=2; plural=(n != 1);', 0, 1],
['nplurals=2; plural=(n != 1);', 1, 0],
['nplurals=2; plural=(n != 1);', 2, 1],
['nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;', 0, 2],
['nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;', 1, 0],
['nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;', 2, 1],
['nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;', 3, 1],
['nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;', 4, 1],
['nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;', 5, 2],
];
}
/**
* @dataProvider dataCreatePluralFunction
*
* @param string $function
* @param int $count
* @param int $expected
*/
public function testCreatePluralFunction($function, $count, $expected) {
$factory = $this->getFactory();
$fn = $factory->createPluralFunction($function);
$this->assertEquals($expected, $fn($count));
}
}

View File

@ -6,11 +6,21 @@
* See the COPYING-README file.
*/
class Test_L10n extends \Test\TestCase {
namespace Test\L10N;
use OC_L10N;
use DateTime;
/**
* Class Test_L10n
* @group DB
*/
class L10nLegacyTest extends \Test\TestCase {
public function testGermanPluralTranslations() {
$l = new OC_L10N('test');
$transFile = OC::$SERVERROOT.'/tests/data/l10n/de.json';
$transFile = \OC::$SERVERROOT.'/tests/data/l10n/de.json';
$l->load($transFile);
$this->assertEquals('1 Datei', (string)$l->n('%n file', '%n files', 1));
@ -19,7 +29,7 @@ class Test_L10n extends \Test\TestCase {
public function testRussianPluralTranslations() {
$l = new OC_L10N('test');
$transFile = OC::$SERVERROOT.'/tests/data/l10n/ru.json';
$transFile = \OC::$SERVERROOT.'/tests/data/l10n/ru.json';
$l->load($transFile);
$this->assertEquals('1 файл', (string)$l->n('%n file', '%n files', 1));
@ -44,7 +54,7 @@ class Test_L10n extends \Test\TestCase {
public function testCzechPluralTranslations() {
$l = new OC_L10N('test');
$transFile = OC::$SERVERROOT.'/tests/data/l10n/cs.json';
$transFile = \OC::$SERVERROOT.'/tests/data/l10n/cs.json';
$l->load($transFile);
$this->assertEquals('1 okno', (string)$l->n('%n window', '%n windows', 1));
@ -113,51 +123,8 @@ class Test_L10n extends \Test\TestCase {
$this->assertSame($expected, $l->l('firstday', 'firstday'));
}
/**
* @dataProvider findLanguageData
*/
public function testFindLanguage($default, $preference, $expected) {
OC_User::setUserId(null);
$config = \OC::$server->getConfig();
if (is_null($default)) {
$config->deleteSystemValue('default_language');
} else {
$config->setSystemValue('default_language', $default);
}
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = $preference;
$reflection = new \ReflectionClass('OC_L10N');
$prop = $reflection->getProperty('language');
$prop->setAccessible(1);
$prop->setValue('');
$prop->setAccessible(0);
$this->assertSame($expected, OC_L10N::findLanguage());
}
public function findLanguageData() {
return array(
// Exact match
array(null, 'de-DE,en;q=0.5', 'de_DE'),
array(null, 'de-DE,en-US;q=0.8,en;q=0.6', 'de_DE'),
// Best match
array(null, 'de-US,en;q=0.5', 'de'),
array(null, 'de-US,en-US;q=0.8,en;q=0.6', 'de'),
// The default_language config setting overrides browser preferences.
array('es_AR', 'de-DE,en;q=0.5', 'es_AR'),
array('es_AR', 'de-DE,en-US;q=0.8,en;q=0.6', 'es_AR'),
// Worst case default to english
array(null, '', 'en'),
array(null, null, 'en'),
);
}
public function testFactoryGetLanguageCode() {
$factory = new \OC\L10N\Factory();
$factory = new \OC\L10N\Factory($this->getMock('OCP\IConfig'), $this->getMock('OCP\IRequest'));
$l = $factory->get('lib', 'de');
$this->assertEquals('de', $l->getLanguageCode());
}

162
tests/lib/l10n/l10ntest.php Normal file
View File

@ -0,0 +1,162 @@
<?php
/**
* Copyright (c) 2016 Joas Schilling <nickvergessen@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\L10N;
use DateTime;
use OC\L10N\Factory;
use OC\L10N\L10N;
use Test\TestCase;
/**
* Class L10nTest
*
* @package Test\L10N
*/
class L10nTest extends TestCase {
/**
* @return Factory
*/
protected function getFactory() {
/** @var \OCP\IConfig $config */
$config = $this->getMock('OCP\IConfig');
/** @var \OCP\IRequest $request */
$request = $this->getMock('OCP\IRequest');
return new Factory($config, $request);
}
public function testGermanPluralTranslations() {
$transFile = \OC::$SERVERROOT.'/tests/data/l10n/de.json';
$l = new L10N($this->getFactory(), 'test', 'de', [$transFile]);
$this->assertEquals('1 Datei', (string) $l->n('%n file', '%n files', 1));
$this->assertEquals('2 Dateien', (string) $l->n('%n file', '%n files', 2));
}
public function testRussianPluralTranslations() {
$transFile = \OC::$SERVERROOT.'/tests/data/l10n/ru.json';
$l = new L10N($this->getFactory(), 'test', 'ru', [$transFile]);
$this->assertEquals('1 файл', (string)$l->n('%n file', '%n files', 1));
$this->assertEquals('2 файла', (string)$l->n('%n file', '%n files', 2));
$this->assertEquals('6 файлов', (string)$l->n('%n file', '%n files', 6));
$this->assertEquals('21 файл', (string)$l->n('%n file', '%n files', 21));
$this->assertEquals('22 файла', (string)$l->n('%n file', '%n files', 22));
$this->assertEquals('26 файлов', (string)$l->n('%n file', '%n files', 26));
/*
1 file 1 файл 1 папка
2-4 files 2-4 файла 2-4 папки
5-20 files 5-20 файлов 5-20 папок
21 files 21 файл 21 папка
22-24 files 22-24 файла 22-24 папки
25-30 files 25-30 файлов 25-30 папок
etc
100 files 100 файлов, 100 папок
1000 files 1000 файлов 1000 папок
*/
}
public function testCzechPluralTranslations() {
$transFile = \OC::$SERVERROOT.'/tests/data/l10n/cs.json';
$l = new L10N($this->getFactory(), 'test', 'cs', [$transFile]);
$this->assertEquals('1 okno', (string)$l->n('%n window', '%n windows', 1));
$this->assertEquals('2 okna', (string)$l->n('%n window', '%n windows', 2));
$this->assertEquals('5 oken', (string)$l->n('%n window', '%n windows', 5));
}
public function localizationData() {
return array(
// timestamp as string
array('February 13, 2009 at 11:31:30 PM GMT+0', 'en', 'datetime', '1234567890'),
array('13. Februar 2009 um 23:31:30 GMT+0', 'de', 'datetime', '1234567890'),
array('February 13, 2009', 'en', 'date', '1234567890'),
array('13. Februar 2009', 'de', 'date', '1234567890'),
array('11:31:30 PM GMT+0', 'en', 'time', '1234567890'),
array('23:31:30 GMT+0', 'de', 'time', '1234567890'),
// timestamp as int
array('February 13, 2009 at 11:31:30 PM GMT+0', 'en', 'datetime', 1234567890),
array('13. Februar 2009 um 23:31:30 GMT+0', 'de', 'datetime', 1234567890),
array('February 13, 2009', 'en', 'date', 1234567890),
array('13. Februar 2009', 'de', 'date', 1234567890),
array('11:31:30 PM GMT+0', 'en', 'time', 1234567890),
array('23:31:30 GMT+0', 'de', 'time', 1234567890),
// DateTime object
array('February 13, 2009 at 11:31:30 PM GMT+0', 'en', 'datetime', new DateTime('@1234567890')),
array('13. Februar 2009 um 23:31:30 GMT+0', 'de', 'datetime', new DateTime('@1234567890')),
array('February 13, 2009', 'en', 'date', new DateTime('@1234567890')),
array('13. Februar 2009', 'de', 'date', new DateTime('@1234567890')),
array('11:31:30 PM GMT+0', 'en', 'time', new DateTime('@1234567890')),
array('23:31:30 GMT+0', 'de', 'time', new DateTime('@1234567890')),
// en_GB
array('13 February 2009 at 23:31:30 GMT+0', 'en_GB', 'datetime', new DateTime('@1234567890')),
array('13 February 2009', 'en_GB', 'date', new DateTime('@1234567890')),
array('23:31:30 GMT+0', 'en_GB', 'time', new DateTime('@1234567890')),
array('13 February 2009 at 23:31:30 GMT+0', 'en-GB', 'datetime', new DateTime('@1234567890')),
array('13 February 2009', 'en-GB', 'date', new DateTime('@1234567890')),
array('23:31:30 GMT+0', 'en-GB', 'time', new DateTime('@1234567890')),
);
}
/**
* @dataProvider localizationData
*/
public function testNumericStringLocalization($expectedDate, $lang, $type, $value) {
$l = new L10N($this->getFactory(), 'test', $lang, []);
$this->assertSame($expectedDate, $l->l($type, $value));
}
public function firstDayData() {
return array(
array(1, 'de'),
array(0, 'en'),
);
}
/**
* @dataProvider firstDayData
* @param $expected
* @param $lang
*/
public function testFirstWeekDay($expected, $lang) {
$l = new L10N($this->getFactory(), 'test', $lang, []);
$this->assertSame($expected, $l->l('firstday', 'firstday'));
}
public function jsDateData() {
return array(
array('dd.MM.yy', 'de'),
array('M/d/yy', 'en'),
);
}
/**
* @dataProvider jsDateData
* @param $expected
* @param $lang
*/
public function testJSDate($expected, $lang) {
$l = new L10N($this->getFactory(), 'test', $lang, []);
$this->assertSame($expected, $l->l('jsdate', 'jsdate'));
}
public function testFactoryGetLanguageCode() {
$l = $this->getFactory()->get('lib', 'de');
$this->assertEquals('de', $l->getLanguageCode());
}
public function testServiceGetLanguageCode() {
$l = \OC::$server->getL10N('lib', 'de');
$this->assertEquals('de', $l->getLanguageCode());
}
}

View File

@ -36,6 +36,46 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
static protected $realDatabase = null;
static private $wasDatabaseAllowed = false;
/** @var array */
protected $services = [];
/**
* @param string $name
* @param mixed $newService
* @return bool
*/
public function overwriteService($name, $newService) {
if (isset($this->services[$name])) {
return false;
}
$this->services[$name] = \OC::$server->query($name);
\OC::$server->registerService($name, function () use ($newService) {
return $newService;
});
return true;
}
/**
* @param string $name
* @return bool
*/
public function restoreService($name) {
if (isset($this->services[$name])) {
$oldService = $this->services[$name];
\OC::$server->registerService($name, function () use ($oldService) {
return $oldService;
});
unset($this->services[$name]);
return true;
}
return false;
}
protected function getTestTraits() {
$traits = [];
$class = $this;