diff --git a/bin/worker.php b/bin/worker.php index 3d2fc3fa9f..d5cd1f6b4b 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -7,7 +7,6 @@ use Friendica\App; use Friendica\Core\Config; use Friendica\Core\Worker; -use Friendica\Core\L10n; // Get options $shortopts = 'sn'; @@ -30,9 +29,6 @@ require_once "boot.php"; $a = new App(dirname(__DIR__)); -$lang = L10n::getBrowserLanguage(); -L10n::loadTranslationTable($lang); - // Check the database structure and possibly fixes it check_db(true); diff --git a/mod/register.php b/mod/register.php index 41d39f4158..7ee0f0d8fd 100644 --- a/mod/register.php +++ b/mod/register.php @@ -63,7 +63,7 @@ function register_post(App $a) $arr['blocked'] = $blocked; $arr['verified'] = $verified; - $arr['language'] = L10n::getBrowserLanguage(); + $arr['language'] = L10n::detectLanguage(); try { $result = Model\User::create($arr); diff --git a/src/App.php b/src/App.php index bde99bfd28..5a29d55ed4 100644 --- a/src/App.php +++ b/src/App.php @@ -50,7 +50,6 @@ class App public $argv; public $argc; public $module; - public $strings; public $hooks = []; public $timezone; public $interactive = true; @@ -370,6 +369,8 @@ class App $this->loadDefaultTimezone(); + Core\L10n::init(); + $this->page = [ 'aside' => '', 'bottom' => '', @@ -1663,40 +1664,18 @@ class App Core\Addon::callHooks('init_1'); } - $lang = Core\L10n::getBrowserLanguage(); - - Core\L10n::loadTranslationTable($lang); - - // Exclude the backend processes from the session management if (!$this->isBackend()) { $stamp1 = microtime(true); session_start(); - $this->saveTimestamp($stamp1, "parser"); + $this->saveTimestamp($stamp1, 'parser'); + Core\L10n::setSessionVariable(); + Core\L10n::setLangFromSession(); } else { $_SESSION = []; Core\Worker::executeIfIdle(); } - /* Language was set earlier, but we can over-ride it in the session. - * We have to do it here because the session was just now opened. - */ - if (!empty($_SESSION['authenticated']) && empty($_SESSION['language'])) { - $_SESSION['language'] = $lang; - // we haven't loaded user data yet, but we need user language - if (!empty($_SESSION['uid'])) { - $user = DBA::selectFirst('user', ['language'], ['uid' => $_SESSION['uid']]); - if (DBA::isResult($user)) { - $_SESSION['language'] = $user['language']; - } - } - } - - if (!empty($_SESSION['language']) && $_SESSION['language'] !== $lang) { - $lang = $_SESSION['language']; - Core\L10n::loadTranslationTable($lang); - } - // ZRL if (!empty($_GET['zrl']) && $this->getMode()->isNormal()) { $this->query_string = Model\Profile::stripZrls($this->query_string); diff --git a/src/Core/Console/Maintenance.php b/src/Core/Console/Maintenance.php index 212edabcc1..c8214b1614 100644 --- a/src/Core/Console/Maintenance.php +++ b/src/Core/Console/Maintenance.php @@ -68,11 +68,6 @@ HELP; throw new \RuntimeException('Database isn\'t ready or populated yet'); } - Core\Config::load(); - - $lang = Core\L10n::getBrowserLanguage(); - Core\L10n::loadTranslationTable($lang); - $enabled = intval($this->getArgument(0)); Core\Config::set('system', 'maintenance', $enabled); diff --git a/src/Core/L10n.php b/src/Core/L10n.php index 542590646d..05f710cd76 100644 --- a/src/Core/L10n.php +++ b/src/Core/L10n.php @@ -12,19 +12,90 @@ require_once 'boot.php'; require_once 'include/dba.php'; /** - * Provide Languange, Translation, and Localisation functions to the application - * Localisation can be referred to by the numeronym L10N (as in: "L", followed by ten more letters, and then "N"). + * Provide Language, Translation, and Localization functions to the application + * Localization can be referred to by the numeronym L10N (as in: "L", followed by ten more letters, and then "N"). */ class L10n extends BaseObject { /** - * @brief get the prefered language from the HTTP_ACCEPT_LANGUAGE header + * A string indicating the current language used for translation: + * - Two-letter ISO 639-1 code. + * - Two-letter ISO 639-1 code + dash + Two-letter ISO 3166-1 alpha-2 country code. + * @var string */ - public static function getBrowserLanguage() + private $lang = ''; + /** + * A language code saved for later after pushLang() has been called. + * + * @var string + */ + private $langSave = ''; + + /** + * An array of translation strings whose key is the neutral english message. + * + * @var array + */ + private $strings = []; + /** + * An array of translation strings saved for later after pushLang() has been called. + * + * @var array + */ + private $stringsSave = []; + + /** + * Detects the language and sets the translation table + */ + public static function init() + { + $lang = self::detectLanguage(); + self::loadTranslationTable($lang); + } + + /** + * Returns the current language code + * + * @return string Language code + */ + public static function getCurrentLang() + { + return self::$lang; + } + + /** + * Sets the language session variable + */ + public static function setSessionVariable() + { + if (!empty($_SESSION['authenticated']) && empty($_SESSION['language'])) { + $_SESSION['language'] = self::$lang; + // we haven't loaded user data yet, but we need user language + if (!empty($_SESSION['uid'])) { + $user = DBA::selectFirst('user', ['language'], ['uid' => $_SESSION['uid']]); + if (DBA::isResult($user)) { + $_SESSION['language'] = $user['language']; + } + } + } + } + + public static function setLangFromSession() + { + if (!empty($_SESSION['language']) && $_SESSION['language'] !== self::$lang) { + self::loadTranslationTable($_SESSION['language']); + } + } + + /** + * @brief Returns the preferred language from the HTTP_ACCEPT_LANGUAGE header + * @return string The two-letter language code + */ + public static function detectLanguage() { $lang_list = []; - if (x($_SERVER, 'HTTP_ACCEPT_LANGUAGE')) { + if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { // break up string into pieces (languages and q factors) preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse); @@ -59,58 +130,69 @@ class L10n extends BaseObject } /** - * @param string $language language + * This function should be called before formatting messages in a specific target language + * different from the current user/system language. + * + * It saves the current translation strings in a separate variable and loads new translations strings. + * + * If called repeatedly, it won't save the translation strings again, just load the new ones. + * + * @see popLang() + * @brief Stores the current language strings and load a different language. + * @param string $lang Language code */ - public static function pushLang($language) + public static function pushLang($lang) { - $a = self::getApp(); + if (!self::$lang) { + self::init(); + } - $a->langsave = Config::get('system', 'language'); - - if ($language === $a->langsave) { + if ($lang === self::$lang) { return; } - if (isset($a->strings) && count($a->strings)) { - $a->stringsave = $a->strings; + if (!self::$langSave) { + self::$langSave = self::$lang; + self::$stringsSave = self::$strings; } - $a->strings = []; - self::loadTranslationTable($language); - Config::set('system', 'language', $language); + + self::loadTranslationTable($lang); } /** - * Pop language off the top of the stack + * Restores the original user/system language after having used pushLang() */ public static function popLang() { - $a = self::getApp(); - - if (Config::get('system', 'language') === $a->langsave) { + if (!self::$langSave) { return; } - if (isset($a->stringsave)) { - $a->strings = $a->stringsave; - } else { - $a->strings = []; - } + self::$strings = self::$stringsSave; + self::$lang = self::$langSave; - Config::set('system', 'language', $a->langsave); + self::$stringsSave = []; + self::$langSave = ''; } /** - * load string translation table for alternate language + * Loads string translation table * - * first addon strings are loaded, then globals + * First addon strings are loaded, then globals + * + * Uses an App object shim since all the strings files refer to $a->strings * * @param string $lang language code to load */ - public static function loadTranslationTable($lang) + private static function loadTranslationTable($lang) { - $a = self::getApp(); + if ($lang === self::$lang) { + return; + } + $a = new \stdClass(); $a->strings = []; + // load enabled addons strings $addons = DBA::select('addon', ['name'], ['installed' => true]); while ($p = DBA::fetch($addons)) { @@ -123,6 +205,11 @@ class L10n extends BaseObject if (file_exists("view/lang/$lang/strings.php")) { include "view/lang/$lang/strings.php"; } + + self::$lang = $lang; + self::$strings = $a->strings; + + unset($a); } /** @@ -143,14 +230,16 @@ class L10n extends BaseObject */ public static function t($s, ...$vars) { - $a = self::getApp(); - if (empty($s)) { return ''; } - if (x($a->strings, $s)) { - $t = $a->strings[$s]; + if (!self::$lang) { + self::init(); + } + + if (!empty(self::$strings[$s])) { + $t = self::$strings[$s]; $s = is_array($t) ? $t[0] : $t; } @@ -181,18 +270,18 @@ class L10n extends BaseObject */ public static function tt($singular, $plural, $count) { - $a = self::getApp(); - if (!is_numeric($count)) { logger('Non numeric count called by ' . System::callstack(20)); } - $lang = Config::get('system', 'language'); + if (!self::$lang) { + self::init(); + } - if (!empty($a->strings[$singular])) { - $t = $a->strings[$singular]; + if (!empty(self::$strings[$singular])) { + $t = self::$strings[$singular]; if (is_array($t)) { - $plural_function = 'string_plural_select_' . str_replace('-', '_', $lang); + $plural_function = 'string_plural_select_' . str_replace('-', '_', self::$lang); if (function_exists($plural_function)) { $i = $plural_function($count); } else { @@ -227,8 +316,6 @@ class L10n extends BaseObject return $n != 1; } - - /** * @brief Return installed languages codes as associative array * diff --git a/src/Util/Temporal.php b/src/Util/Temporal.php index 696721e458..0cc759da14 100644 --- a/src/Util/Temporal.php +++ b/src/Util/Temporal.php @@ -217,13 +217,13 @@ class Temporal // First day of the week (0 = Sunday) $firstDay = PConfig::get(local_user(), 'system', 'first_day_of_week', 0); - $lang = substr(L10n::getBrowserLanguage(), 0, 2); + $lang = substr(L10n::getCurrentLang(), 0, 2); // Check if the detected language is supported by the picker if (!in_array($lang, ["ar", "ro", "id", "bg", "fa", "ru", "uk", "en", "el", "de", "nl", "tr", "fr", "es", "th", "pl", "pt", "ch", "se", "kr", "it", "da", "no", "ja", "vi", "sl", "cs", "hu"])) { - $lang = Config::get('system', 'language', 'en'); + $lang = 'en'; } $o = '';