X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FCore%2FL10n.php;h=f7ed9918ce7bd8feb139774ab2676074aed1aff9;hb=ef0545645a3054a6433340e7ac170c1933e670cc;hp=2389817021b2a5bc26e9e511f94ec8a4df7f1ef5;hpb=2fa6cc000013089d59d9cc221b544ed1a7a4cd37;p=friendica.git diff --git a/src/Core/L10n.php b/src/Core/L10n.php index 2389817021..f7ed9918ce 100644 --- a/src/Core/L10n.php +++ b/src/Core/L10n.php @@ -4,26 +4,99 @@ */ namespace Friendica\Core; -use Friendica\Core\Config; -use dba; - -require_once 'boot.php'; -require_once 'include/dba.php'; +use Friendica\BaseObject; +use Friendica\Database\DBA; /** - * 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 \Friendica\BaseObject +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 + */ + private static $lang = ''; + /** + * A language code saved for later after pushLang() has been called. + * + * @var string + */ + private static $langSave = ''; + + /** + * An array of translation strings whose key is the neutral english message. + * + * @var array + */ + private static $strings = []; + /** + * An array of translation strings saved for later after pushLang() has been called. + * + * @var array + */ + private static $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 getBrowserLanguage() + 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']; + } + } + } + + if (isset($_GET['lang'])) { + $_SESSION['language'] = $_GET['lang']; + } + } + + 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 + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + 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); @@ -42,6 +115,10 @@ class L10n extends \Friendica\BaseObject } } + if (isset($_GET['lang'])) { + $lang_list = [$_GET['lang']]; + } + // check if we have translations for the preferred languages and pick the 1st that has foreach ($lang_list as $lang) { if ($lang === 'en' || (file_exists("view/lang/$lang") && is_dir("view/lang/$lang"))) { @@ -58,61 +135,74 @@ class L10n extends \Friendica\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 + * @throws \Exception */ - public static function pushLang($language) + public static function pushLang($lang) { - $a = self::getApp(); - - $a->langsave = Config::get('system', 'language'); + if (!self::$lang) { + self::init(); + } - 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 + * @throws \Exception */ - 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)) { + $addons = DBA::select('addon', ['name'], ['installed' => true]); + while ($p = DBA::fetch($addons)) { $name = $p['name']; if (file_exists("addon/$name/lang/$lang/strings.php")) { include "addon/$name/lang/$lang/strings.php"; @@ -122,6 +212,11 @@ class L10n extends \Friendica\BaseObject if (file_exists("view/lang/$lang/strings.php")) { include "view/lang/$lang/strings.php"; } + + self::$lang = $lang; + self::$strings = $a->strings; + + unset($a); } /** @@ -142,14 +237,16 @@ class L10n extends \Friendica\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; } @@ -175,23 +272,36 @@ class L10n extends \Friendica\BaseObject * * @param string $singular * @param string $plural - * @param int $count + * @param int $count * @return string + * @throws \Exception */ public static function tt($singular, $plural, $count) { - $lang = Config::get('system', 'language'); + if (!is_numeric($count)) { + Logger::log('Non numeric count called by ' . System::callstack(20)); + } - if (!empty($a->strings[$singular])) { - $t = $a->strings[$singular]; + if (!self::$lang) { + self::init(); + } + + 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 { $i = self::stringPluralSelectDefault($count); } - $s = $t[$i]; + + // for some languages there is only a single array item + if (!isset($t[$i])) { + $s = $t[0]; + } else { + $s = $t[$i]; + } } else { $s = $t; } @@ -208,14 +318,15 @@ class L10n extends \Friendica\BaseObject /** * Provide a fallback which will not collide with a function defined in any language file + * + * @param int $n + * @return bool */ private static function stringPluralSelectDefault($n) { return $n != 1; } - - /** * @brief Return installed languages codes as associative array * @@ -244,4 +355,68 @@ class L10n extends \Friendica\BaseObject } return $langs; } + + /** + * @brief Translate days and months names. + * + * @param string $s String with day or month name. + * @return string Translated string. + */ + public static function getDay($s) + { + $ret = str_replace(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], + [self::t('Monday'), self::t('Tuesday'), self::t('Wednesday'), self::t('Thursday'), self::t('Friday'), self::t('Saturday'), self::t('Sunday')], + $s); + + $ret = str_replace(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + [self::t('January'), self::t('February'), self::t('March'), self::t('April'), self::t('May'), self::t('June'), self::t('July'), self::t('August'), self::t('September'), self::t('October'), self::t('November'), self::t('December')], + $ret); + + return $ret; + } + + /** + * @brief Translate short days and months names. + * + * @param string $s String with short day or month name. + * @return string Translated string. + */ + public static function getDayShort($s) + { + $ret = str_replace(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], + [self::t('Mon'), self::t('Tue'), self::t('Wed'), self::t('Thu'), self::t('Fri'), self::t('Sat'), self::t('Sun')], + $s); + + $ret = str_replace(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + [self::t('Jan'), self::t('Feb'), self::t('Mar'), self::t('Apr'), self::t('May'), ('Jun'), self::t('Jul'), self::t('Aug'), self::t('Sep'), self::t('Oct'), self::t('Nov'), self::t('Dec')], + $ret); + + return $ret; + } + + /** + * Load poke verbs + * + * @return array index is present tense verb + * value is array containing past tense verb, translation of present, translation of past + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @hook poke_verbs pokes array + */ + public static function getPokeVerbs() + { + // index is present tense verb + // value is array containing past tense verb, translation of present, translation of past + $arr = [ + 'poke' => ['poked', self::t('poke'), self::t('poked')], + 'ping' => ['pinged', self::t('ping'), self::t('pinged')], + 'prod' => ['prodded', self::t('prod'), self::t('prodded')], + 'slap' => ['slapped', self::t('slap'), self::t('slapped')], + 'finger' => ['fingered', self::t('finger'), self::t('fingered')], + 'rebuff' => ['rebuffed', self::t('rebuff'), self::t('rebuffed')], + ]; + + Hook::callAll('poke_verbs', $arr); + + return $arr; + } }