From d8921ccd85f3414c1f3e13b23b6da735bf9b69b8 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 5 Jul 2017 17:36:43 +0200 Subject: [PATCH 01/19] Allow user to choose locale Signed-off-by: Thomas Citharel --- .../lib/Controller/UsersController.php | 20 + .../Settings/Personal/PersonalInfo.php | 48 +- lib/private/Settings/Personal/locales.json | 436 ++++++++++++++++++ settings/js/settings/personalInfo.js | 27 ++ .../settings/personal/personal.info.php | 27 ++ 5 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 lib/private/Settings/Personal/locales.json diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 2e46492b84..0430226e12 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -13,6 +13,7 @@ declare(strict_types=1); * @author Thomas Müller * @author Tom Needham * @author John Molakvoæ + * @author Thomas Citharel * * @license AGPL-3.0 * @@ -391,6 +392,19 @@ class UsersController extends AUserData { } } + // Find the data + $data['id'] = $targetUserObject->getUID(); + $data['quota'] = $this->fillStorageInfo($targetUserObject->getUID()); + $data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress(); + $data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName(); + $data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value']; + $data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value']; + $data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value']; + $data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value']; + $data['groups'] = $gids; + $data['language'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'lang'); + $data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale'); + return new DataResponse($permittedFields); } @@ -428,6 +442,7 @@ class UsersController extends AUserData { if ($this->config->getSystemValue('force_language', false) === false || $this->groupManager->isAdmin($currentLoggedInUser->getUID())) { $permittedFields[] = 'language'; + $permittedFields[] = 'locale'; } if ($this->appManager->isEnabledForUser('federatedfilesharing')) { @@ -456,6 +471,7 @@ class UsersController extends AUserData { $permittedFields[] = AccountManager::PROPERTY_EMAIL; $permittedFields[] = 'password'; $permittedFields[] = 'language'; + $permittedFields[] = 'locale'; $permittedFields[] = AccountManager::PROPERTY_PHONE; $permittedFields[] = AccountManager::PROPERTY_ADDRESS; $permittedFields[] = AccountManager::PROPERTY_WEBSITE; @@ -505,6 +521,10 @@ class UsersController extends AUserData { } $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value); break; + case 'locale': + // do some stuff + $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value); + break; case AccountManager::PROPERTY_EMAIL: if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') { $targetUser->setEMailAddress($value); diff --git a/lib/private/Settings/Personal/PersonalInfo.php b/lib/private/Settings/Personal/PersonalInfo.php index 305e3fb0a4..f6fb9d48d9 100644 --- a/lib/private/Settings/Personal/PersonalInfo.php +++ b/lib/private/Settings/Personal/PersonalInfo.php @@ -4,6 +4,7 @@ * * @author Arthur Schiwon * @author Morris Jobke + * @author Thomas Citharel * * @license GNU AGPL version 3 or any later version * @@ -106,6 +107,7 @@ class PersonalInfo implements ISettings { } $languageParameters = $this->getLanguages($user); + $localeParameters = $this->getLocales($user); $messageParameters = $this->getMessageParameters($userData); $parameters = [ @@ -134,7 +136,7 @@ class PersonalInfo implements ISettings { 'twitterVerification' => $userData[AccountManager::PROPERTY_TWITTER]['verified'], 'groups' => $this->getGroups($user), 'passwordChangeSupported' => $user->canChangePassword(), - ] + $messageParameters + $languageParameters; + ] + $messageParameters + $languageParameters + $localeParameters; return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, ''); @@ -218,6 +220,50 @@ class PersonalInfo implements ISettings { ); } + private function getLocales(IUser $user) { + $forceLanguage = $this->config->getSystemValue('force_locale', false); + if($forceLanguage !== false) { + return []; + } + + $uid = $user->getUID(); + + $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', 'en_US'); + + $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); + + $localeData = file_get_contents(__DIR__ . '/locales.json'); + $localeCodes = json_decode($localeData, true); + + $userLocale = array_filter($localeCodes, function($value) use ($userLocaleString) { + return $userLocaleString === $value['code']; + }); + + if (count($userLocale) > 0) + { + $userLocale = reset($userLocale); + } + + $localesForLanguage = array_filter($localeCodes, function($localeCode) use ($userLang) { + return 0 === strpos($localeCode['code'], $userLang); + }); + + /*$localesForLanguage = []; + + foreach (array_keys($localeCodes) as $localeCode) { + if (0 === strpos($localeCode, $userLang)) { + $localesForLanguage[] = $localeCode; + } + }*/ + + return [ + 'activelocaleLang' => $userLocaleString, + 'activelocale' => $userLocale, + 'locales' => $localeCodes, + 'localesforlanguage' => $localesForLanguage, + ]; + } + /** * @param array $userData * @return array diff --git a/lib/private/Settings/Personal/locales.json b/lib/private/Settings/Personal/locales.json new file mode 100644 index 0000000000..1098973c5e --- /dev/null +++ b/lib/private/Settings/Personal/locales.json @@ -0,0 +1,436 @@ +[ + { "code":"af_NA", "name":"Afrikaans (Namibia)"}, + { "code":"af_ZA", "name":"Afrikaans (South Africa)"}, + { "code":"af", "name":"Afrikaans"}, + { "code":"ak_GH", "name":"Akan (Ghana)"}, + { "code":"ak", "name":"Akan"}, + { "code":"sq_AL", "name":"Albanian (Albania)"}, + { "code":"sq", "name":"Albanian"}, + { "code":"am_ET", "name":"Amharic (Ethiopia)"}, + { "code":"am", "name":"Amharic"}, + { "code":"ar_DZ", "name":"Arabic (Algeria)"}, + { "code":"ar_BH", "name":"Arabic (Bahrain)"}, + { "code":"ar_EG", "name":"Arabic (Egypt)"}, + { "code":"ar_IQ", "name":"Arabic (Iraq)"}, + { "code":"ar_JO", "name":"Arabic (Jordan)"}, + { "code":"ar_KW", "name":"Arabic (Kuwait)"}, + { "code":"ar_LB", "name":"Arabic (Lebanon)"}, + { "code":"ar_LY", "name":"Arabic (Libya)"}, + { "code":"ar_MA", "name":"Arabic (Morocco)"}, + { "code":"ar_OM", "name":"Arabic (Oman)"}, + { "code":"ar_QA", "name":"Arabic (Qatar)"}, + { "code":"ar_SA", "name":"Arabic (Saudi Arabia)"}, + { "code":"ar_SD", "name":"Arabic (Sudan)"}, + { "code":"ar_SY", "name":"Arabic (Syria)"}, + { "code":"ar_TN", "name":"Arabic (Tunisia)"}, + { "code":"ar_AE", "name":"Arabic (United Arab Emirates)"}, + { "code":"ar_YE", "name":"Arabic (Yemen)"}, + { "code":"ar", "name":"Arabic"}, + { "code":"hy_AM", "name":"Armenian (Armenia)"}, + { "code":"hy", "name":"Armenian"}, + { "code":"as_IN", "name":"Assamese (India)"}, + { "code":"as", "name":"Assamese"}, + { "code":"asa_TZ", "name":"Asu (Tanzania)"}, + { "code":"asa", "name":"Asu"}, + { "code":"az_Cyrl", "name":"Azerbaijani (Cyrillic)"}, + { "code":"az_Cyrl_AZ", "name":"Azerbaijani (Cyrillic, Azerbaijan)"}, + { "code":"az_Latn", "name":"Azerbaijani (Latin)"}, + { "code":"az_Latn_AZ", "name":"Azerbaijani (Latin, Azerbaijan)"}, + { "code":"az", "name":"Azerbaijani"}, + { "code":"bm_ML", "name":"Bambara (Mali)"}, + { "code":"bm", "name":"Bambara"}, + { "code":"eu_ES", "name":"Basque (Spain)"}, + { "code":"eu", "name":"Basque"}, + { "code":"be_BY", "name":"Belarusian (Belarus)"}, + { "code":"be", "name":"Belarusian"}, + { "code":"bem_ZM", "name":"Bemba (Zambia)"}, + { "code":"bem", "name":"Bemba"}, + { "code":"bez_TZ", "name":"Bena (Tanzania)"}, + { "code":"bez", "name":"Bena"}, + { "code":"bn_BD", "name":"Bengali (Bangladesh)"}, + { "code":"bn_IN", "name":"Bengali (India)"}, + { "code":"bn", "name":"Bengali"}, + { "code":"bs_BA", "name":"Bosnian (Bosnia and Herzegovina)"}, + { "code":"bs", "name":"Bosnian"}, + { "code":"bg_BG", "name":"Bulgarian (Bulgaria)"}, + { "code":"bg", "name":"Bulgarian"}, + { "code":"my_MM", "name":"Burmese (Myanmar [Burma])"}, + { "code":"my", "name":"Burmese"}, + { "code":"ca_ES", "name":"Catalan (Spain)"}, + { "code":"ca", "name":"Catalan"}, + { "code":"tzm_Latn", "name":"Central Morocco Tamazight (Latin)"}, + { "code":"tzm_Latn_MA", "name":"Central Morocco Tamazight (Latin, Morocco)"}, + { "code":"tzm", "name":"Central Morocco Tamazight"}, + { "code":"chr_US", "name":"Cherokee (United States)"}, + { "code":"chr", "name":"Cherokee"}, + { "code":"cgg_UG", "name":"Chiga (Uganda)"}, + { "code":"cgg", "name":"Chiga"}, + { "code":"zh_Hans", "name":"Chinese (Simplified Han)"}, + { "code":"zh_Hans_CN", "name":"Chinese (Simplified Han, China)"}, + { "code":"zh_Hans_HK", "name":"Chinese (Simplified Han, Hong Kong SAR China)"}, + { "code":"zh_Hans_MO", "name":"Chinese (Simplified Han, Macau SAR China)"}, + { "code":"zh_Hans_SG", "name":"Chinese (Simplified Han, Singapore)"}, + { "code":"zh_Hant", "name":"Chinese (Traditional Han)"}, + { "code":"zh_Hant_HK", "name":"Chinese (Traditional Han, Hong Kong SAR China)"}, + { "code":"zh_Hant_MO", "name":"Chinese (Traditional Han, Macau SAR China)"}, + { "code":"zh_Hant_TW", "name":"Chinese (Traditional Han, Taiwan)"}, + { "code":"zh", "name":"Chinese"}, + { "code":"kw_GB", "name":"Cornish (United Kingdom)"}, + { "code":"kw", "name":"Cornish"}, + { "code":"hr_HR", "name":"Croatian (Croatia)"}, + { "code":"hr", "name":"Croatian"}, + { "code":"cs_CZ", "name":"Czech (Czech Republic)"}, + { "code":"cs", "name":"Czech"}, + { "code":"da_DK", "name":"Danish (Denmark)"}, + { "code":"da", "name":"Danish"}, + { "code":"nl_BE", "name":"Dutch (Belgium)"}, + { "code":"nl_NL", "name":"Dutch (Netherlands)"}, + { "code":"nl", "name":"Dutch"}, + { "code":"ebu_KE", "name":"Embu (Kenya)"}, + { "code":"ebu", "name":"Embu"}, + { "code":"en_AS", "name":"English (American Samoa)"}, + { "code":"en_AU", "name":"English (Australia)"}, + { "code":"en_BE", "name":"English (Belgium)"}, + { "code":"en_BZ", "name":"English (Belize)"}, + { "code":"en_BW", "name":"English (Botswana)"}, + { "code":"en_CA", "name":"English (Canada)"}, + { "code":"en_GU", "name":"English (Guam)"}, + { "code":"en_HK", "name":"English (Hong Kong SAR China)"}, + { "code":"en_IN", "name":"English (India)"}, + { "code":"en_IE", "name":"English (Ireland)"}, + { "code":"en_JM", "name":"English (Jamaica)"}, + { "code":"en_MT", "name":"English (Malta)"}, + { "code":"en_MH", "name":"English (Marshall Islands)"}, + { "code":"en_MU", "name":"English (Mauritius)"}, + { "code":"en_NA", "name":"English (Namibia)"}, + { "code":"en_NZ", "name":"English (New Zealand)"}, + { "code":"en_MP", "name":"English (Northern Mariana Islands)"}, + { "code":"en_PK", "name":"English (Pakistan)"}, + { "code":"en_PH", "name":"English (Philippines)"}, + { "code":"en_SG", "name":"English (Singapore)"}, + { "code":"en_ZA", "name":"English (South Africa)"}, + { "code":"en_TT", "name":"English (Trinidad and Tobago)"}, + { "code":"en_UM", "name":"English (U.S. Minor Outlying Islands)"}, + { "code":"en_VI", "name":"English (U.S. Virgin Islands)"}, + { "code":"en_GB", "name":"English (United Kingdom)"}, + { "code":"en_US", "name":"English (United States)"}, + { "code":"en_ZW", "name":"English (Zimbabwe)"}, + { "code":"en", "name":"English"}, + { "code":"eo", "name":"Esperanto"}, + { "code":"et_EE", "name":"Estonian (Estonia)"}, + { "code":"et", "name":"Estonian"}, + { "code":"ee_GH", "name":"Ewe (Ghana)"}, + { "code":"ee_TG", "name":"Ewe (Togo)"}, + { "code":"ee", "name":"Ewe"}, + { "code":"fo_FO", "name":"Faroese (Faroe Islands)"}, + { "code":"fo", "name":"Faroese"}, + { "code":"fil_PH", "name":"Filipino (Philippines)"}, + { "code":"fil", "name":"Filipino"}, + { "code":"fi_FI", "name":"Finnish (Finland)"}, + { "code":"fi", "name":"Finnish"}, + { "code":"fr_BE", "name":"French (Belgium)"}, + { "code":"fr_BJ", "name":"French (Benin)"}, + { "code":"fr_BF", "name":"French (Burkina Faso)"}, + { "code":"fr_BI", "name":"French (Burundi)"}, + { "code":"fr_CM", "name":"French (Cameroon)"}, + { "code":"fr_CA", "name":"French (Canada)"}, + { "code":"fr_CF", "name":"French (Central African Republic)"}, + { "code":"fr_TD", "name":"French (Chad)"}, + { "code":"fr_KM", "name":"French (Comoros)"}, + { "code":"fr_CG", "name":"French (Congo - Brazzaville)"}, + { "code":"fr_CD", "name":"French (Congo - Kinshasa)"}, + { "code":"fr_CI", "name":"French (Côte d’Ivoire)"}, + { "code":"fr_DJ", "name":"French (Djibouti)"}, + { "code":"fr_GQ", "name":"French (Equatorial Guinea)"}, + { "code":"fr_FR", "name":"French (France)"}, + { "code":"fr_GA", "name":"French (Gabon)"}, + { "code":"fr_GP", "name":"French (Guadeloupe)"}, + { "code":"fr_GN", "name":"French (Guinea)"}, + { "code":"fr_LU", "name":"French (Luxembourg)"}, + { "code":"fr_MG", "name":"French (Madagascar)"}, + { "code":"fr_ML", "name":"French (Mali)"}, + { "code":"fr_MQ", "name":"French (Martinique)"}, + { "code":"fr_MC", "name":"French (Monaco)"}, + { "code":"fr_NE", "name":"French (Niger)"}, + { "code":"fr_RW", "name":"French (Rwanda)"}, + { "code":"fr_RE", "name":"French (Réunion)"}, + { "code":"fr_BL", "name":"French (Saint Barthélemy)"}, + { "code":"fr_MF", "name":"French (Saint Martin)"}, + { "code":"fr_SN", "name":"French (Senegal)"}, + { "code":"fr_CH", "name":"French (Switzerland)"}, + { "code":"fr_TG", "name":"French (Togo)"}, + { "code":"fr", "name":"French"}, + { "code":"ff_SN", "name":"Fulah (Senegal)"}, + { "code":"ff", "name":"Fulah"}, + { "code":"gl_ES", "name":"Galician (Spain)"}, + { "code":"gl", "name":"Galician"}, + { "code":"lg_UG", "name":"Ganda (Uganda)"}, + { "code":"lg", "name":"Ganda"}, + { "code":"ka_GE", "name":"Georgian (Georgia)"}, + { "code":"ka", "name":"Georgian"}, + { "code":"de_AT", "name":"German (Austria)"}, + { "code":"de_BE", "name":"German (Belgium)"}, + { "code":"de_DE", "name":"German (Germany)"}, + { "code":"de_LI", "name":"German (Liechtenstein)"}, + { "code":"de_LU", "name":"German (Luxembourg)"}, + { "code":"de_CH", "name":"German (Switzerland)"}, + { "code":"de", "name":"German"}, + { "code":"el_CY", "name":"Greek (Cyprus)"}, + { "code":"el_GR", "name":"Greek (Greece)"}, + { "code":"el", "name":"Greek"}, + { "code":"gu_IN", "name":"Gujarati (India)"}, + { "code":"gu", "name":"Gujarati"}, + { "code":"guz_KE", "name":"Gusii (Kenya)"}, + { "code":"guz", "name":"Gusii"}, + { "code":"ha_Latn", "name":"Hausa (Latin)"}, + { "code":"ha_Latn_GH", "name":"Hausa (Latin, Ghana)"}, + { "code":"ha_Latn_NE", "name":"Hausa (Latin, Niger)"}, + { "code":"ha_Latn_NG", "name":"Hausa (Latin, Nigeria)"}, + { "code":"ha", "name":"Hausa"}, + { "code":"haw_US", "name":"Hawaiian (United States)"}, + { "code":"haw", "name":"Hawaiian"}, + { "code":"he_IL", "name":"Hebrew (Israel)"}, + { "code":"he", "name":"Hebrew"}, + { "code":"hi_IN", "name":"Hindi (India)"}, + { "code":"hi", "name":"Hindi"}, + { "code":"hu_HU", "name":"Hungarian (Hungary)"}, + { "code":"hu", "name":"Hungarian"}, + { "code":"is_IS", "name":"Icelandic (Iceland)"}, + { "code":"is", "name":"Icelandic"}, + { "code":"ig_NG", "name":"Igbo (Nigeria)"}, + { "code":"ig", "name":"Igbo"}, + { "code":"id_ID", "name":"Indonesian (Indonesia)"}, + { "code":"id", "name":"Indonesian"}, + { "code":"ga_IE", "name":"Irish (Ireland)"}, + { "code":"ga", "name":"Irish"}, + { "code":"it_IT", "name":"Italian (Italy)"}, + { "code":"it_CH", "name":"Italian (Switzerland)"}, + { "code":"it", "name":"Italian"}, + { "code":"ja_JP", "name":"Japanese (Japan)"}, + { "code":"ja", "name":"Japanese"}, + { "code":"kea_CV", "name":"Kabuverdianu (Cape Verde)"}, + { "code":"kea", "name":"Kabuverdianu"}, + { "code":"kab_DZ", "name":"Kabyle (Algeria)"}, + { "code":"kab", "name":"Kabyle"}, + { "code":"kl_GL", "name":"Kalaallisut (Greenland)"}, + { "code":"kl", "name":"Kalaallisut"}, + { "code":"kln_KE", "name":"Kalenjin (Kenya)"}, + { "code":"kln", "name":"Kalenjin"}, + { "code":"kam_KE", "name":"Kamba (Kenya)"}, + { "code":"kam", "name":"Kamba"}, + { "code":"kn_IN", "name":"Kannada (India)"}, + { "code":"kn", "name":"Kannada"}, + { "code":"kk_Cyrl", "name":"Kazakh (Cyrillic)"}, + { "code":"kk_Cyrl_KZ", "name":"Kazakh (Cyrillic, Kazakhstan)"}, + { "code":"kk", "name":"Kazakh"}, + { "code":"km_KH", "name":"Khmer (Cambodia)"}, + { "code":"km", "name":"Khmer"}, + { "code":"ki_KE", "name":"Kikuyu (Kenya)"}, + { "code":"ki", "name":"Kikuyu"}, + { "code":"rw_RW", "name":"Kinyarwanda (Rwanda)"}, + { "code":"rw", "name":"Kinyarwanda"}, + { "code":"kok_IN", "name":"Konkani (India)"}, + { "code":"kok", "name":"Konkani"}, + { "code":"ko_KR", "name":"Korean (South Korea)"}, + { "code":"ko", "name":"Korean"}, + { "code":"khq_ML", "name":"Koyra Chiini (Mali)"}, + { "code":"khq", "name":"Koyra Chiini"}, + { "code":"ses_ML", "name":"Koyraboro Senni (Mali)"}, + { "code":"ses", "name":"Koyraboro Senni"}, + { "code":"lag_TZ", "name":"Langi (Tanzania)"}, + { "code":"lag", "name":"Langi"}, + { "code":"lv_LV", "name":"Latvian (Latvia)"}, + { "code":"lv", "name":"Latvian"}, + { "code":"lt_LT", "name":"Lithuanian (Lithuania)"}, + { "code":"lt", "name":"Lithuanian"}, + { "code":"luo_KE", "name":"Luo (Kenya)"}, + { "code":"luo", "name":"Luo"}, + { "code":"luy_KE", "name":"Luyia (Kenya)"}, + { "code":"luy", "name":"Luyia"}, + { "code":"mk_MK", "name":"Macedonian (Macedonia)"}, + { "code":"mk", "name":"Macedonian"}, + { "code":"jmc_TZ", "name":"Machame (Tanzania)"}, + { "code":"jmc", "name":"Machame"}, + { "code":"kde_TZ", "name":"Makonde (Tanzania)"}, + { "code":"kde", "name":"Makonde"}, + { "code":"mg_MG", "name":"Malagasy (Madagascar)"}, + { "code":"mg", "name":"Malagasy"}, + { "code":"ms_BN", "name":"Malay (Brunei)"}, + { "code":"ms_MY", "name":"Malay (Malaysia)"}, + { "code":"ms", "name":"Malay"}, + { "code":"ml_IN", "name":"Malayalam (India)"}, + { "code":"ml", "name":"Malayalam"}, + { "code":"mt_MT", "name":"Maltese (Malta)"}, + { "code":"mt", "name":"Maltese"}, + { "code":"gv_GB", "name":"Manx (United Kingdom)"}, + { "code":"gv", "name":"Manx"}, + { "code":"mr_IN", "name":"Marathi (India)"}, + { "code":"mr", "name":"Marathi"}, + { "code":"mas_KE", "name":"Masai (Kenya)"}, + { "code":"mas_TZ", "name":"Masai (Tanzania)"}, + { "code":"mas", "name":"Masai"}, + { "code":"mer_KE", "name":"Meru (Kenya)"}, + { "code":"mer", "name":"Meru"}, + { "code":"mfe_MU", "name":"Morisyen (Mauritius)"}, + { "code":"mfe", "name":"Morisyen"}, + { "code":"naq_NA", "name":"Nama (Namibia)"}, + { "code":"naq", "name":"Nama"}, + { "code":"ne_IN", "name":"Nepali (India)"}, + { "code":"ne_NP", "name":"Nepali (Nepal)"}, + { "code":"ne", "name":"Nepali"}, + { "code":"nd_ZW", "name":"North Ndebele (Zimbabwe)"}, + { "code":"nd", "name":"North Ndebele"}, + { "code":"nb_NO", "name":"Norwegian Bokmål (Norway)"}, + { "code":"nb", "name":"Norwegian Bokmål"}, + { "code":"nn_NO", "name":"Norwegian Nynorsk (Norway)"}, + { "code":"nn", "name":"Norwegian Nynorsk"}, + { "code":"nyn_UG", "name":"Nyankole (Uganda)"}, + { "code":"nyn", "name":"Nyankole"}, + { "code":"or_IN", "name":"Oriya (India)"}, + { "code":"or", "name":"Oriya"}, + { "code":"om_ET", "name":"Oromo (Ethiopia)"}, + { "code":"om_KE", "name":"Oromo (Kenya)"}, + { "code":"om", "name":"Oromo"}, + { "code":"ps_AF", "name":"Pashto (Afghanistan)"}, + { "code":"ps", "name":"Pashto"}, + { "code":"fa_AF", "name":"Persian (Afghanistan)"}, + { "code":"fa_IR", "name":"Persian (Iran)"}, + { "code":"fa", "name":"Persian"}, + { "code":"pl_PL", "name":"Polish (Poland)"}, + { "code":"pl", "name":"Polish"}, + { "code":"pt_BR", "name":"Portuguese (Brazil)"}, + { "code":"pt_GW", "name":"Portuguese (Guinea-Bissau)"}, + { "code":"pt_MZ", "name":"Portuguese (Mozambique)"}, + { "code":"pt_PT", "name":"Portuguese (Portugal)"}, + { "code":"pt", "name":"Portuguese"}, + { "code":"pa_Arab", "name":"Punjabi (Arabic)"}, + { "code":"pa_Arab_PK", "name":"Punjabi (Arabic, Pakistan)"}, + { "code":"pa_Guru", "name":"Punjabi (Gurmukhi)"}, + { "code":"pa_Guru_IN", "name":"Punjabi (Gurmukhi, India)"}, + { "code":"pa", "name":"Punjabi"}, + { "code":"ro_MD", "name":"Romanian (Moldova)"}, + { "code":"ro_RO", "name":"Romanian (Romania)"}, + { "code":"ro", "name":"Romanian"}, + { "code":"rm_CH", "name":"Romansh (Switzerland)"}, + { "code":"rm", "name":"Romansh"}, + { "code":"rof_TZ", "name":"Rombo (Tanzania)"}, + { "code":"rof", "name":"Rombo"}, + { "code":"ru_MD", "name":"Russian (Moldova)"}, + { "code":"ru_RU", "name":"Russian (Russia)"}, + { "code":"ru_UA", "name":"Russian (Ukraine)"}, + { "code":"ru", "name":"Russian"}, + { "code":"rwk_TZ", "name":"Rwa (Tanzania)"}, + { "code":"rwk", "name":"Rwa"}, + { "code":"saq_KE", "name":"Samburu (Kenya)"}, + { "code":"saq", "name":"Samburu"}, + { "code":"sg_CF", "name":"Sango (Central African Republic)"}, + { "code":"sg", "name":"Sango"}, + { "code":"seh_MZ", "name":"Sena (Mozambique)"}, + { "code":"seh", "name":"Sena"}, + { "code":"sr_Cyrl", "name":"Serbian (Cyrillic)"}, + { "code":"sr_Cyrl_BA", "name":"Serbian (Cyrillic, Bosnia and Herzegovina)"}, + { "code":"sr_Cyrl_ME", "name":"Serbian (Cyrillic, Montenegro)"}, + { "code":"sr_Cyrl_RS", "name":"Serbian (Cyrillic, Serbia)"}, + { "code":"sr_Latn", "name":"Serbian (Latin)"}, + { "code":"sr_Latn_BA", "name":"Serbian (Latin, Bosnia and Herzegovina)"}, + { "code":"sr_Latn_ME", "name":"Serbian (Latin, Montenegro)"}, + { "code":"sr_Latn_RS", "name":"Serbian (Latin, Serbia)"}, + { "code":"sr", "name":"Serbian"}, + { "code":"sn_ZW", "name":"Shona (Zimbabwe)"}, + { "code":"sn", "name":"Shona"}, + { "code":"ii_CN", "name":"Sichuan Yi (China)"}, + { "code":"ii", "name":"Sichuan Yi"}, + { "code":"si_LK", "name":"Sinhala (Sri Lanka)"}, + { "code":"si", "name":"Sinhala"}, + { "code":"sk_SK", "name":"Slovak (Slovakia)"}, + { "code":"sk", "name":"Slovak"}, + { "code":"sl_SI", "name":"Slovenian (Slovenia)"}, + { "code":"sl", "name":"Slovenian"}, + { "code":"xog_UG", "name":"Soga (Uganda)"}, + { "code":"xog", "name":"Soga"}, + { "code":"so_DJ", "name":"Somali (Djibouti)"}, + { "code":"so_ET", "name":"Somali (Ethiopia)"}, + { "code":"so_KE", "name":"Somali (Kenya)"}, + { "code":"so_SO", "name":"Somali (Somalia)"}, + { "code":"so", "name":"Somali"}, + { "code":"es_AR", "name":"Spanish (Argentina)"}, + { "code":"es_BO", "name":"Spanish (Bolivia)"}, + { "code":"es_CL", "name":"Spanish (Chile)"}, + { "code":"es_CO", "name":"Spanish (Colombia)"}, + { "code":"es_CR", "name":"Spanish (Costa Rica)"}, + { "code":"es_DO", "name":"Spanish (Dominican Republic)"}, + { "code":"es_EC", "name":"Spanish (Ecuador)"}, + { "code":"es_SV", "name":"Spanish (El Salvador)"}, + { "code":"es_GQ", "name":"Spanish (Equatorial Guinea)"}, + { "code":"es_GT", "name":"Spanish (Guatemala)"}, + { "code":"es_HN", "name":"Spanish (Honduras)"}, + { "code":"es_419", "name":"Spanish (Latin America)"}, + { "code":"es_MX", "name":"Spanish (Mexico)"}, + { "code":"es_NI", "name":"Spanish (Nicaragua)"}, + { "code":"es_PA", "name":"Spanish (Panama)"}, + { "code":"es_PY", "name":"Spanish (Paraguay)"}, + { "code":"es_PE", "name":"Spanish (Peru)"}, + { "code":"es_PR", "name":"Spanish (Puerto Rico)"}, + { "code":"es_ES", "name":"Spanish (Spain)"}, + { "code":"es_US", "name":"Spanish (United States)"}, + { "code":"es_UY", "name":"Spanish (Uruguay)"}, + { "code":"es_VE", "name":"Spanish (Venezuela)"}, + { "code":"es", "name":"Spanish"}, + { "code":"sw_KE", "name":"Swahili (Kenya)"}, + { "code":"sw_TZ", "name":"Swahili (Tanzania)"}, + { "code":"sw", "name":"Swahili"}, + { "code":"sv_FI", "name":"Swedish (Finland)"}, + { "code":"sv_SE", "name":"Swedish (Sweden)"}, + { "code":"sv", "name":"Swedish"}, + { "code":"gsw_CH", "name":"Swiss German (Switzerland)"}, + { "code":"gsw", "name":"Swiss German"}, + { "code":"shi_Latn", "name":"Tachelhit (Latin)"}, + { "code":"shi_Latn_MA", "name":"Tachelhit (Latin, Morocco)"}, + { "code":"shi_Tfng", "name":"Tachelhit (Tifinagh)"}, + { "code":"shi_Tfng_MA", "name":"Tachelhit (Tifinagh, Morocco)"}, + { "code":"shi", "name":"Tachelhit"}, + { "code":"dav_KE", "name":"Taita (Kenya)"}, + { "code":"dav", "name":"Taita"}, + { "code":"ta_IN", "name":"Tamil (India)"}, + { "code":"ta_LK", "name":"Tamil (Sri Lanka)"}, + { "code":"ta", "name":"Tamil"}, + { "code":"te_IN", "name":"Telugu (India)"}, + { "code":"te", "name":"Telugu"}, + { "code":"teo_KE", "name":"Teso (Kenya)"}, + { "code":"teo_UG", "name":"Teso (Uganda)"}, + { "code":"teo", "name":"Teso"}, + { "code":"th_TH", "name":"Thai (Thailand)"}, + { "code":"th", "name":"Thai"}, + { "code":"bo_CN", "name":"Tibetan (China)"}, + { "code":"bo_IN", "name":"Tibetan (India)"}, + { "code":"bo", "name":"Tibetan"}, + { "code":"ti_ER", "name":"Tigrinya (Eritrea)"}, + { "code":"ti_ET", "name":"Tigrinya (Ethiopia)"}, + { "code":"ti", "name":"Tigrinya"}, + { "code":"to_TO", "name":"Tonga (Tonga)"}, + { "code":"to", "name":"Tonga"}, + { "code":"tr_TR", "name":"Turkish (Turkey)"}, + { "code":"tr", "name":"Turkish"}, + { "code":"uk_UA", "name":"Ukrainian (Ukraine)"}, + { "code":"uk", "name":"Ukrainian"}, + { "code":"ur_IN", "name":"Urdu (India)"}, + { "code":"ur_PK", "name":"Urdu (Pakistan)"}, + { "code":"ur", "name":"Urdu"}, + { "code":"uz_Arab", "name":"Uzbek (Arabic)"}, + { "code":"uz_Arab_AF", "name":"Uzbek (Arabic, Afghanistan)"}, + { "code":"uz_Cyrl", "name":"Uzbek (Cyrillic)"}, + { "code":"uz_Cyrl_UZ", "name":"Uzbek (Cyrillic, Uzbekistan)"}, + { "code":"uz_Latn", "name":"Uzbek (Latin)"}, + { "code":"uz_Latn_UZ", "name":"Uzbek (Latin, Uzbekistan)"}, + { "code":"uz", "name":"Uzbek"}, + { "code":"vi_VN", "name":"Vietnamese (Vietnam)"}, + { "code":"vi", "name":"Vietnamese"}, + { "code":"vun_TZ", "name":"Vunjo (Tanzania)"}, + { "code":"vun", "name":"Vunjo"}, + { "code":"cy_GB", "name":"Welsh (United Kingdom)"}, + { "code":"cy", "name":"Welsh"}, + { "code":"yo_NG", "name":"Yoruba (Nigeria)"}, + { "code":"yo", "name":"Yoruba"}, + { "code":"zu_ZA", "name":"Zulu (South Africa)"}, + { "code":"zu", "name":"Zulu"} +] \ No newline at end of file diff --git a/settings/js/settings/personalInfo.js b/settings/js/settings/personalInfo.js index 565ae4a2f5..aad2311a70 100644 --- a/settings/js/settings/personalInfo.js +++ b/settings/js/settings/personalInfo.js @@ -5,6 +5,7 @@ * 2013, Morris Jobke * 2016, Christoph Wurst * 2017, Arthur Schiwon + * 2017, Thomas Citharel * This file is licensed under the Affero General Public License version 3 or later. * See the COPYING-README file. */ @@ -294,6 +295,32 @@ $(document).ready(function () { }; $("#languageinput").change(updateLanguage); + var updateLocale = function () { + if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { + OC.PasswordConfirmation.requirePasswordConfirmation(updateLocale); + return; + } + + var selectedLocale = $("#localeinput").val(), + user = OC.getCurrentUser(); + + $.ajax({ + url: OC.linkToOCS('cloud/users', 2) + user['uid'], + method: 'PUT', + data: { + key: 'locale', + value: selectedLocale + }, + success: function() { + location.reload(); + }, + fail: function() { + OC.Notification.showTemporary(t('settings', 'An error occured while changing your locale. Please reload the page and try again.')); + } + }); + }; + $("#localeinput").change(updateLocale); + var uploadparms = { pasteZone: null, done: function (e, data) { diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index f8268e199e..959650cf2f 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -3,6 +3,7 @@ * @copyright Copyright (c) 2017 Arthur Schiwon * * @author Arthur Schiwon + * @author Thomas Citharel * * @license GNU AGPL version 3 or any later version * @@ -379,3 +380,29 @@ vendor_style('jcrop/css/jquery.Jcrop'); + +
+

