Skip to content

Commit

Permalink
Merge pull request #5623 from nextcloud/locale-setting
Browse files Browse the repository at this point in the history
Add user locale/region setting
  • Loading branch information
MorrisJobke authored Jun 29, 2018
2 parents 89b6ee1 + c1682f4 commit 61842f6
Show file tree
Hide file tree
Showing 19 changed files with 808 additions and 61 deletions.
1 change: 1 addition & 0 deletions apps/provisioning_api/lib/Controller/AUserData.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ protected function getUserData(string $userId): array {
$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;
}
Expand Down
13 changes: 13 additions & 0 deletions apps/provisioning_api/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Tom Needham <tom@owncloud.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Thomas Citharel <tcit@tcit.fr>
*
* @license AGPL-3.0
*
Expand Down Expand Up @@ -430,6 +431,11 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$permittedFields[] = 'language';
}

if ($this->config->getSystemValue('force_locale', false) === false ||
$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
$permittedFields[] = 'locale';
}

if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
$federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application();
$shareProvider = $federatedFileSharing->getFederatedShareProvider();
Expand All @@ -456,6 +462,7 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$permittedFields[] = AccountManager::PROPERTY_EMAIL;
$permittedFields[] = 'password';
$permittedFields[] = 'language';
$permittedFields[] = 'locale';
$permittedFields[] = AccountManager::PROPERTY_PHONE;
$permittedFields[] = AccountManager::PROPERTY_ADDRESS;
$permittedFields[] = AccountManager::PROPERTY_WEBSITE;
Expand Down Expand Up @@ -505,6 +512,12 @@ public function editUser(string $userId, string $key, string $value): DataRespon
}
$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
break;
case 'locale':
if (!$this->l10nFactory->localeExists($value)) {
throw new OCSException('Invalid locale', 102);
}
$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
break;
case AccountManager::PROPERTY_EMAIL:
if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
$targetUser->setEMailAddress($value);
Expand Down
13 changes: 8 additions & 5 deletions apps/provisioning_api/tests/Controller/UsersControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ public function testGetUserDataAsAdmin() {
->method('getBackendClassName')
->will($this->returnValue('Database'));
$targetUser
->expects($this->exactly(5))
->expects($this->exactly(6))
->method('getUID')
->will($this->returnValue('UID'));

Expand All @@ -780,6 +780,7 @@ public function testGetUserDataAsAdmin() {
'twitter' => 'twitter',
'groups' => ['group0', 'group1', 'group2'],
'language' => 'de',
'locale' => null,
];
$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
}
Expand Down Expand Up @@ -865,7 +866,7 @@ public function testGetUserDataAsSubAdminAndUserIsAccessible() {
->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')
Expand Down Expand Up @@ -895,6 +896,7 @@ public function testGetUserDataAsSubAdminAndUserIsAccessible() {
'twitter' => 'twitter',
'groups' => [],
'language' => 'da',
'locale' => null,
];
$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
}
Expand Down Expand Up @@ -1004,7 +1006,7 @@ public function testGetUserDataAsSubAdminSelfLookup() {
->method('getEMailAddress')
->will($this->returnValue('subadmin@nextcloud.com'));
$targetUser
->expects($this->exactly(5))
->expects($this->exactly(6))
->method('getUID')
->will($this->returnValue('UID'));
$targetUser
Expand Down Expand Up @@ -1050,6 +1052,7 @@ public function testGetUserDataAsSubAdminSelfLookup() {
'twitter' => 'twitter',
'groups' => [],
'language' => 'ru',
'locale' => null,
];
$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
}
Expand Down Expand Up @@ -1236,7 +1239,7 @@ public function testEditUserAdminUserSelfEditChangeValidQuota() {
->with('UserToEdit')
->will($this->returnValue($targetUser));
$this->groupManager
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('isAdmin')
->with('UID')
->will($this->returnValue(true));
Expand Down Expand Up @@ -1271,7 +1274,7 @@ public function testEditUserAdminUserSelfEditChangeInvalidQuota() {
->with('UserToEdit')
->will($this->returnValue($targetUser));
$this->groupManager
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('isAdmin')
->with('UID')
->will($this->returnValue(true));
Expand Down
23 changes: 23 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,29 @@
*/
'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
Expand Down
4 changes: 4 additions & 0 deletions core/css/icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
Expand Down
3 changes: 3 additions & 0 deletions core/img/actions/timezone.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions core/js/js.js
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,15 @@ var OCP = {},
* @return {String} locale string
*/
getLocale: function() {
return $('html').data('locale');
},

/**
* Returns the user's language
*
* @returns {String} language string
*/
getLanguage: function () {
return $('html').prop('lang');
},

Expand Down
2 changes: 1 addition & 1 deletion core/templates/layout.user.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="ng-csp" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" >
<html class="ng-csp" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" data-locale="<?php p($_['locale']); ?>" >
<head data-user="<?php p($_['user_uid']); ?>" data-user-displayname="<?php p($_['user_displayname']); ?>" data-requesttoken="<?php p($_['requesttoken']); ?>">
<meta charset="utf-8">
<title>
Expand Down
92 changes: 90 additions & 2 deletions lib/private/L10N/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ class Factory implements IFactory {
*/
protected $availableLanguages = [];

/**
* @var array
*/
protected $availableLocales = [];

/**
* @var array Structure: string => callable
*/
Expand Down Expand Up @@ -97,9 +102,10 @@ public function __construct(IConfig $config,
*
* @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);
Expand All @@ -110,13 +116,22 @@ public function get($app, $lang = null) {
$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);
}

if ($locale === null || !$this->localeExists($locale)) {
$locale = $this->findLocale($lang);
}

if (!isset($this->instances[$lang][$app])) {
$this->instances[$lang][$app] = new L10N(
$this, $app, $lang,
$this, $app, $lang, $locale,
$this->getL10nFilesForApp($app, $lang)
);
}
Expand Down Expand Up @@ -185,6 +200,48 @@ public function findLanguage($app = null) {
return 'en';
}

/**
* find the best locale
*
* @param string $lang
* @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;
if (null !== $userId) {
$userLocale = $this->config->getUserValue($userId, 'core', 'locale', null);
}
} else {
$userId = null;
$userLocale = null;
}

if ($userLocale && $this->localeExists($userLocale)) {
return $userLocale;
}

// 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';
}

/**
* Find all available languages for an app
*
Expand Down Expand Up @@ -236,6 +293,20 @@ public function findAvailableLanguages($app = null) {
return $available;
}

/**
* @return array|mixed
*/
public function findAvailableLocales() {
if (!empty($this->availableLocales)) {
return $this->availableLocales;
}

$localeData = file_get_contents(\OC::$SERVERROOT . '/resources/locales.json');
$this->availableLocales = \json_decode($localeData, true);

return $this->availableLocales;
}

/**
* @param string|null $app App id or null for core
* @param string $lang
Expand All @@ -250,6 +321,23 @@ public function languageExists($app, $lang) {
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
Expand Down
Loading

0 comments on commit 61842f6

Please sign in to comment.