From 6aec550d6efb1cb50f6404bf72eb78ea37f36266 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 10:04:41 +0100 Subject: Move findAvailableLanguages() to the factory --- lib/private/l10n.php | 20 ++-------------- lib/private/l10n/factory.php | 56 ++++++++++++++++++++++++++++++++++++++++++- lib/public/l10n/ifactory.php | 9 +++++++ settings/ajax/setlanguage.php | 2 +- settings/personal.php | 2 +- 5 files changed, 68 insertions(+), 21 deletions(-) diff --git a/lib/private/l10n.php b/lib/private/l10n.php index b53fadc2bdd..aa19102b70c 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -452,26 +452,10 @@ class OC_L10N implements \OCP\IL10N { * 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); } /** diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index c3c7cc21bba..aaa41f5e645 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -33,8 +33,11 @@ use OCP\L10N\IFactory; class Factory implements IFactory { /** * cached instances + * @var array Structure: Lang => App => \OCP\IL10N */ - protected $instances = array(); + protected $instances = []; + + protected $availableLanguages = []; /** * Get a language instance @@ -56,4 +59,55 @@ class Factory implements IFactory { return $this->instances[$key][$app]; } + /** + * 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; + } + + /** + * find the l10n directory + * + * @param string $app App id or empty string for core + * @return string directory + */ + protected function findL10nDir($app = '') { + if ($app !== '') { + // Check if the app is in the app folder + if (file_exists(\OC_App::getAppPath($app) . '/l10n/')) { + return \OC_App::getAppPath($app) . '/l10n/'; + } else { + return \OC::$SERVERROOT . '/' . $app . '/l10n/'; + } + } + return \OC::$SERVERROOT.'/core/l10n/'; + } } diff --git a/lib/public/l10n/ifactory.php b/lib/public/l10n/ifactory.php index fa3f84fa2fd..d6944b375f0 100644 --- a/lib/public/l10n/ifactory.php +++ b/lib/public/l10n/ifactory.php @@ -33,4 +33,13 @@ interface IFactory { * @since 8.2.0 */ public function get($app, $lang = 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); } diff --git a/settings/ajax/setlanguage.php b/settings/ajax/setlanguage.php index 760d2ca5d7d..537a5afe958 100644 --- a/settings/ajax/setlanguage.php +++ b/settings/ajax/setlanguage.php @@ -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 ); diff --git a/settings/personal.php b/settings/personal.php index 11b4f762a36..261a459a921 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -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( -- cgit v1.2.3 From 29a93064299f57584c6aadc8a6c56b51715d7269 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 10:27:09 +0100 Subject: Move languageExists() to the factory --- lib/private/l10n.php | 10 ++-------- lib/private/l10n/factory.php | 17 +++++++++++++++++ lib/public/l10n/ifactory.php | 8 ++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/private/l10n.php b/lib/private/l10n.php index aa19102b70c..085914a68b6 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -462,16 +462,10 @@ class OC_L10N implements \OCP\IL10N { * @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 \OC::$server->getL10NFactory()->languageExists($app, $lang); } /** diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index aaa41f5e645..b1304727606 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -37,6 +37,9 @@ class Factory implements IFactory { */ protected $instances = []; + /** + * @var array Structure: App => string[] + */ protected $availableLanguages = []; /** @@ -93,6 +96,20 @@ class Factory implements IFactory { 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); + } + /** * find the l10n directory * diff --git a/lib/public/l10n/ifactory.php b/lib/public/l10n/ifactory.php index d6944b375f0..09c75e9af7d 100644 --- a/lib/public/l10n/ifactory.php +++ b/lib/public/l10n/ifactory.php @@ -42,4 +42,12 @@ interface IFactory { * @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); } -- cgit v1.2.3 From 043625ee522357e25bb786c4b439c90ab21bbfd1 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 11:09:37 +0100 Subject: Move findLanguage() and setLanguageFromRequest() to factory --- lib/private/l10n.php | 139 +++++++++++++------------------------------ lib/private/l10n/factory.php | 100 +++++++++++++++++++++++++++++++ lib/private/server.php | 7 ++- lib/public/l10n/ifactory.php | 16 +++++ ocs/v1.php | 2 +- remote.php | 2 +- tests/lib/l10n.php | 2 +- 7 files changed, 166 insertions(+), 102 deletions(-) diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 085914a68b6..43e1121b983 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -88,44 +88,6 @@ class OC_L10N implements \OCP\IL10N { $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 @@ -169,7 +131,7 @@ class OC_L10N implements \OCP\IL10N { if(array_key_exists($app.'::'.$lang, self::$cache)) { $this->translations = self::$cache[$app.'::'.$lang]['t']; } else{ - $i18nDir = self::findI18nDir($app); + $i18nDir = $this->findI18nDir($app); $transFile = strip_tags($i18nDir).strip_tags($lang).'.json'; // Texts are in $i18ndir // (Just no need to define date/time format etc. twice) @@ -391,49 +353,12 @@ class OC_L10N implements \OCP\IL10N { return $this->lang ? $this->lang : self::findLanguage(); } - /** - * find the best language - * @param string $app - * @return string language - * - * If nothing works it returns 'en' - */ - 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; - } - /** * find the l10n directory * @param string $app App that needs to be translated * @return string directory */ - protected static function findI18nDir($app) { + protected function findI18nDir($app) { // find the i18n dir $i18nDir = OC::$SERVERROOT.'/core/l10n/'; if($app != '') { @@ -448,26 +373,6 @@ class OC_L10N implements \OCP\IL10N { return $i18nDir; } - /** - * 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) { - 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) { - return \OC::$server->getL10NFactory()->languageExists($app, $lang); - } - /** * @return string * @throws \Punic\Exception\ValueNotInList @@ -494,4 +399,44 @@ class OC_L10N implements \OCP\IL10N { return $locale; } + + /** + * find the best language + * @param string $app + * @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) { + return \OC::$server->getL10NFactory()->findLanguage($app); + } + + /** + * @return string + * @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->setLanguageFromRequest() instead + */ + 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) { + 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) { + return \OC::$server->getL10NFactory()->languageExists($app, $lang); + } } diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index b1304727606..ecc2318893a 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -25,12 +25,18 @@ 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 @@ -42,6 +48,21 @@ class Factory implements IFactory { */ protected $availableLanguages = []; + /** @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 * @@ -62,6 +83,41 @@ class Factory implements IFactory { 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? + + if ($userId && $this->config->getUserValue($userId, 'core', 'lang')) { + $lang = $this->config->getUserValue($userId, 'core', 'lang'); + $this->requestLanguage = $lang; + if ($this->languageExists($app, $lang)) { + return $lang; + } + } + + $defaultLanguage = $this->config->getSystemValue('default_language', false); + + if ($defaultLanguage !== false) { + return $defaultLanguage; + } + + $lang = $this->setLanguageFromRequest($app); + if ($userId && $app === null && !$this->config->getUserValue($userId, 'core', 'lang')) { + $this->config->setUserValue($userId, 'core', 'lang', $lang); + } + + return $lang; + } + /** * Find all available languages for an app * @@ -110,6 +166,50 @@ class Factory implements IFactory { return array_search($lang, $languages); } + /** + * @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 + } + /** * find the l10n directory * diff --git a/lib/private/server.php b/lib/private/server.php index b8f4bdb53fe..81929d0c7b3 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -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(); diff --git a/lib/public/l10n/ifactory.php b/lib/public/l10n/ifactory.php index 09c75e9af7d..2d6f8e873cc 100644 --- a/lib/public/l10n/ifactory.php +++ b/lib/public/l10n/ifactory.php @@ -34,6 +34,15 @@ interface IFactory { */ 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 * @@ -50,4 +59,11 @@ interface IFactory { * @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); } diff --git a/ocs/v1.php b/ocs/v1.php index 4371b0b604a..39a8f4e468f 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -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) { diff --git a/remote.php b/remote.php index a145fc4bd84..26203e2df8a 100644 --- a/remote.php +++ b/remote.php @@ -109,7 +109,7 @@ try { } // force language as given in the http request - \OC_L10N::setLanguageFromRequest(); + \OC::$server->getL10NFactory()->setLanguageFromRequest(); $file=ltrim($file, '/'); diff --git a/tests/lib/l10n.php b/tests/lib/l10n.php index d77548c5bf5..eb0d9117517 100644 --- a/tests/lib/l10n.php +++ b/tests/lib/l10n.php @@ -157,7 +157,7 @@ class Test_L10n extends \Test\TestCase { } 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()); } -- cgit v1.2.3 From 7ec7464ee249f15d377a44c1b8b23e127e07385e Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 11:25:35 +0100 Subject: Move validation and fallbacks of app and lang to the constructor/factory --- lib/private/l10n.php | 30 +++++++++++++++++------------- lib/private/l10n/factory.php | 7 ++++++- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 43e1121b983..4405491cb78 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -82,9 +82,21 @@ 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 (!\OC::$server->getL10NFactory()->languageExists($app, $lang)) { + $lang = \OC::$server->getL10NFactory()->findLanguage($app); + } + $this->lang = $lang; } @@ -119,13 +131,9 @@ 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)) { @@ -323,12 +331,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']; @@ -350,7 +354,7 @@ class OC_L10N implements \OCP\IL10N { * @return string language */ public function getLanguageCode() { - return $this->lang ? $this->lang : self::findLanguage(); + return $this->lang; } /** diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index ecc2318893a..39d69c81935 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -71,9 +71,14 @@ 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])) { -- cgit v1.2.3 From 6652a0fb6cee6b5494dfca56c561c3ba6ae03185 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 12:37:34 +0100 Subject: Move finding the language files and creating the function out of the object --- lib/private/l10n.php | 105 ++++------------------------------------ lib/private/l10n/factory.php | 112 +++++++++++++++++++++++++++++++++++++++++-- lib/public/l10n/ifactory.php | 10 ++++ 3 files changed, 129 insertions(+), 98 deletions(-) diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 4405491cb78..5a704916f40 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -102,10 +102,9 @@ class OC_L10N implements \OCP\IL10N { /** * @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); @@ -118,11 +117,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; } @@ -135,93 +130,13 @@ class OC_L10N implements \OCP\IL10N { $lang = $this->lang; $this->app = true; - // Use cache if possible - if(array_key_exists($app.'::'.$lang, self::$cache)) { - $this->translations = self::$cache[$app.'::'.$lang]['t']; - } else{ - $i18nDir = $this->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); - } - } - } - } - - 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; - } - } + /** @var \OC\L10N\Factory $factory */ + $factory = \OC::$server->getL10NFactory(); + $languageFiles = $factory->getL10nFilesForApp($app, $lang); - $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); } } @@ -286,8 +201,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; } diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index 39d69c81935..c564de685f2 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -48,6 +48,11 @@ class Factory implements IFactory { */ protected $availableLanguages = []; + /** + * @var array Structure: string => callable + */ + protected $pluralFunctions = []; + /** @var IConfig */ protected $config; @@ -215,16 +220,53 @@ class Factory implements IFactory { 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 = '') { - if ($app !== '') { + protected function findL10nDir($app = null) { + if ($app) { // Check if the app is in the app folder - if (file_exists(\OC_App::getAppPath($app) . '/l10n/')) { + if (\OC_App::getAppPath($app) && file_exists(\OC_App::getAppPath($app) . '/l10n/')) { return \OC_App::getAppPath($app) . '/l10n/'; } else { return \OC::$SERVERROOT . '/' . $app . '/l10n/'; @@ -232,4 +274,68 @@ class Factory implements IFactory { } 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; + } + } } diff --git a/lib/public/l10n/ifactory.php b/lib/public/l10n/ifactory.php index 2d6f8e873cc..264c9719639 100644 --- a/lib/public/l10n/ifactory.php +++ b/lib/public/l10n/ifactory.php @@ -66,4 +66,14 @@ interface IFactory { * @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); } -- cgit v1.2.3 From 4ea0d3c05d374040377fdf7302e3fe23b98d7342 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 12:57:50 +0100 Subject: Deprecate getFirstWeekDay() and getDateFormat() in favor of l() --- core/js/config.php | 4 ++-- lib/private/l10n.php | 34 ++++++++++------------------------ 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/core/js/config.php b/core/js/config.php index 0670df60abd..708da777ee4 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -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')), diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 5a704916f40..2ad93c8c90b 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -226,6 +226,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') { @@ -272,45 +274,29 @@ class OC_L10N implements \OCP\IL10N { return $this->lang; } - /** - * find the l10n directory - * @param string $app App that needs to be translated - * @return string directory - */ - protected 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; - } - /** * @return string * @throws \Punic\Exception\ValueNotInList + * @deprecated 9.0.0 Use $this->l('jsdate', null) instead */ public function getDateFormat() { - $locale = $this->getLanguageCode(); - $locale = $this->transformToCLDRLocale($locale); + $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->getLanguageCode(); - $locale = $this->transformToCLDRLocale($locale); + $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'; -- cgit v1.2.3 From c7abe687766c12062721e1a8314aa59d6f4286dc Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 13:30:13 +0100 Subject: Deprecate OC_L10N in favor of a namespaces and DI version --- lib/private/l10n.php | 1 + lib/private/l10n/factory.php | 5 +- lib/private/l10n/l10n.php | 216 +++++++++++++++++++++++++++++++++++++++++++ lib/private/l10n/string.php | 22 ++--- 4 files changed, 229 insertions(+), 15 deletions(-) create mode 100644 lib/private/l10n/l10n.php diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 2ad93c8c90b..1507de20b40 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -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 { /** diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index c564de685f2..3044cf09519 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -87,7 +87,10 @@ class Factory implements IFactory { } 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]; diff --git a/lib/private/l10n/l10n.php b/lib/private/l10n/l10n.php new file mode 100644 index 00000000000..3e999e8c671 --- /dev/null +++ b/lib/private/l10n/l10n.php @@ -0,0 +1,216 @@ + + * + * @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 + * + */ + +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; + } +} diff --git a/lib/private/l10n/string.php b/lib/private/l10n/string.php index 84d1603871f..9c93b8c5a64 100644 --- a/lib/private/l10n/string.php +++ b/lib/private/l10n/string.php @@ -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(); } - } -- cgit v1.2.3 From 2cb26a915184301873843629e8c4cbf7641776f5 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 14:59:48 +0100 Subject: Fix the tests --- lib/private/l10n.php | 2 +- tests/lib/l10n.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/private/l10n.php b/lib/private/l10n.php index 1507de20b40..5d5d89100ac 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -94,7 +94,7 @@ class OC_L10N implements \OCP\IL10N { } // Find the right language - if (!\OC::$server->getL10NFactory()->languageExists($app, $lang)) { + if ($app !== 'test' && !\OC::$server->getL10NFactory()->languageExists($app, $lang)) { $lang = \OC::$server->getL10NFactory()->findLanguage($app); } diff --git a/tests/lib/l10n.php b/tests/lib/l10n.php index eb0d9117517..d285f399291 100644 --- a/tests/lib/l10n.php +++ b/tests/lib/l10n.php @@ -6,6 +6,10 @@ * See the COPYING-README file. */ +/** + * Class Test_L10n + * @group DB + */ class Test_L10n extends \Test\TestCase { public function testGermanPluralTranslations() { -- cgit v1.2.3 From 119961951841407191e29b220a17f9bf16fce126 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 15:22:39 +0100 Subject: Move legacy test file to new location --- tests/lib/l10n.php | 173 ------------------------------------ tests/lib/l10n/l10nlegacytest.php | 179 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 173 deletions(-) delete mode 100644 tests/lib/l10n.php create mode 100644 tests/lib/l10n/l10nlegacytest.php diff --git a/tests/lib/l10n.php b/tests/lib/l10n.php deleted file mode 100644 index d285f399291..00000000000 --- a/tests/lib/l10n.php +++ /dev/null @@ -1,173 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -/** - * Class Test_L10n - * @group DB - */ -class Test_L10n extends \Test\TestCase { - - public function testGermanPluralTranslations() { - $l = new OC_L10N('test'); - $transFile = OC::$SERVERROOT.'/tests/data/l10n/de.json'; - - $l->load($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() { - $l = new OC_L10N('test'); - $transFile = OC::$SERVERROOT.'/tests/data/l10n/ru.json'; - - $l->load($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() { - $l = new OC_L10N('test'); - $transFile = OC::$SERVERROOT.'/tests/data/l10n/cs.json'; - - $l->load($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 localizationDataProvider() { - 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 localizationDataProvider - */ - public function testNumericStringLocalization($expectedDate, $lang, $type, $value) { - $l = new OC_L10N('test', $lang); - $this->assertSame($expectedDate, $l->l($type, $value)); - } - - public function firstDayDataProvider() { - return array( - array(1, 'de'), - array(0, 'en'), - ); - } - - /** - * @dataProvider firstDayDataProvider - * @param $expected - * @param $lang - */ - public function testFirstWeekDay($expected, $lang) { - $l = new OC_L10N('test', $lang); - $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($this->getMock('OCP\IConfig'), $this->getMock('OCP\IRequest')); - $l = $factory->get('lib', 'de'); - $this->assertEquals('de', $l->getLanguageCode()); - } - - public function testServiceGetLanguageCode() { - $l = \OC::$server->getL10N('lib', 'de'); - $this->assertEquals('de', $l->getLanguageCode()); - } -} diff --git a/tests/lib/l10n/l10nlegacytest.php b/tests/lib/l10n/l10nlegacytest.php new file mode 100644 index 00000000000..f83f6b2b69a --- /dev/null +++ b/tests/lib/l10n/l10nlegacytest.php @@ -0,0 +1,179 @@ + + * 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; +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'; + + $l->load($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() { + $l = new OC_L10N('test'); + $transFile = \OC::$SERVERROOT.'/tests/data/l10n/ru.json'; + + $l->load($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() { + $l = new OC_L10N('test'); + $transFile = \OC::$SERVERROOT.'/tests/data/l10n/cs.json'; + + $l->load($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 localizationDataProvider() { + 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 localizationDataProvider + */ + public function testNumericStringLocalization($expectedDate, $lang, $type, $value) { + $l = new OC_L10N('test', $lang); + $this->assertSame($expectedDate, $l->l($type, $value)); + } + + public function firstDayDataProvider() { + return array( + array(1, 'de'), + array(0, 'en'), + ); + } + + /** + * @dataProvider firstDayDataProvider + * @param $expected + * @param $lang + */ + public function testFirstWeekDay($expected, $lang) { + $l = new OC_L10N('test', $lang); + $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($this->getMock('OCP\IConfig'), $this->getMock('OCP\IRequest')); + $l = $factory->get('lib', 'de'); + $this->assertEquals('de', $l->getLanguageCode()); + } + + public function testServiceGetLanguageCode() { + $l = \OC::$server->getL10N('lib', 'de'); + $this->assertEquals('de', $l->getLanguageCode()); + } +} -- cgit v1.2.3 From 8f9cc515593e396d57ec47b836b48411828d8efa Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 15:23:51 +0100 Subject: Add tests for the new l10n class --- tests/lib/l10n/l10ntest.php | 162 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 tests/lib/l10n/l10ntest.php diff --git a/tests/lib/l10n/l10ntest.php b/tests/lib/l10n/l10ntest.php new file mode 100644 index 00000000000..95546b4f788 --- /dev/null +++ b/tests/lib/l10n/l10ntest.php @@ -0,0 +1,162 @@ + + * 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()); + } +} -- cgit v1.2.3 From b24b198a83de78c2288482cdf915f15c131653dc Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 16:24:31 +0100 Subject: Add tests for the factory --- lib/private/l10n/factory.php | 16 ++-- tests/lib/l10n/factorytest.php | 201 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 8 deletions(-) create mode 100644 tests/lib/l10n/factorytest.php diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index 3044cf09519..40ad4f5ef59 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -176,7 +176,7 @@ class Factory implements IFactory { } $languages = $this->findAvailableLanguages($app); - return array_search($lang, $languages); + return array_search($lang, $languages) !== false; } /** @@ -238,7 +238,7 @@ class Factory implements IFactory { $i18nDir = $this->findL10nDir($app); $transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json'; - if((\OC_Helper::isSubDirectory($transFile, \OC::$SERVERROOT . '/core/l10n/') + 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/') @@ -267,15 +267,15 @@ class Factory implements IFactory { * @return string directory */ protected function findL10nDir($app = null) { - if ($app) { - // Check if the app is in the app folder - if (\OC_App::getAppPath($app) && file_exists(\OC_App::getAppPath($app) . '/l10n/')) { - return \OC_App::getAppPath($app) . '/l10n/'; - } else { + 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/'; + return \OC::$SERVERROOT . '/core/l10n/'; } diff --git a/tests/lib/l10n/factorytest.php b/tests/lib/l10n/factorytest.php new file mode 100644 index 00000000000..3af1627159a --- /dev/null +++ b/tests/lib/l10n/factorytest.php @@ -0,0 +1,201 @@ + + * 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 Test_L10n + * @group DB + */ +class FactoryTest extends TestCase { + /** @var \OCP\IRequest|\PHPUnit_Framework_MockObject_MockObject */ + protected $request; + + public function setUp() { + parent::setUp(); + + /** @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 = []) { + /** @var \OCP\IConfig $config */ + $config = $this->getMock('OCP\IConfig'); + + if (!empty($methods)) { + return $this->getMockBuilder('OC\L10N\Factory') + ->setConstructorArgs([ + $config, + $this->request, + ]) + ->setMethods($methods) + ->getMock(); + } else { + return new Factory($config, $this->request); + } + } + + 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)); + } +} -- cgit v1.2.3 From fe411788b7ee45e2da551fd20aded030b762eecc Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 15 Jan 2016 16:24:42 +0100 Subject: Remove tests for wrapper --- tests/lib/l10n/l10nlegacytest.php | 43 --------------------------------------- 1 file changed, 43 deletions(-) diff --git a/tests/lib/l10n/l10nlegacytest.php b/tests/lib/l10n/l10nlegacytest.php index f83f6b2b69a..ae84968e65d 100644 --- a/tests/lib/l10n/l10nlegacytest.php +++ b/tests/lib/l10n/l10nlegacytest.php @@ -123,49 +123,6 @@ class L10nLegacyTest 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($this->getMock('OCP\IConfig'), $this->getMock('OCP\IRequest')); $l = $factory->get('lib', 'de'); -- cgit v1.2.3 From 70396581eb4c33b380962d4589ce183f01dfc090 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 18 Jan 2016 09:53:25 +0100 Subject: Add tests for findLanguage() --- lib/private/l10n/factory.php | 14 +++--- tests/lib/l10n/factorytest.php | 99 +++++++++++++++++++++++++++++++++++++++--- tests/lib/testcase.php | 40 +++++++++++++++++ 3 files changed, 140 insertions(+), 13 deletions(-) diff --git a/lib/private/l10n/factory.php b/lib/private/l10n/factory.php index 40ad4f5ef59..09496cba410 100644 --- a/lib/private/l10n/factory.php +++ b/lib/private/l10n/factory.php @@ -109,22 +109,22 @@ class Factory implements IFactory { $userId = \OC_User::getUser(); // FIXME not available in non-static? - if ($userId && $this->config->getUserValue($userId, 'core', 'lang')) { - $lang = $this->config->getUserValue($userId, 'core', 'lang'); - $this->requestLanguage = $lang; - if ($this->languageExists($app, $lang)) { - return $lang; + $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) { + if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) { return $defaultLanguage; } $lang = $this->setLanguageFromRequest($app); - if ($userId && $app === null && !$this->config->getUserValue($userId, 'core', 'lang')) { + if ($userId !== false && $app === null && !$userLang) { $this->config->setUserValue($userId, 'core', 'lang', $lang); } diff --git a/tests/lib/l10n/factorytest.php b/tests/lib/l10n/factorytest.php index 3af1627159a..1cdc094043b 100644 --- a/tests/lib/l10n/factorytest.php +++ b/tests/lib/l10n/factorytest.php @@ -13,16 +13,27 @@ use OC\L10N\Factory; use Test\TestCase; /** - * Class Test_L10n + * 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() @@ -34,22 +45,98 @@ class FactoryTest extends TestCase { * @return Factory|\PHPUnit_Framework_MockObject_MockObject */ protected function getFactory(array $methods = []) { - /** @var \OCP\IConfig $config */ - $config = $this->getMock('OCP\IConfig'); - if (!empty($methods)) { return $this->getMockBuilder('OC\L10N\Factory') ->setConstructorArgs([ - $config, + $this->config, $this->request, ]) ->setMethods($methods) ->getMock(); } else { - return new Factory($config, $this->request); + 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 dataLanguageExists() { return [ [null, 'en', [], true], diff --git a/tests/lib/testcase.php b/tests/lib/testcase.php index 38d5cf49320..008b96b3417 100644 --- a/tests/lib/testcase.php +++ b/tests/lib/testcase.php @@ -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; -- cgit v1.2.3 From f413b3ff3a7d4be4e8e4072c354ea3228f4110d5 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 18 Jan 2016 10:00:29 +0100 Subject: Add tests for findAvailableLanguages --- tests/lib/l10n/factorytest.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/lib/l10n/factorytest.php b/tests/lib/l10n/factorytest.php index 1cdc094043b..f632e48e2de 100644 --- a/tests/lib/l10n/factorytest.php +++ b/tests/lib/l10n/factorytest.php @@ -137,6 +137,28 @@ class FactoryTest extends TestCase { $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], -- cgit v1.2.3 From d46cd6a24561aa44f0a0d27fec9da78a83428eb2 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 18 Jan 2016 10:05:46 +0100 Subject: Fix tests --- apps/files_sharing/tests/activity.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/tests/activity.php b/apps/files_sharing/tests/activity.php index b3575b0b709..40a1031f779 100644 --- a/apps/files_sharing/tests/activity.php +++ b/apps/files_sharing/tests/activity.php @@ -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() ); -- cgit v1.2.3 From 3a6390031cb7e6575edd8d4a5271614eb8ba9470 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 26 Jan 2016 14:10:05 +0100 Subject: Move class to legacy folder --- lib/private/l10n.php | 348 -------------------------------------------- lib/private/legacy/l10n.php | 348 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+), 348 deletions(-) delete mode 100644 lib/private/l10n.php create mode 100644 lib/private/legacy/l10n.php diff --git a/lib/private/l10n.php b/lib/private/l10n.php deleted file mode 100644 index 5d5d89100ac..00000000000 --- a/lib/private/l10n.php +++ /dev/null @@ -1,348 +0,0 @@ - - * @author Andreas Fischer - * @author Bart Visscher - * @author Bernhard Posselt - * @author Jakob Sack - * @author Jan-Christoph Borchardt - * @author Joas Schilling - * @author Jörn Friedrich Dreyer - * @author Lennart Rosam - * @author Lukas Reschke - * @author Morris Jobke - * @author Robin Appelman - * @author Robin McCorkell - * @author Scrutinizer Auto-Fixer - * @author Thomas Müller - * @author Thomas Tanghus - * @author Vincent Petry - * - * @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 - * - */ - -/** - * This class is for i18n and l10n - * @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->get() instead - */ -class OC_L10N implements \OCP\IL10N { - /** - * cache - */ - protected static $cache = array(); - protected static $availableLanguages = array(); - - /** - * The best language - */ - protected static $language = ''; - - /** - * App of this object - */ - protected $app; - - /** - * Language of this object - */ - protected $lang; - - /** - * Translations - */ - private $translations = array(); - - /** - * Plural forms (string) - */ - private $pluralFormString = 'nplurals=2; plural=(n != 1);'; - - /** - * Plural forms (function) - */ - private $pluralFormFunction = null; - - /** - * The constructor - * @param string $app app requesting l10n - * @param string $lang default: null Language - * - * 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; - } - - /** - * @param $transFile - * @return bool - */ - public function load($transFile) { - $this->app = true; - - $json = json_decode(file_get_contents($transFile), true); - if (!is_array($json)) { - $jsonError = json_last_error(); - \OC::$server->getLogger()->warning("Failed to load $transFile - json error code: $jsonError", ['app' => 'l10n']); - return false; - } - - $this->pluralFormString = $json['pluralForm']; - $translations = $json['translations']; - - $this->translations = array_merge($this->translations, $translations); - - return true; - } - - protected function init() { - if ($this->app === true) { - return; - } - $app = $this->app; - $lang = $this->lang; - $this->app = true; - - /** @var \OC\L10N\Factory $factory */ - $factory = \OC::$server->getL10NFactory(); - $languageFiles = $factory->getL10nFilesForApp($app, $lang); - - $this->translations = []; - foreach ($languageFiles as $languageFile) { - $this->load($languageFile); - } - } - - /** - * Translating - * @param string $text The text we need a translation for - * @param array $parameters default:array() Parameters for sprintf - * @return \OC_L10N_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 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 \OC_L10N_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()) { - $this->init(); - $identifier = "_${text_singular}_::_${text_plural}_"; - if( array_key_exists($identifier, $this->translations)) { - return new OC_L10N_String( $this, $identifier, $parameters, $count ); - }else{ - if($count === 1) { - return new OC_L10N_String($this, $text_singular, $parameters, $count); - }else{ - return new OC_L10N_String($this, $text_plural, $parameters, $count); - } - } - } - - /** - * getTranslations - * @return array Fetch all translations - * - * Returns an associative array with all translations - */ - public function getTranslations() { - $this->init(); - return $this->translations; - } - - /** - * getPluralFormFunction - * @return string the plural form function - * - * returned function accepts the argument $n - */ - public function getPluralFormFunction() { - $this->init(); - if (is_null($this->pluralFormFunction)) { - $this->pluralFormFunction = \OC::$server->getL10NFactory()->createPluralFunction($this->pluralFormString); - } - return $this->pluralFormFunction; - } - - /** - * Localization - * @param string $type Type of localization - * @param array|int|string $data parameters for this localization - * @param array $options - * @return string|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, $options = array()) { - if ($type === 'firstday') { - return $this->getFirstWeekDay(); - } - if ($type === 'jsdate') { - return $this->getDateFormat(); - } - - $this->init(); - $value = new DateTime(); - if($data instanceof DateTime) { - $value = $data; - } elseif(is_string($data) && !is_numeric($data)) { - $data = strtotime($data); - $value->setTimestamp($data); - } else { - $value->setTimestamp($data); - } - - // Use the language of the instance - $locale = $this->transformToCLDRLocale($this->getLanguageCode()); - - $options = array_merge(array('width' => 'long'), $options); - $width = $options['width']; - switch($type) { - case 'date': - return Punic\Calendar::formatDate($value, $width, $locale); - case 'datetime': - return Punic\Calendar::formatDatetime($value, $width, $locale); - case 'time': - return Punic\Calendar::formatTime($value, $width, $locale); - default: - return false; - } - } - - /** - * The code (en, de, ...) of the language that is used for this OC_L10N object - * - * @return string language - */ - public function getLanguageCode() { - 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; - } - - /** - * find the best language - * @param string $app - * @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) { - return \OC::$server->getL10NFactory()->findLanguage($app); - } - - /** - * @return string - * @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->setLanguageFromRequest() instead - */ - 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) { - 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) { - return \OC::$server->getL10NFactory()->languageExists($app, $lang); - } -} diff --git a/lib/private/legacy/l10n.php b/lib/private/legacy/l10n.php new file mode 100644 index 00000000000..5d5d89100ac --- /dev/null +++ b/lib/private/legacy/l10n.php @@ -0,0 +1,348 @@ + + * @author Andreas Fischer + * @author Bart Visscher + * @author Bernhard Posselt + * @author Jakob Sack + * @author Jan-Christoph Borchardt + * @author Joas Schilling + * @author Jörn Friedrich Dreyer + * @author Lennart Rosam + * @author Lukas Reschke + * @author Morris Jobke + * @author Robin Appelman + * @author Robin McCorkell + * @author Scrutinizer Auto-Fixer + * @author Thomas Müller + * @author Thomas Tanghus + * @author Vincent Petry + * + * @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 + * + */ + +/** + * This class is for i18n and l10n + * @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->get() instead + */ +class OC_L10N implements \OCP\IL10N { + /** + * cache + */ + protected static $cache = array(); + protected static $availableLanguages = array(); + + /** + * The best language + */ + protected static $language = ''; + + /** + * App of this object + */ + protected $app; + + /** + * Language of this object + */ + protected $lang; + + /** + * Translations + */ + private $translations = array(); + + /** + * Plural forms (string) + */ + private $pluralFormString = 'nplurals=2; plural=(n != 1);'; + + /** + * Plural forms (function) + */ + private $pluralFormFunction = null; + + /** + * The constructor + * @param string $app app requesting l10n + * @param string $lang default: null Language + * + * 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; + } + + /** + * @param $transFile + * @return bool + */ + public function load($transFile) { + $this->app = true; + + $json = json_decode(file_get_contents($transFile), true); + if (!is_array($json)) { + $jsonError = json_last_error(); + \OC::$server->getLogger()->warning("Failed to load $transFile - json error code: $jsonError", ['app' => 'l10n']); + return false; + } + + $this->pluralFormString = $json['pluralForm']; + $translations = $json['translations']; + + $this->translations = array_merge($this->translations, $translations); + + return true; + } + + protected function init() { + if ($this->app === true) { + return; + } + $app = $this->app; + $lang = $this->lang; + $this->app = true; + + /** @var \OC\L10N\Factory $factory */ + $factory = \OC::$server->getL10NFactory(); + $languageFiles = $factory->getL10nFilesForApp($app, $lang); + + $this->translations = []; + foreach ($languageFiles as $languageFile) { + $this->load($languageFile); + } + } + + /** + * Translating + * @param string $text The text we need a translation for + * @param array $parameters default:array() Parameters for sprintf + * @return \OC_L10N_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 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 \OC_L10N_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()) { + $this->init(); + $identifier = "_${text_singular}_::_${text_plural}_"; + if( array_key_exists($identifier, $this->translations)) { + return new OC_L10N_String( $this, $identifier, $parameters, $count ); + }else{ + if($count === 1) { + return new OC_L10N_String($this, $text_singular, $parameters, $count); + }else{ + return new OC_L10N_String($this, $text_plural, $parameters, $count); + } + } + } + + /** + * getTranslations + * @return array Fetch all translations + * + * Returns an associative array with all translations + */ + public function getTranslations() { + $this->init(); + return $this->translations; + } + + /** + * getPluralFormFunction + * @return string the plural form function + * + * returned function accepts the argument $n + */ + public function getPluralFormFunction() { + $this->init(); + if (is_null($this->pluralFormFunction)) { + $this->pluralFormFunction = \OC::$server->getL10NFactory()->createPluralFunction($this->pluralFormString); + } + return $this->pluralFormFunction; + } + + /** + * Localization + * @param string $type Type of localization + * @param array|int|string $data parameters for this localization + * @param array $options + * @return string|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, $options = array()) { + if ($type === 'firstday') { + return $this->getFirstWeekDay(); + } + if ($type === 'jsdate') { + return $this->getDateFormat(); + } + + $this->init(); + $value = new DateTime(); + if($data instanceof DateTime) { + $value = $data; + } elseif(is_string($data) && !is_numeric($data)) { + $data = strtotime($data); + $value->setTimestamp($data); + } else { + $value->setTimestamp($data); + } + + // Use the language of the instance + $locale = $this->transformToCLDRLocale($this->getLanguageCode()); + + $options = array_merge(array('width' => 'long'), $options); + $width = $options['width']; + switch($type) { + case 'date': + return Punic\Calendar::formatDate($value, $width, $locale); + case 'datetime': + return Punic\Calendar::formatDatetime($value, $width, $locale); + case 'time': + return Punic\Calendar::formatTime($value, $width, $locale); + default: + return false; + } + } + + /** + * The code (en, de, ...) of the language that is used for this OC_L10N object + * + * @return string language + */ + public function getLanguageCode() { + 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; + } + + /** + * find the best language + * @param string $app + * @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) { + return \OC::$server->getL10NFactory()->findLanguage($app); + } + + /** + * @return string + * @deprecated 9.0.0 Use \OC::$server->getL10NFactory()->setLanguageFromRequest() instead + */ + 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) { + 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) { + return \OC::$server->getL10NFactory()->languageExists($app, $lang); + } +} -- cgit v1.2.3