+ +

+ +
From ea380b2918b298259f7f45a3e4671f50d89e6c13 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 5 Jul 2017 17:37:12 +0200 Subject: [PATCH 02/19] Allow apps to specify locale for localisation Signed-off-by: Thomas Citharel --- lib/private/L10N/Factory.php | 5 +++-- lib/private/L10N/L10N.php | 39 ++++++++++++++++++++++++++---------- lib/public/IL10N.php | 9 +++++++++ 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index e8a734f412..c84bc53f9e 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -97,9 +97,10 @@ class Factory implements IFactory { * * @param string $app * @param string|null $lang + * @param string|null $locale * @return \OCP\IL10N */ - public function get($app, $lang = null) { + public function get($app, $lang = null, $locale = null) { $app = \OC_App::cleanAppId($app); if ($lang !== null) { $lang = str_replace(array('\0', '/', '\\', '..'), '', (string) $lang); @@ -116,7 +117,7 @@ class Factory implements IFactory { if (!isset($this->instances[$lang][$app])) { $this->instances[$lang][$app] = new L10N( - $this, $app, $lang, + $this, $app, $lang, $locale, $this->getL10nFilesForApp($app, $lang) ); } diff --git a/lib/private/L10N/L10N.php b/lib/private/L10N/L10N.php index a9b1b7377a..7087fcae04 100644 --- a/lib/private/L10N/L10N.php +++ b/lib/private/L10N/L10N.php @@ -5,6 +5,7 @@ declare(strict_types=1); * * @author Georg Ehrke * @author Joas Schilling + * @author Thomas Citharel * @author Roeland Jago Douma * * @license AGPL-3.0 @@ -41,6 +42,9 @@ class L10N implements IL10N { /** @var string Language of this object */ protected $lang; + /** @var string Locale of this object */ + protected $locale; + /** @var string Plural forms (string) */ private $pluralFormString = 'nplurals=2; plural=(n != 1);'; @@ -54,12 +58,14 @@ class L10N implements IL10N { * @param IFactory $factory * @param string $app * @param string $lang + * @param string $locale * @param array $files */ - public function __construct(IFactory $factory, $app, $lang, array $files) { + public function __construct(IFactory $factory, $app, $lang, $locale, array $files) { $this->factory = $factory; $this->app = $app; $this->lang = $lang; + $this->locale = $locale; foreach ($files as $languageFile) { $this->load($languageFile); @@ -75,6 +81,15 @@ class L10N implements IL10N { return $this->lang; } + /** + * The code (en_US, fr_CA, ...) of the locale that is used for this instance + * + * @return string locale + */ + public function getLocaleCode() { + return $this->locale; + } + /** * Translating * @param string $text The text we need a translation for @@ -143,17 +158,19 @@ class L10N implements IL10N { * - jsdate: Returns the short JS date format */ public function l(string $type, $data = null, array $options = []) { - // Use the language of the instance - $locale = $this->getLanguageCode(); - if ($locale === 'sr@latin') { - $locale = 'sr_latn'; + if (null === $this->locale) { + // Use the language of the instance + $this->locale = $this->getLanguageCode(); + } + if ($this->locale === 'sr@latin') { + $this->locale = 'sr_latn'; } if ($type === 'firstday') { - return (int) Calendar::getFirstWeekday($locale); + return (int) Calendar::getFirstWeekday($this->locale); } if ($type === 'jsdate') { - return (string) Calendar::getDateFormat('short', $locale); + return (string) Calendar::getDateFormat('short', $this->locale); } $value = new \DateTime(); @@ -171,13 +188,13 @@ class L10N implements IL10N { $width = $options['width']; switch ($type) { case 'date': - return (string) Calendar::formatDate($value, $width, $locale); + return (string) Calendar::formatDate($value, $width, $this->locale); case 'datetime': - return (string) Calendar::formatDatetime($value, $width, $locale); + return (string) Calendar::formatDatetime($value, $width, $this->locale); case 'time': - return (string) Calendar::formatTime($value, $width, $locale); + return (string) Calendar::formatTime($value, $width, $this->locale); case 'weekdayName': - return (string) Calendar::getWeekdayName($value, $width, $locale); + return (string) Calendar::getWeekdayName($value, $width, $this->locale); default: return false; } diff --git a/lib/public/IL10N.php b/lib/public/IL10N.php index 2e55c151f6..43d19059d9 100644 --- a/lib/public/IL10N.php +++ b/lib/public/IL10N.php @@ -8,6 +8,7 @@ declare(strict_types=1); * @author Joas Schilling * @author Morris Jobke * @author Roeland Jago Douma + * @author Thomas Citharel * * @license AGPL-3.0 * @@ -107,4 +108,12 @@ interface IL10N { * @since 7.0.0 */ public function getLanguageCode(): string ; + + /** + * * The code (en_US, fr_CA, ...) of the locale that is used for this IL10N object + * + * @return string locale + * @since 13.0.0 + */ + public function getLocaleCode(); } From 93011579fe27624ed746b54f1094da556d019f5a Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 5 Jul 2017 18:02:16 +0200 Subject: [PATCH 03/19] Add tests Signed-off-by: Thomas Citharel --- tests/lib/L10N/L10nTest.php | 76 +++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/tests/lib/L10N/L10nTest.php b/tests/lib/L10N/L10nTest.php index 703aa9e227..dece334a9b 100644 --- a/tests/lib/L10N/L10nTest.php +++ b/tests/lib/L10N/L10nTest.php @@ -38,7 +38,7 @@ class L10nTest extends TestCase { public function testGermanPluralTranslations() { $transFile = \OC::$SERVERROOT.'/tests/data/l10n/de.json'; - $l = new L10N($this->getFactory(), 'test', 'de', [$transFile]); + $l = new L10N($this->getFactory(), 'test', 'de', 'de_AT', [$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)); @@ -46,7 +46,7 @@ class L10nTest extends TestCase { public function testRussianPluralTranslations() { $transFile = \OC::$SERVERROOT.'/tests/data/l10n/ru.json'; - $l = new L10N($this->getFactory(), 'test', 'ru', [$transFile]); + $l = new L10N($this->getFactory(), 'test', 'ru', 'ru_UA',[$transFile]); $this->assertEquals('1 файл', (string)$l->n('%n file', '%n files', 1)); $this->assertEquals('2 файла', (string)$l->n('%n file', '%n files', 2)); @@ -70,7 +70,7 @@ class L10nTest extends TestCase { public function testCzechPluralTranslations() { $transFile = \OC::$SERVERROOT.'/tests/data/l10n/cs.json'; - $l = new L10N($this->getFactory(), 'test', 'cs', [$transFile]); + $l = new L10N($this->getFactory(), 'test', 'cs', 'cs_CZ', [$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)); @@ -80,51 +80,51 @@ class L10nTest extends TestCase { 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'), + array('February 13, 2009 at 11:31:30 PM GMT+0', 'en', 'en_US', 'datetime', '1234567890'), + array('13. Februar 2009 um 23:31:30 GMT+0', 'de', 'de_DE', 'datetime', '1234567890'), + array('February 13, 2009', 'en', 'en_US', 'date', '1234567890'), + array('13. Februar 2009', 'de', 'de_DE', 'date', '1234567890'), + array('11:31:30 PM GMT+0', 'en', 'en_US', 'time', '1234567890'), + array('23:31:30 GMT+0', 'de', 'de_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), + array('February 13, 2009 at 11:31:30 PM GMT+0', 'en', 'en_US', 'datetime', 1234567890), + array('13. Februar 2009 um 23:31:30 GMT+0', 'de', 'de_DE', 'datetime', 1234567890), + array('February 13, 2009', 'en', 'en_US', 'date', 1234567890), + array('13. Februar 2009', 'de', 'de_DE', 'date', 1234567890), + array('11:31:30 PM GMT+0', 'en', 'en_US', 'time', 1234567890), + array('23:31:30 GMT+0', 'de', 'de_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')), + array('February 13, 2009 at 11:31:30 PM GMT+0', 'en', 'en_US', 'datetime', new DateTime('@1234567890')), + array('13. Februar 2009 um 23:31:30 GMT+0', 'de', 'de_DE', 'datetime', new DateTime('@1234567890')), + array('February 13, 2009', 'en', 'en_US', 'date', new DateTime('@1234567890')), + array('13. Februar 2009', 'de', 'de_DE', 'date', new DateTime('@1234567890')), + array('11:31:30 PM GMT+0', 'en', 'en_US', 'time', new DateTime('@1234567890')), + array('23:31:30 GMT+0', 'de', 'de_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')), + array('13 February 2009 at 23:31:30 GMT+0', 'en_GB', 'en_GB', 'datetime', new DateTime('@1234567890')), + array('13 February 2009', 'en_GB', 'en_GB', 'date', new DateTime('@1234567890')), + array('23:31:30 GMT+0', 'en_GB', 'en_GB', 'time', new DateTime('@1234567890')), + array('13 February 2009 at 23:31:30 GMT+0', 'en-GB', 'en_GB', 'datetime', new DateTime('@1234567890')), + array('13 February 2009', 'en-GB', 'en_GB', 'date', new DateTime('@1234567890')), + array('23:31:30 GMT+0', 'en-GB', 'en_GB', 'time', new DateTime('@1234567890')), ); } /** * @dataProvider localizationData */ - public function testNumericStringLocalization($expectedDate, $lang, $type, $value) { - $l = new L10N($this->getFactory(), 'test', $lang, []); + public function testNumericStringLocalization($expectedDate, $lang, $locale, $type, $value) { + $l = new L10N($this->getFactory(), 'test', $lang, $locale, []); $this->assertSame($expectedDate, $l->l($type, $value)); } public function firstDayData() { return array( - array(1, 'de'), - array(0, 'en'), + array(1, 'de', 'de_DE'), + array(0, 'en', 'en_US'), ); } @@ -132,16 +132,17 @@ class L10nTest extends TestCase { * @dataProvider firstDayData * @param $expected * @param $lang + * @param $locale */ - public function testFirstWeekDay($expected, $lang) { - $l = new L10N($this->getFactory(), 'test', $lang, []); + public function testFirstWeekDay($expected, $lang, $locale) { + $l = new L10N($this->getFactory(), 'test', $lang, $locale, []); $this->assertSame($expected, $l->l('firstday', 'firstday')); } public function jsDateData() { return array( - array('dd.MM.yy', 'de'), - array('M/d/yy', 'en'), + array('dd.MM.yy', 'de', 'de_DE'), + array('M/d/yy', 'en', 'en_US'), ); } @@ -149,9 +150,10 @@ class L10nTest extends TestCase { * @dataProvider jsDateData * @param $expected * @param $lang + * @param $locale */ - public function testJSDate($expected, $lang) { - $l = new L10N($this->getFactory(), 'test', $lang, []); + public function testJSDate($expected, $lang, $locale) { + $l = new L10N($this->getFactory(), 'test', $lang, $locale, []); $this->assertSame($expected, $l->l('jsdate', 'jsdate')); } From 4a9f8623b50ab0ba6a7fd411c0750a6bd5f63c14 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 7 Jul 2017 11:17:54 +0200 Subject: [PATCH 04/19] Fix review & tests Signed-off-by: Thomas Citharel --- .../lib/Controller/UsersController.php | 4 +- .../tests/Controller/UsersControllerTest.php | 13 ++-- lib/private/L10N/Factory.php | 60 +++++++++++++++++++ .../{Settings/Personal => L10N}/locales.json | 0 .../Settings/Personal/PersonalInfo.php | 13 +--- lib/public/L10N/IFactory.php | 22 +++++++ .../settings/personal/personal.info.php | 2 + 7 files changed, 97 insertions(+), 17 deletions(-) rename lib/private/{Settings/Personal => L10N}/locales.json (100%) diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 0430226e12..32967742bd 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -522,7 +522,9 @@ class UsersController extends AUserData { $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value); break; case 'locale': - // do some stuff + if (!$this->l10nFactory->localeExists($value)) { + throw new OCSException('Invalid locale', 102); + } $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value); break; case AccountManager::PROPERTY_EMAIL: diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php index 114742de4f..0de62bc0d1 100644 --- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php +++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php @@ -747,6 +747,10 @@ class UsersControllerTest extends TestCase { ->expects($this->once()) ->method('getDisplayName') ->will($this->returnValue('Demo User')); + $targetUser + ->expects($this->exactly(5)) + ->method('getUID') + ->will($this->returnValue('UID')); $targetUser ->expects($this->once()) ->method('getHome') @@ -759,10 +763,6 @@ class UsersControllerTest extends TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('Database')); - $targetUser - ->expects($this->exactly(5)) - ->method('getUID') - ->will($this->returnValue('UID')); $expected = [ 'id' => 'UID', @@ -780,6 +780,7 @@ class UsersControllerTest extends TestCase { 'twitter' => 'twitter', 'groups' => ['group0', 'group1', 'group2'], 'language' => 'de', + 'locale' => null, ]; $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID'])); } @@ -865,7 +866,7 @@ class UsersControllerTest extends TestCase { ->method('getBackendClassName') ->will($this->returnValue('Database')); $targetUser - ->expects($this->exactly(5)) + ->expects($this->exactly(6)) ->method('getUID') ->will($this->returnValue('UID')); $this->accountManager->expects($this->any())->method('getUser') @@ -895,6 +896,7 @@ class UsersControllerTest extends TestCase { 'twitter' => 'twitter', 'groups' => [], 'language' => 'da', + 'locale' => null, ]; $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID'])); } @@ -1050,6 +1052,7 @@ class UsersControllerTest extends TestCase { 'twitter' => 'twitter', 'groups' => [], 'language' => 'ru', + 'locale' => null, ]; $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID'])); } diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index c84bc53f9e..a6b91d53e4 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -115,6 +115,10 @@ class Factory implements IFactory { $lang = $this->findLanguage($app); } + if ($locale === null || !$this->localeExists($locale)) { + $locale = $this->findLocale($app, $lang); + } + if (!isset($this->instances[$lang][$app])) { $this->instances[$lang][$app] = new L10N( $this, $app, $lang, $locale, @@ -186,6 +190,38 @@ class Factory implements IFactory { return 'en'; } + public function findLocale($app = null, $lang = null) + { + if ($this->config->getSystemValue('installed', false)) { + $userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null; + $userLocale = null; + if (null !== $userId) { + $userLocale = $this->config->getUserValue($userId, 'core', 'locale', null); + } + } else { + $userId = null; + $userLocale = null; + } + + if ($userLocale && $this->localeExists($userLocale)) { + return $userLocale; + } + + // If no user locale set, use lang as locale + if (null !== $lang && $this->localeExists($lang)) { + return $lang; + } + + // Default : use system default locale + $defaultLocale = $this->config->getSystemValue('default_locale', false); + if ($defaultLocale !== false && $this->localeExists($defaultLocale)) { + return $defaultLocale; + } + + // At last, return USA + return 'en_US'; + } + /** * Find all available languages for an app * @@ -237,6 +273,12 @@ class Factory implements IFactory { return $available; } + public function findAvailableLocales() + { + $localeData = file_get_contents(__DIR__ . '/locales.json'); + return json_decode($localeData, true); + } + /** * @param string|null $app App id or null for core * @param string $lang @@ -251,6 +293,24 @@ class Factory implements IFactory { return array_search($lang, $languages) !== false; } + /** + * @param string $locale + * @return bool + */ + public function localeExists($locale) { + if ($locale === 'en') { //english is always available + return true; + } + + $locales = $this->findAvailableLocales(); + + $userLocale = array_filter($locales, function($value) use ($locale) { + return $locale === $value['code']; + }); + + return !empty($userLocale); + } + /** * @param string|null $app * @return string diff --git a/lib/private/Settings/Personal/locales.json b/lib/private/L10N/locales.json similarity index 100% rename from lib/private/Settings/Personal/locales.json rename to lib/private/L10N/locales.json diff --git a/lib/private/Settings/Personal/PersonalInfo.php b/lib/private/Settings/Personal/PersonalInfo.php index f6fb9d48d9..267be2878d 100644 --- a/lib/private/Settings/Personal/PersonalInfo.php +++ b/lib/private/Settings/Personal/PersonalInfo.php @@ -232,14 +232,13 @@ class PersonalInfo implements ISettings { $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); - $localeData = file_get_contents(__DIR__ . '/locales.json'); - $localeCodes = json_decode($localeData, true); + $localeCodes = $this->l10nFactory->findAvailableLocales(); $userLocale = array_filter($localeCodes, function($value) use ($userLocaleString) { return $userLocaleString === $value['code']; }); - if (count($userLocale) > 0) + if (!empty($userLocale)) { $userLocale = reset($userLocale); } @@ -248,14 +247,6 @@ class PersonalInfo implements ISettings { return 0 === strpos($localeCode['code'], $userLang); }); - /*$localesForLanguage = []; - - foreach (array_keys($localeCodes) as $localeCode) { - if (0 === strpos($localeCode, $userLang)) { - $localesForLanguage[] = $localeCode; - } - }*/ - return [ 'activelocaleLang' => $userLocaleString, 'activelocale' => $userLocale, diff --git a/lib/public/L10N/IFactory.php b/lib/public/L10N/IFactory.php index 9820082c72..08e4f83416 100644 --- a/lib/public/L10N/IFactory.php +++ b/lib/public/L10N/IFactory.php @@ -53,6 +53,12 @@ interface IFactory { */ public function findAvailableLanguages($app = null); + /** + * @return array an array of available + * @since 13.0.0 + */ + public function findAvailableLocales(); + /** * @param string|null $app App id or null for core * @param string $lang @@ -60,4 +66,20 @@ interface IFactory { * @since 9.0.0 */ public function languageExists($app, $lang); + + /** + * @param string $locale + * @return bool + * @since 13.0.0 + */ + public function localeExists($locale); + + /** + * Creates a function from the plural string + * + * @param string $string + * @return string Unique function name + * @since 9.0.0 + */ + public function createPluralFunction($string); } diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index 959650cf2f..20e346c867 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -381,6 +381,7 @@ vendor_style('jcrop/css/jquery.Jcrop'); +

@@ -406,3 +407,4 @@ vendor_style('jcrop/css/jquery.Jcrop'); + From abab7eb7f071282a4a3fa839dcc6f61ac4632f6b Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 13 Jul 2017 10:34:27 +0200 Subject: [PATCH 05/19] handle config.php values Signed-off-by: Georg Ehrke --- .../lib/Controller/UsersController.php | 4 ++++ config/config.sample.php | 23 +++++++++++++++++++ lib/private/L10N/Factory.php | 15 ++++++++---- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 32967742bd..8b39de2908 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -442,6 +442,10 @@ class UsersController extends AUserData { if ($this->config->getSystemValue('force_language', false) === false || $this->groupManager->isAdmin($currentLoggedInUser->getUID())) { $permittedFields[] = 'language'; + } + + if ($this->config->getSystemValue('force_locale', false) === false || + $this->groupManager->isAdmin($currentLoggedInUser->getUID())) { $permittedFields[] = 'locale'; } diff --git a/config/config.sample.php b/config/config.sample.php index a1445d7709..2218021bab 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -178,6 +178,29 @@ $CONFIG = array( */ 'force_language' => 'en', +/** + * This sets the default locale on your Nextcloud server, using ISO_639 + * language codes such as ``en`` for English, ``de`` for German, and ``fr`` for + * French, and ISO-3166 country codes such as ``GB``, ``US``, ``CA``, as defined + * in RFC 5646. It overrides automatic locale detection on public pages like + * login or shared items. User's locale preferences configured under "personal + * -> locale" override this setting after they have logged in. + * + * Defaults to ``en`` + */ +'default_locale' => 'en_US', + +/** + * With this setting a locale can be forced for all users. If a locale is + * forced, the users are also unable to change their locale in the personal + * settings. If users shall be unable to change their locale, but users have + * different languages, this value can be set to ``true`` instead of a locale + * code. + * + * Defaults to ``false`` + */ +'force_locale' => 'en_US', + /** * Set the default app to open on login. Use the app names as they appear in the * URL after clicking them in the Apps menu, such as documents, calendar, and diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index a6b91d53e4..86f2ab3b1f 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -111,6 +111,11 @@ class Factory implements IFactory { $lang = $forceLang; } + $forceLocale = $this->config->getSystemValue('force_locale', false); + if (is_string($forceLocale)) { + $locale = $forceLocale; + } + if ($lang === null || !$this->languageExists($app, $lang)) { $lang = $this->findLanguage($app); } @@ -207,17 +212,17 @@ class Factory implements IFactory { return $userLocale; } - // If no user locale set, use lang as locale - if (null !== $lang && $this->localeExists($lang)) { - return $lang; - } - // Default : use system default locale $defaultLocale = $this->config->getSystemValue('default_locale', false); if ($defaultLocale !== false && $this->localeExists($defaultLocale)) { return $defaultLocale; } + // If no user locale set, use lang as locale + if (null !== $lang && $this->localeExists($lang)) { + return $lang; + } + // At last, return USA return 'en_US'; } From 7d8124ad66d336b99c48936e2a6a84ea4d0fef09 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 17 Jul 2017 09:49:58 +0200 Subject: [PATCH 06/19] add findLocale() to public interface Signed-off-by: Thomas Citharel --- lib/public/L10N/IFactory.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/public/L10N/IFactory.php b/lib/public/L10N/IFactory.php index 08e4f83416..4efbe84ea3 100644 --- a/lib/public/L10N/IFactory.php +++ b/lib/public/L10N/IFactory.php @@ -44,6 +44,13 @@ interface IFactory { */ public function findLanguage($app = null); + /** + * @param string|null $lang user language as default locale + * @return string locale If nothing works it returns 'en_US' + * @since 13.0.0 + */ + public function findLocale($lang = null); + /** * Find all available languages for an app * From bf6bfad76bcf40266f7a7bdc71cbf59e8873abe7 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 17 Jul 2017 10:08:51 +0200 Subject: [PATCH 07/19] OC.getLocale() now returns Locale and no longer Language Added OC.getLanguage() to get Language still gets language, though according to IETF BCP47 locale should be good. Signed-off-by: Thomas Citharel --- core/js/js.js | 9 +++++++++ core/templates/layout.user.php | 2 +- lib/private/L10N/Factory.php | 2 +- lib/private/TemplateLayout.php | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/core/js/js.js b/core/js/js.js index a7dba7981f..3cd8f30604 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -791,6 +791,15 @@ var OCP = {}, * @return {String} locale string */ getLocale: function() { + return $('html').prop('data-locale'); + }, + + /** + * Returns the user's language + * + * @returns {String} language string + */ + getLanguage: function () { return $('html').prop('lang'); }, diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 44820c4a9a..3cac14b4ab 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -1,5 +1,5 @@ - + diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index 86f2ab3b1f..8c35d75a4b 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -195,7 +195,7 @@ class Factory implements IFactory { return 'en'; } - public function findLocale($app = null, $lang = null) + public function findLocale($lang = null) { if ($this->config->getSystemValue('installed', false)) { $userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null; diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index c378396c72..be81608f07 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -131,10 +131,11 @@ class TemplateLayout extends \OC_Template { parent::__construct('core', 'layout.base'); } - // Send the language to our layouts + // Send the language and the locale to our layouts $lang = \OC::$server->getL10NFactory()->findLanguage(); $lang = str_replace('_', '-', $lang); $this->assign('language', $lang); + $this->assign('locale', \OC::$server->getL10NFactory()->findLocale()); if(\OC::$server->getSystemConfig()->getValue('installed', false)) { if (empty(self::$versionHash)) { From 9af82296c7fa01cd6f6d940f8ffcd32362b90d55 Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Sun, 12 Nov 2017 10:29:57 +0100 Subject: [PATCH 08/19] fix position of locale input Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- settings/css/settings.scss | 3 +- .../settings/personal/personal.info.php | 55 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/settings/css/settings.scss b/settings/css/settings.scss index 5742b6a552..0ca3a96360 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -118,7 +118,8 @@ input { select { &#timezone, - &#languageinput { + &#languageinput, + &#localeinput { width: 100%; } } diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index 20e346c867..83530222f3 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -341,6 +341,33 @@ vendor_style('jcrop/css/jquery.Jcrop'); </a> </form> <?php } ?> + <?php if (isset($_['activelocale'])) { ?> + <form id="locale" class="section"> + <h2> + <label for="localeinput"><?php p($l->t('Locale'));?></label> + </h2> + <select id="localeinput" name="lang" data-placeholder="<?php p($l->t('Locale'));?>"> + <option value="<?php p($_['activelocale']['code']);?>"> + <?php p($_['activelocale']['name']);?> + </option> + <optgroup label="––––––––––"></optgroup> + <?php foreach($_['localesforlanguage'] as $locale):?> + <option value="<?php p($locale['code']);?>"> + <?php p($locale['name']);?> + </option> + <?php endforeach;?> + <optgroup label="––––––––––"></optgroup> + <option value="<?php p($_['activelocale']['code']);?>"> + <?php p($_['activelocale']['name']);?> + </option> + <?php foreach($_['locales'] as $locale):?> + <option value="<?php p($locale['code']);?>"> + <?php p($locale['name']);?> + </option> + <?php endforeach;?> + </select> + </form> + <?php } ?> </div> <div class="personal-settings-setting-box personal-settings-password-box"> <?php @@ -380,31 +407,3 @@ vendor_style('jcrop/css/jquery.Jcrop'); </div> </div> - -<?php if (isset($_['activelocale'])) { ?> -<form id="locale" class="section"> - <h2> - <label for="localeinput"><?php p($l->t('Locale'));?></label> - </h2> - <select id="localeinput" name="lang" data-placeholder="<?php p($l->t('Locale'));?>"> - <option value="<?php p($_['activelocale']['code']);?>"> - <?php p($_['activelocale']['name']);?> - </option> - <optgroup label="––––––––––"></optgroup> - <?php foreach($_['localesforlanguage'] as $locale):?> - <option value="<?php p($locale['code']);?>"> - <?php p($locale['name']);?> - </option> - <?php endforeach;?> - <optgroup label="––––––––––"></optgroup> - <option value="<?php p($_['activelocale']['code']);?>"> - <?php p($_['activelocale']['name']);?> - </option> - <?php foreach($_['locales'] as $locale):?> - <option value="<?php p($locale['code']);?>"> - <?php p($locale['name']);?> - </option> - <?php endforeach;?> - </select> -</form> -<?php } ?> From fd41192bb45947f56304d465c2266f168eb6036b Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Sun, 12 Nov 2017 10:38:33 +0100 Subject: [PATCH 09/19] fix OC.getLocale() Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- core/js/js.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/js/js.js b/core/js/js.js index 3cd8f30604..c1713539f4 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -791,7 +791,7 @@ var OCP = {}, * @return {String} locale string */ getLocale: function() { - return $('html').prop('data-locale'); + return $('html').data('locale'); }, /** From 0cacdd3132d7f82801038efd5117e1629c97e749 Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Sun, 12 Nov 2017 11:06:15 +0100 Subject: [PATCH 10/19] show locale example and apply new locale right away Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- settings/css/settings.scss | 4 ++++ settings/js/settings/personalInfo.js | 10 +++++++++- settings/templates/settings/personal/personal.info.php | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/settings/css/settings.scss b/settings/css/settings.scss index 0ca3a96360..3770a30b46 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -124,6 +124,10 @@ select { } } +#localeexample { + text-align: center; +} + input { &#pass1, &#pass2, diff --git a/settings/js/settings/personalInfo.js b/settings/js/settings/personalInfo.js index aad2311a70..2f7e1c386b 100644 --- a/settings/js/settings/personalInfo.js +++ b/settings/js/settings/personalInfo.js @@ -312,7 +312,7 @@ $(document).ready(function () { value: selectedLocale }, success: function() { - location.reload(); + moment.locale(selectedLocale); }, fail: function() { OC.Notification.showTemporary(t('settings', 'An error occured while changing your locale. Please reload the page and try again.')); @@ -441,4 +441,12 @@ $(document).ready(function () { }, user.displayName); }); +window.setInterval(function() { + $('#localeexample-time').text(moment().format('LTS')); + $('#localeexample-date').text(moment().format('L')); + $('#localeexample-fdow').text(t('settings', 'Week starts on {fdow}', + {fdow: moment().weekday(0).format('dddd')})); + +}, 1000); + OC.Settings.updateAvatar = updateAvatar; diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index 83530222f3..b3952803ab 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -366,6 +366,11 @@ vendor_style('jcrop/css/jquery.Jcrop'); </option> <?php endforeach;?> </select> + <div id="localeexample"> + <p id="localeexample-time"></p> + <p id="localeexample-date"></p> + <p id="localeexample-fdow"></p> + </div> </form> <?php } ?> </div> From 3ffeb4ae060d97b9306a2c243332d3a137b1e464 Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Wed, 27 Jun 2018 19:09:09 +0200 Subject: [PATCH 11/19] fix tests / mistakes made during rebase Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- apps/provisioning_api/lib/Controller/AUserData.php | 1 + .../lib/Controller/UsersController.php | 13 ------------- .../tests/Controller/UsersControllerTest.php | 14 +++++++------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/apps/provisioning_api/lib/Controller/AUserData.php b/apps/provisioning_api/lib/Controller/AUserData.php index 2e29cc1df1..f08fef9141 100644 --- a/apps/provisioning_api/lib/Controller/AUserData.php +++ b/apps/provisioning_api/lib/Controller/AUserData.php @@ -123,6 +123,7 @@ abstract class AUserData extends OCSController { $data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value']; $data['groups'] = $gids; $data['language'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'lang'); + $data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale'); return $data; } diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 8b39de2908..52021ec248 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -392,19 +392,6 @@ class UsersController extends AUserData { } } - // Find the data - $data['id'] = $targetUserObject->getUID(); - $data['quota'] = $this->fillStorageInfo($targetUserObject->getUID()); - $data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress(); - $data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName(); - $data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value']; - $data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value']; - $data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value']; - $data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value']; - $data['groups'] = $gids; - $data['language'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'lang'); - $data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale'); - return new DataResponse($permittedFields); } diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php index 0de62bc0d1..af4d5958b5 100644 --- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php +++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php @@ -747,10 +747,6 @@ class UsersControllerTest extends TestCase { ->expects($this->once()) ->method('getDisplayName') ->will($this->returnValue('Demo User')); - $targetUser - ->expects($this->exactly(5)) - ->method('getUID') - ->will($this->returnValue('UID')); $targetUser ->expects($this->once()) ->method('getHome') @@ -763,6 +759,10 @@ class UsersControllerTest extends TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('Database')); + $targetUser + ->expects($this->exactly(6)) + ->method('getUID') + ->will($this->returnValue('UID')); $expected = [ 'id' => 'UID', @@ -1006,7 +1006,7 @@ class UsersControllerTest extends TestCase { ->method('getEMailAddress') ->will($this->returnValue('subadmin@nextcloud.com')); $targetUser - ->expects($this->exactly(5)) + ->expects($this->exactly(6)) ->method('getUID') ->will($this->returnValue('UID')); $targetUser @@ -1239,7 +1239,7 @@ class UsersControllerTest extends TestCase { ->with('UserToEdit') ->will($this->returnValue($targetUser)); $this->groupManager - ->expects($this->exactly(2)) + ->expects($this->exactly(3)) ->method('isAdmin') ->with('UID') ->will($this->returnValue(true)); @@ -1274,7 +1274,7 @@ class UsersControllerTest extends TestCase { ->with('UserToEdit') ->will($this->returnValue($targetUser)); $this->groupManager - ->expects($this->exactly(2)) + ->expects($this->exactly(3)) ->method('isAdmin') ->with('UID') ->will($this->returnValue(true)); From 51a35c23f6fcaff85be8e5785447092e2b982681 Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Wed, 27 Jun 2018 19:40:06 +0200 Subject: [PATCH 12/19] fix camelCase for localesForLanguage Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- lib/private/Settings/Personal/PersonalInfo.php | 2 +- settings/templates/settings/personal/personal.info.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/Settings/Personal/PersonalInfo.php b/lib/private/Settings/Personal/PersonalInfo.php index 267be2878d..f4a8548e8f 100644 --- a/lib/private/Settings/Personal/PersonalInfo.php +++ b/lib/private/Settings/Personal/PersonalInfo.php @@ -251,7 +251,7 @@ class PersonalInfo implements ISettings { 'activelocaleLang' => $userLocaleString, 'activelocale' => $userLocale, 'locales' => $localeCodes, - 'localesforlanguage' => $localesForLanguage, + 'localesForLanguage' => $localesForLanguage, ]; } diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index b3952803ab..f2cc270083 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -351,7 +351,7 @@ vendor_style('jcrop/css/jquery.Jcrop'); <?php p($_['activelocale']['name']);?> </option> <optgroup label="––––––––––"></optgroup> - <?php foreach($_['localesforlanguage'] as $locale):?> + <?php foreach($_['localesForLanguage'] as $locale):?> <option value="<?php p($locale['code']);?>"> <?php p($locale['name']);?> </option> From b3921c65a68111aa319e75f546953f77ebd8008f Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Wed, 27 Jun 2018 19:43:46 +0200 Subject: [PATCH 13/19] cache available locales to minimize fs access Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- lib/private/L10N/Factory.php | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index 8c35d75a4b..6d6f452738 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -54,6 +54,11 @@ class Factory implements IFactory { */ protected $availableLanguages = []; + /** + * @var array + */ + protected $availableLocales = []; + /** * @var array Structure: string => callable */ @@ -195,8 +200,13 @@ class Factory implements IFactory { return 'en'; } - public function findLocale($lang = null) - { + /** + * find the best locale + * + * @param string $lang + * @return null|string + */ + public function findLocale($lang = null) { if ($this->config->getSystemValue('installed', false)) { $userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null; $userLocale = null; @@ -278,10 +288,18 @@ class Factory implements IFactory { return $available; } - public function findAvailableLocales() - { + /** + * @return array|mixed + */ + public function findAvailableLocales() { + if (!empty($this->availableLocales)) { + return $this->availableLocales; + } + $localeData = file_get_contents(__DIR__ . '/locales.json'); - return json_decode($localeData, true); + $this->availableLocales = \json_decode($localeData, true); + + return $this->availableLocales; } /** @@ -308,7 +326,6 @@ class Factory implements IFactory { } $locales = $this->findAvailableLocales(); - $userLocale = array_filter($locales, function($value) use ($locale) { return $locale === $value['code']; }); From 9157086c23c8a82479084cc2c12a98f5f726ef5f Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Wed, 27 Jun 2018 20:03:28 +0200 Subject: [PATCH 14/19] properly handle force_locale setting Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- lib/private/L10N/Factory.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index 6d6f452738..b8774c2f74 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -207,6 +207,11 @@ class Factory implements IFactory { * @return null|string */ public function findLocale($lang = null) { + $forceLocale = $this->config->getSystemValue('force_locale', false); + if (is_string($forceLocale) && $this->localeExists($forceLocale)) { + return $forceLocale; + } + if ($this->config->getSystemValue('installed', false)) { $userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null; $userLocale = null; From b17d256761ef244bacbadd37f661bfe92ed6972e Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Wed, 27 Jun 2018 21:12:08 +0200 Subject: [PATCH 15/19] fix use of findLocale Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- lib/private/L10N/Factory.php | 2 +- lib/private/TemplateLayout.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index b8774c2f74..d824fa461e 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -126,7 +126,7 @@ class Factory implements IFactory { } if ($locale === null || !$this->localeExists($locale)) { - $locale = $this->findLocale($app, $lang); + $locale = $this->findLocale($lang); } if (!isset($this->instances[$lang][$app])) { diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index be81608f07..5e6719593a 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -135,7 +135,7 @@ class TemplateLayout extends \OC_Template { $lang = \OC::$server->getL10NFactory()->findLanguage(); $lang = str_replace('_', '-', $lang); $this->assign('language', $lang); - $this->assign('locale', \OC::$server->getL10NFactory()->findLocale()); + $this->assign('locale', \OC::$server->getL10NFactory()->findLocale($lang)); if(\OC::$server->getSystemConfig()->getValue('installed', false)) { if (empty(self::$versionHash)) { From 8c73b13ac870b0a6b75b701f4f52463c198421a5 Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Wed, 27 Jun 2018 21:22:42 +0200 Subject: [PATCH 16/19] move locales file to /resources/ Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- lib/private/L10N/Factory.php | 2 +- {lib/private/L10N => resources}/locales.json | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {lib/private/L10N => resources}/locales.json (100%) diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index d824fa461e..8c8735836b 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -301,7 +301,7 @@ class Factory implements IFactory { return $this->availableLocales; } - $localeData = file_get_contents(__DIR__ . '/locales.json'); + $localeData = file_get_contents(\OC::$SERVERROOT . '/resources/locales.json'); $this->availableLocales = \json_decode($localeData, true); return $this->availableLocales; diff --git a/lib/private/L10N/locales.json b/resources/locales.json similarity index 100% rename from lib/private/L10N/locales.json rename to resources/locales.json From 325403ac24099e5e7d41ca97b4d7b1d2daa8c425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= <skjnldsv@protonmail.com> Date: Thu, 28 Jun 2018 17:28:27 +0200 Subject: [PATCH 17/19] Design fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> --- core/css/icons.scss | 4 ++++ core/img/actions/timezone.svg | 3 +++ settings/css/settings.scss | 9 +++------ settings/templates/settings/personal/personal.info.php | 4 +++- 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 core/img/actions/timezone.svg diff --git a/core/css/icons.scss b/core/css/icons.scss index 6685f53a08..91a90abe27 100644 --- a/core/css/icons.scss +++ b/core/css/icons.scss @@ -402,6 +402,10 @@ img, object, video, button, textarea, input, select, div[contenteditable='true'] background-image: url('../img/actions/tag.svg?v=1'); } +.icon-timezone { + background-image: url('../img/actions/timezone.svg?v=1'); +} + .icon-toggle { background-image: url('../img/actions/toggle.svg?v=1'); } diff --git a/core/img/actions/timezone.svg b/core/img/actions/timezone.svg new file mode 100644 index 0000000000..f12c366574 --- /dev/null +++ b/core/img/actions/timezone.svg @@ -0,0 +1,3 @@ +<svg enable-background="new 0 0 15 15" version="1.1" viewBox="0 0 15 15" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" width="16" height="16"> + <path d="m14.982 7c-0.246-3.744-3.238-6.737-6.982-6.983v-0.017h-1v0.017c-3.744 0.246-6.737 3.239-6.983 6.983h-0.017v1h0.017c0.246 3.744 3.239 6.736 6.983 6.982v0.018h1v-0.018c3.744-0.246 6.736-3.238 6.982-6.982h0.018v-1h-0.018zm-10.287-5.365c-0.483 0.642-0.884 1.447-1.176 2.365h-1.498c0.652-1.017 1.578-1.84 2.674-2.365zm-3.197 3.365h1.758c-0.134 0.632-0.219 1.303-0.246 2h-1.991c0.053-0.704 0.219-1.377 0.479-2zm-0.479 3h1.991c0.027 0.697 0.112 1.368 0.246 2h-1.758c-0.26-0.623-0.426-1.296-0.479-2zm1.002 3h1.497c0.292 0.918 0.693 1.723 1.177 2.365-1.096-0.525-2.022-1.347-2.674-2.365zm4.979 2.936c-1.028-0.275-1.913-1.379-2.45-2.936h2.45v2.936zm0-3.936h-2.731c-0.141-0.623-0.23-1.296-0.259-2h2.99v2zm0-3h-2.99c0.029-0.704 0.118-1.377 0.259-2h2.731v2zm0-3h-2.45c0.537-1.557 1.422-2.661 2.45-2.935v2.935zm5.979 0h-1.496c-0.293-0.918-0.693-1.723-1.178-2.365 1.095 0.525 2.022 1.348 2.674 2.365zm-4.979-2.935c1.027 0.274 1.913 1.378 2.45 2.935h-2.45v-2.935zm0 3.935h2.73c0.142 0.623 0.229 1.296 0.26 2h-2.99v-2zm0 3h2.99c-0.029 0.704-0.118 1.377-0.26 2h-2.73v-2zm0 5.936v-2.936h2.45c-0.537 1.557-1.423 2.661-2.45 2.936zm2.305-0.571c0.483-0.643 0.885-1.447 1.178-2.365h1.496c-0.652 1.018-1.579 1.84-2.674 2.365zm3.197-3.365h-1.758c0.134-0.632 0.219-1.303 0.246-2h1.99c-0.052 0.704-0.218 1.377-0.478 2zm-1.512-3c-0.027-0.697-0.112-1.368-0.246-2h1.758c0.26 0.623 0.426 1.296 0.479 2h-1.991z"/> +</svg> diff --git a/settings/css/settings.scss b/settings/css/settings.scss index 3770a30b46..cfca22d5bb 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -103,7 +103,7 @@ input { .profile-settings-container { display: inline-grid; grid-template-columns: 1fr; - grid-template-rows: 1fr 2fr 1fr; + grid-template-rows: 1fr 1fr 1fr 1fr; } .personal-show-container { @@ -124,10 +124,6 @@ select { } } -#localeexample { - text-align: center; -} - input { &#pass1, &#pass2, @@ -150,7 +146,8 @@ input { } .personal-info { margin-right: 10%; - margin-bottom: 20px; + margin-bottom: 12px; + margin-top: 12px; } .personal-info[class^='icon-'], .personal-info[class*=' icon-'] { background-position: 0px 2px; diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index f2cc270083..1946e3ec66 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -341,6 +341,8 @@ vendor_style('jcrop/css/jquery.Jcrop'); </a> </form> <?php } ?> + </div> + <div class="personal-settings-setting-box personal-settings-locale-box"> <?php if (isset($_['activelocale'])) { ?> <form id="locale" class="section"> <h2> @@ -366,7 +368,7 @@ vendor_style('jcrop/css/jquery.Jcrop'); </option> <?php endforeach;?> </select> - <div id="localeexample"> + <div id="localeexample" class="personal-info icon-timezone"> <p id="localeexample-time"></p> <p id="localeexample-date"></p> <p id="localeexample-fdow"></p> From 4c1e581f4793d0556d42a67911458c4c282c4507 Mon Sep 17 00:00:00 2001 From: Georg Ehrke <developer@georgehrke.com> Date: Thu, 28 Jun 2018 17:37:16 +0200 Subject: [PATCH 18/19] docblock version fixes Signed-off-by: Georg Ehrke <developer@georgehrke.com> --- lib/private/L10N/L10N.php | 2 +- lib/public/IL10N.php | 4 ++-- lib/public/L10N/IFactory.php | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/private/L10N/L10N.php b/lib/private/L10N/L10N.php index 7087fcae04..a12375c421 100644 --- a/lib/private/L10N/L10N.php +++ b/lib/private/L10N/L10N.php @@ -86,7 +86,7 @@ class L10N implements IL10N { * * @return string locale */ - public function getLocaleCode() { + public function getLocaleCode(): string { return $this->locale; } diff --git a/lib/public/IL10N.php b/lib/public/IL10N.php index 43d19059d9..53decd7805 100644 --- a/lib/public/IL10N.php +++ b/lib/public/IL10N.php @@ -113,7 +113,7 @@ interface IL10N { * * The code (en_US, fr_CA, ...) of the locale that is used for this IL10N object * * @return string locale - * @since 13.0.0 + * @since 14.0.0 */ - public function getLocaleCode(); + public function getLocaleCode(): string; } diff --git a/lib/public/L10N/IFactory.php b/lib/public/L10N/IFactory.php index 4efbe84ea3..8c0a9fb448 100644 --- a/lib/public/L10N/IFactory.php +++ b/lib/public/L10N/IFactory.php @@ -47,7 +47,7 @@ interface IFactory { /** * @param string|null $lang user language as default locale * @return string locale If nothing works it returns 'en_US' - * @since 13.0.0 + * @since 14.0.0 */ public function findLocale($lang = null); @@ -77,7 +77,7 @@ interface IFactory { /** * @param string $locale * @return bool - * @since 13.0.0 + * @since 14.0.0 */ public function localeExists($locale); @@ -86,7 +86,7 @@ interface IFactory { * * @param string $string * @return string Unique function name - * @since 9.0.0 + * @since 14.0.0 */ public function createPluralFunction($string); } From c1682f4228e29cfe9203fed63e7f203dc582482b Mon Sep 17 00:00:00 2001 From: Morris Jobke <hey@morrisjobke.de> Date: Fri, 29 Jun 2018 08:02:50 +0200 Subject: [PATCH 19/19] Update PHPDoc Signed-off-by: Morris Jobke <hey@morrisjobke.de> --- lib/public/L10N/IFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/L10N/IFactory.php b/lib/public/L10N/IFactory.php index 8c0a9fb448..263ebe81d0 100644 --- a/lib/public/L10N/IFactory.php +++ b/lib/public/L10N/IFactory.php @@ -62,7 +62,7 @@ interface IFactory { /** * @return array an array of available - * @since 13.0.0 + * @since 14.0.0 */ public function findAvailableLocales();