Skip to content

Commit

Permalink
Merge pull request #29015 from nextcloud/enhancement/l10n-factory-fin…
Browse files Browse the repository at this point in the history
…d-generic-language

Add L10n factory method for generic language heuristics
  • Loading branch information
ChristophWurst authored Oct 13, 2021
2 parents c1e7f6e + 5b727fc commit e163d19
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 149 deletions.
80 changes: 59 additions & 21 deletions lib/private/L10N/Factory.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl>
Expand Down Expand Up @@ -42,6 +45,7 @@
use OCP\IUserSession;
use OCP\L10N\IFactory;
use OCP\L10N\ILanguageIterator;
use function is_null;

/**
* A factory that generates language instances
Expand Down Expand Up @@ -157,21 +161,24 @@ public function get($app, $lang = null, $locale = null) {
/**
* Find the best language
*
* @param string|null $app App id or null for core
* @param string|null $appId App id or null for core
*
* @return string language If nothing works it returns 'en'
*/
public function findLanguage($app = null) {
public function findLanguage(?string $appId = null): string {
// Step 1: Forced language always has precedence over anything else
$forceLang = $this->config->getSystemValue('force_language', false);
if (is_string($forceLang)) {
$this->requestLanguage = $forceLang;
}

if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
// Step 2: Return cached language
if ($this->requestLanguage !== '' && $this->languageExists($appId, $this->requestLanguage)) {
return $this->requestLanguage;
}

/**
* At this point Nextcloud might not yet be installed and thus the lookup
* Step 3: At this point Nextcloud might not yet be installed and thus the lookup
* in the preferences table might fail. For this reason we need to check
* whether the instance has already been installed
*
Expand All @@ -188,30 +195,67 @@ public function findLanguage($app = null) {
$userId = null;
$userLang = null;
}

if ($userLang) {
$this->requestLanguage = $userLang;
if ($this->languageExists($app, $userLang)) {
if ($this->languageExists($appId, $userLang)) {
return $userLang;
}
}

// Step 4: Check the request headers
try {
// Try to get the language from the Request
$lang = $this->getLanguageFromRequest($app);
if ($userId !== null && $app === null && !$userLang) {
$lang = $this->getLanguageFromRequest($appId);
if ($userId !== null && $appId === null && !$userLang) {
$this->config->setUserValue($userId, 'core', 'lang', $lang);
}
return $lang;
} catch (LanguageNotFoundException $e) {
// Finding language from request failed fall back to default language
$defaultLanguage = $this->config->getSystemValue('default_language', false);
if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
if ($defaultLanguage !== false && $this->languageExists($appId, $defaultLanguage)) {
return $defaultLanguage;
}
}

// We could not find any language so fall back to english
// Step 5: fall back to English
return 'en';
}

public function findGenericLanguage(string $appId = null): string {
// Step 1: Forced language always has precedence over anything else
$forcedLanguage = $this->config->getSystemValue('force_language', false);
if ($forcedLanguage !== false) {
return $forcedLanguage;
}

// Step 2: Check if we have a default language
$defaultLanguage = $this->config->getSystemValue('default_language', false);
if ($defaultLanguage !== false && $this->languageExists($appId, $defaultLanguage)) {
return $defaultLanguage;
}

// Step 3.1: Check if Nextcloud is already installed before we try to access user info
if (!$this->config->getSystemValue('installed', false)) {
return 'en';
}
// Step 3.2: Check the current user (if any) for their preferred language
$user = $this->userSession->getUser();
if ($user !== null) {
$userLang = $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
if ($userLang !== null) {
return $userLang;
}
}

// Step 4: Check the request headers
try {
return $this->getLanguageFromRequest($appId);
} catch (LanguageNotFoundException $e) {
// Ignore and continue
}

// Step 5: fall back to English
return 'en';
}

Expand Down Expand Up @@ -280,9 +324,9 @@ public function findLanguageFromLocale(string $app = 'core', string $locale = nu
* Find all available languages for an app
*
* @param string|null $app App id or null for core
* @return array an array of available languages
* @return string[] an array of available languages
*/
public function findAvailableLanguages($app = null) {
public function findAvailableLanguages($app = null): array {
$key = $app;
if ($key === null) {
$key = 'null';
Expand Down Expand Up @@ -352,7 +396,7 @@ public function languageExists($app, $lang) {
}

$languages = $this->findAvailableLanguages($app);
return array_search($lang, $languages) !== false;
return in_array($lang, $languages);
}

public function getLanguageIterator(IUser $user = null): ILanguageIterator {
Expand Down Expand Up @@ -406,11 +450,9 @@ public function localeExists($locale) {
}

/**
* @param string|null $app
* @return string
* @throws LanguageNotFoundException
*/
private function getLanguageFromRequest($app) {
private function getLanguageFromRequest(?string $app = null): string {
$header = $this->request->getHeader('ACCEPT_LANGUAGE');
if ($header !== '') {
$available = $this->findAvailableLanguages($app);
Expand Down Expand Up @@ -444,12 +486,8 @@ private function getLanguageFromRequest($app) {
/**
* if default language is set to de_DE (formal German) this should be
* preferred to 'de' (non-formal German) if possible
*
* @param string|null $app
* @param string $lang
* @return string
*/
protected function respectDefaultLanguage($app, $lang) {
protected function respectDefaultLanguage(?string $app, string $lang): string {
$result = $lang;
$defaultLanguage = $this->config->getSystemValue('default_language', false);

Expand Down
35 changes: 30 additions & 5 deletions lib/public/L10N/IFactory.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
Expand Down Expand Up @@ -46,13 +49,35 @@ interface IFactory {
public function get($app, $lang = null, $locale = null);

/**
* Find the best language
* Find the best language for the context of the current user
*
* @param string|null $app App id or null for core
* @return string language If nothing works it returns 'en'
* This method will try to find the most specific language based on info
* from the user who is logged into the current process and will fall
* back to system settings and heuristics otherwise.
*
* @param string|null $appId specify if you only want a language a specific app supports
*
* @return string language code, defaults to 'en' if no other matches are found
* @since 9.0.0
*/
public function findLanguage($app = null);
public function findLanguage(?string $appId = null): string;

/**
* Try to find the best language for generic tasks
*
* This method will try to find the most generic language based on system
* settings, independent of the user logged into the current process. This
* is useful for tasks that are run for another user. E.g. the current user
* sends an email to someone else, then we don't want the current user's
* language to be picked but rather a instance-wide default that likely fits
* the target user
*
* @param string|null $appId specify if you only want a language a specific app supports
*
* @return string language code, defaults to 'en' if no other matches are found
* @since 23.0.0
*/
public function findGenericLanguage(string $appId = null): string;

/**
* @param string|null $lang user language as default locale
Expand All @@ -78,7 +103,7 @@ public function findLanguageFromLocale(string $app = 'core', string $locale = nu
* @return string[] an array of available languages
* @since 9.0.0
*/
public function findAvailableLanguages($app = null);
public function findAvailableLanguages($app = null): array;

/**
* @return array an array of available
Expand Down
Loading

0 comments on commit e163d19

Please sign in to comment.