From fffa17448fd0015cb465b7facdbd15e9997c3dca Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Tue, 21 Jul 2020 23:12:49 +0000 Subject: [PATCH] [CORE][I18n][DEFAULTS] Remove I18nHelper --- src/Core/DB/DefaultSettings.php | 10 +- src/Core/GNUsocial.php | 4 +- src/Core/I18n/I18n.php | 226 ++++++++++++++++++++++++++- src/Core/I18n/I18nHelper.php | 254 ------------------------------- src/Core/I18n/TransExtractor.php | 2 +- 5 files changed, 227 insertions(+), 269 deletions(-) delete mode 100644 src/Core/I18n/I18nHelper.php diff --git a/src/Core/DB/DefaultSettings.php b/src/Core/DB/DefaultSettings.php index 7fe7941298..1291b469da 100644 --- a/src/Core/DB/DefaultSettings.php +++ b/src/Core/DB/DefaultSettings.php @@ -33,7 +33,7 @@ namespace App\Core\DB; use function App\Core\I18n\_m; -use App\Core\I18n\I18nHelper; +use App\Core\I18n\I18n; use App\Util\Common; abstract class DefaultSettings @@ -55,7 +55,7 @@ abstract class DefaultSettings 'logo' => null, 'language' => 'en', 'detect_language' => true, - 'languages' => I18nHelper::get_all_languages(), + 'languages' => I18n::getAllLanguages(), 'email' => $_ENV['SERVER_ADMIN'] ?? $_ENV['SOCIAL_ADMIN_EMAIL'] ?? null, 'recovery_disclose' => false, // Whether to not say that we found the email in the database, when asking for recovery 'timezone' => 'UTC', @@ -126,9 +126,9 @@ abstract class DefaultSettings 'video/quicktime' => 'mov', 'video/webm' => 'webm', ], - 'file_quota' => Common::get_preferred_php_upload_limit(), - 'user_quota' => Common::size_str_to_int('200M'), - 'monthly_quota' => Common::size_str_to_int('20M'), + 'file_quota' => Common::getPreferredPhpUploadLimit(), + 'user_quota' => Common::sizeStrToInt('200M'), + 'monthly_quota' => Common::sizeStrToInt('20M'), 'uploads' => true, 'show_html' => true, // show (filtered) text/html attachments (and oEmbed HTML etc.). Doesn't affect AJAX calls. 'show_thumbs' => true, // show thumbnails in notice lists for uploaded images, and photos and videos linked remotely that provide oEmbed info diff --git a/src/Core/GNUsocial.php b/src/Core/GNUsocial.php index 1343c80484..2f4c423709 100644 --- a/src/Core/GNUsocial.php +++ b/src/Core/GNUsocial.php @@ -42,7 +42,7 @@ namespace App\Core; use App\Core\DB\DB; use App\Core\DB\DefaultSettings; -use App\Core\I18n\I18nHelper; +use App\Core\I18n\I18n; use App\Core\Queue\Queue; use App\Core\Router\Router; use Doctrine\ORM\EntityManagerInterface; @@ -93,7 +93,7 @@ class GNUsocial implements EventSubscriberInterface { Log::setLogger($this->logger); Event::setDispatcher($event_dispatcher); - I18nHelper::setTranslator($this->translator); + I18n::setTranslator($this->translator); DB::setManager($this->entity_manager); Router::setRouter($this->router); Form::setFactory($this->form_factory); diff --git a/src/Core/I18n/I18n.php b/src/Core/I18n/I18n.php index 249bf95bab..2cd810e445 100644 --- a/src/Core/I18n/I18n.php +++ b/src/Core/I18n/I18n.php @@ -34,7 +34,9 @@ namespace App\Core\I18n; +use App\Util\Formatting; use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Contracts\Translation\TranslatorInterface; // Locale category constants are usually predefined, but may not be // on some systems such as Win32. @@ -55,9 +57,219 @@ foreach ($LC_CATEGORIES as $key => $name) { abstract class I18n { - // Dummy class, because the bellow function needs to be outside of - // a class, since `rfc/use-static-function` isn't implemented, so - // we'd have to use `I18n::_m`, but the autoloader still needs a class with the same name as the file + public static ?TranslatorInterface $translator = null; + + public static function setTranslator($trans): void + { + self::$translator = $trans; + } + + /** + * Looks for which plugin we've been called from to get the gettext domain; + * if not in a plugin subdirectory, we'll use the default 'core+intl-icu'. + * + * @return string + */ + public static function _mdomain(string $path): string + { + /* + 0 => + array + 'file' => string '/var/www/mublog/plugins/FeedSub/FeedSubPlugin.php' (length=49) + 'line' => int 77 + 'function' => string '_m' (length=2) + 'args' => + array + 0 => &string 'Feeds' (length=5) + */ + static $cached; + if (!isset($cached[$path])) { + $path = Formatting::normalizePath($path); + $cached[$path] = Formatting::pluginFromPath($path); + } + return $cached[$path] ?? 'core+intl-icu'; + } + + /** + * Content negotiation for language codes + * + * @param string $http_accept_lang_header HTTP Accept-Language header + * + * @return string language code for best language match, false otherwise + */ + public static function clientPreferredLanguage(string $http_accept_lang_header): string + { + $client_langs = []; + $all_languages = Common::config('site', 'languages'); + + preg_match_all('"(((\S\S)-?(\S\S)?)(;q=([0-9.]+))?)\s*(,\s*|$)"', + strtolower($http_accept_lang_header), $http_langs); + + for ($i = 0; $i < count($http_langs); ++$i) { + if (!empty($http_langs[2][$i])) { + // if no q default to 1.0 + $client_langs[$http_langs[2][$i]] = ($http_langs[6][$i] ? (float) $http_langs[6][$i] : 1.0 - ($i * 0.01)); + } + if (!empty($http_langs[3][$i]) && empty($client_langs[$http_langs[3][$i]])) { + // if a catchall default 0.01 lower + $client_langs[$http_langs[3][$i]] = ($http_langs[6][$i] ? (float) $http_langs[6][$i] - 0.01 : 0.99); + } + } + // sort in descending q + arsort($client_langs); + + foreach ($client_langs as $lang => $q) { + if (isset($all_languages[$lang])) { + return $all_languages[$lang]['lang']; + } + } + return false; + } + + /** + * returns a simple code -> name mapping for languages + * + * @return array map of available languages by code to language name. + */ + public static function getNiceLanguageList(): array + { + $nice_lang = []; + $all_languages = Common::config('site', 'languages'); + + foreach ($all_languages as $lang) { + $nice_lang[$lang['lang']] = $lang['name']; + } + return $nice_lang; + } + + /** + * Check whether a language is right-to-left + * + * @param string $lang_value language code of the language to check + * + * @return bool true if language is rtl + */ + public static function isRtl(string $lang_value): bool + { + foreach (Common::config('site', 'languages') as $code => $info) { + if ($lang_value == $info['lang']) { + return $info['direction'] == 'rtl'; + } + } + throw new InvalidArgumentException('is_rtl function received an invalid lang to test. Lang was: ' . $lang_value); + } + + /** + * Get a list of all languages that are enabled in the default config + * + * @return array mapping of language codes to language info + */ + public static function getAllLanguages(): array + { + return [ + 'af' => ['q' => 0.8, 'lang' => 'af', 'name' => 'Afrikaans', 'direction' => 'ltr'], + 'ar' => ['q' => 0.8, 'lang' => 'ar', 'name' => 'Arabic', 'direction' => 'rtl'], + 'ast' => ['q' => 1, 'lang' => 'ast', 'name' => 'Asturian', 'direction' => 'ltr'], + 'eu' => ['q' => 1, 'lang' => 'eu', 'name' => 'Basque', 'direction' => 'ltr'], + 'be-tarask' => ['q' => 0.5, 'lang' => 'be-tarask', 'name' => 'Belarusian (Taraškievica orthography)', 'direction' => 'ltr'], + 'br' => ['q' => 0.8, 'lang' => 'br', 'name' => 'Breton', 'direction' => 'ltr'], + 'bg' => ['q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'], + 'my' => ['q' => 1, 'lang' => 'my', 'name' => 'Burmese', 'direction' => 'ltr'], + 'ca' => ['q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'], + 'zh-cn' => ['q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'], + 'zh-hant' => ['q' => 0.2, 'lang' => 'zh_TW', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'], + 'ksh' => ['q' => 1, 'lang' => 'ksh', 'name' => 'Colognian', 'direction' => 'ltr'], + 'cs' => ['q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'], + 'da' => ['q' => 0.8, 'lang' => 'da', 'name' => 'Danish', 'direction' => 'ltr'], + 'nl' => ['q' => 0.5, 'lang' => 'nl', 'name' => 'Dutch', 'direction' => 'ltr'], + 'arz' => ['q' => 0.8, 'lang' => 'arz', 'name' => 'Egyptian Spoken Arabic', 'direction' => 'rtl'], + 'en' => ['q' => 1, 'lang' => 'en', 'name' => 'English', 'direction' => 'ltr'], + 'en-us' => ['q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'], + 'en-gb' => ['q' => 1, 'lang' => 'en_GB', 'name' => 'English (UK)', 'direction' => 'ltr'], + 'eo' => ['q' => 0.8, 'lang' => 'eo', 'name' => 'Esperanto', 'direction' => 'ltr'], + 'fi' => ['q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'], + 'fr' => ['q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'], + 'fr-fr' => ['q' => 1, 'lang' => 'fr', 'name' => 'French (France)', 'direction' => 'ltr'], + 'fur' => ['q' => 0.8, 'lang' => 'fur', 'name' => 'Friulian', 'direction' => 'ltr'], + 'gl' => ['q' => 0.8, 'lang' => 'gl', 'name' => 'Galician', 'direction' => 'ltr'], + 'ka' => ['q' => 0.8, 'lang' => 'ka', 'name' => 'Georgian', 'direction' => 'ltr'], + 'de' => ['q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'], + 'el' => ['q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'], + 'he' => ['q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'], + 'hu' => ['q' => 0.8, 'lang' => 'hu', 'name' => 'Hungarian', 'direction' => 'ltr'], + 'is' => ['q' => 0.1, 'lang' => 'is', 'name' => 'Icelandic', 'direction' => 'ltr'], + 'id' => ['q' => 1, 'lang' => 'id', 'name' => 'Indonesian', 'direction' => 'ltr'], + 'ia' => ['q' => 0.8, 'lang' => 'ia', 'name' => 'Interlingua', 'direction' => 'ltr'], + 'ga' => ['q' => 0.5, 'lang' => 'ga', 'name' => 'Irish', 'direction' => 'ltr'], + 'it' => ['q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'], + 'ja' => ['q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'], + 'ko' => ['q' => 0.9, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'], + 'lv' => ['q' => 1, 'lang' => 'lv', 'name' => 'Latvian', 'direction' => 'ltr'], + 'lt' => ['q' => 1, 'lang' => 'lt', 'name' => 'Lithuanian', 'direction' => 'ltr'], + 'lb' => ['q' => 1, 'lang' => 'lb', 'name' => 'Luxembourgish', 'direction' => 'ltr'], + 'mk' => ['q' => 0.5, 'lang' => 'mk', 'name' => 'Macedonian', 'direction' => 'ltr'], + 'mg' => ['q' => 1, 'lang' => 'mg', 'name' => 'Malagasy', 'direction' => 'ltr'], + 'ms' => ['q' => 1, 'lang' => 'ms', 'name' => 'Malay', 'direction' => 'ltr'], + 'ml' => ['q' => 0.5, 'lang' => 'ml', 'name' => 'Malayalam', 'direction' => 'ltr'], + 'ne' => ['q' => 1, 'lang' => 'ne', 'name' => 'Nepali', 'direction' => 'ltr'], + 'nb' => ['q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'], + 'no' => ['q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'], + 'nn' => ['q' => 1, 'lang' => 'nn', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'], + 'fa' => ['q' => 1, 'lang' => 'fa', 'name' => 'Persian', 'direction' => 'rtl'], + 'pl' => ['q' => 0.5, 'lang' => 'pl', 'name' => 'Polish', 'direction' => 'ltr'], + 'pt' => ['q' => 1, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'], + 'pt-br' => ['q' => 0.9, 'lang' => 'pt_BR', 'name' => 'Brazilian Portuguese', 'direction' => 'ltr'], + 'ru' => ['q' => 0.9, 'lang' => 'ru', 'name' => 'Russian', 'direction' => 'ltr'], + 'sr-ec' => ['q' => 1, 'lang' => 'sr-ec', 'name' => 'Serbian', 'direction' => 'ltr'], + 'es' => ['q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'], + 'sv' => ['q' => 0.8, 'lang' => 'sv', 'name' => 'Swedish', 'direction' => 'ltr'], + 'tl' => ['q' => 0.8, 'lang' => 'tl', 'name' => 'Tagalog', 'direction' => 'ltr'], + 'ta' => ['q' => 1, 'lang' => 'ta', 'name' => 'Tamil', 'direction' => 'ltr'], + 'te' => ['q' => 0.3, 'lang' => 'te', 'name' => 'Telugu', 'direction' => 'ltr'], + 'tr' => ['q' => 0.5, 'lang' => 'tr', 'name' => 'Turkish', 'direction' => 'ltr'], + 'uk' => ['q' => 1, 'lang' => 'uk', 'name' => 'Ukrainian', 'direction' => 'ltr'], + 'hsb' => ['q' => 0.8, 'lang' => 'hsb', 'name' => 'Upper Sorbian', 'direction' => 'ltr'], + 'ur' => ['q' => 1, 'lang' => 'ur_PK', 'name' => 'Urdu (Pakistan)', 'direction' => 'rtl'], + 'vi' => ['q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'], + ]; + } + + public static function formatICU(array $messages, array $params): string + { + $res = ''; + foreach (array_slice($params, 0, 1, true) as $var => $type) { + if (is_int($type)) { + $pref = '='; + $op = 'plural'; + } elseif (is_string($type)) { + $pref = ''; + $op = 'select'; + } else { + throw new Exception('Invalid variable type. (int|string) only'); + } + + $res = "{$var}, {$op}, "; + $i = 0; + $cnt = count($messages) - 1; + foreach ($messages as $val => $m) { + if ($i !== $cnt) { + $res .= "{$pref}{$val}"; + } else { + $res .= 'other'; + } + + if (is_array($m)) { + $res .= ' {' . self::formatICU($m, array_slice($params, 1, null, true)) . '} '; + } elseif (is_string($m)) { + $res .= " {{$m}} "; + } else { + throw new Exception('Invalid message array'); + } + ++$i; + } + } + return "{{$res}}"; + } } /** @@ -83,12 +295,12 @@ abstract class I18n */ function _m(): string { - $domain = I18nHelper::_mdomain(debug_backtrace()[0]['file']); + $domain = I18n::_mdomain(debug_backtrace()[0]['file']); $args = func_get_args(); switch (count($args)) { case 1: // Empty parameters, simple message - return I18nHelper::$translator->trans($args[0], [], $domain); + return I18n::$translator->trans($args[0], [], $domain); case 3: if (is_int($args[2])) { throw new Exception('Calling `_m()` with an explicit number is deprecated, ' . @@ -108,13 +320,13 @@ function _m(): string } if (is_array($args[0])) { - $args[0] = I18nHelper::formatICU($args[0], $args[1]); + $args[0] = I18n::formatICU($args[0], $args[1]); } if (is_string($args[0])) { $msg = $args[0]; $params = $args[1] ?? []; - return I18nHelper::$translator->trans($context ?? '' . $msg, $params, $domain); + return I18n::$translator->trans($context ?? '' . $msg, $params, $domain); } // Fallthrough // no break diff --git a/src/Core/I18n/I18nHelper.php b/src/Core/I18n/I18nHelper.php deleted file mode 100644 index 284bf2ea53..0000000000 --- a/src/Core/I18n/I18nHelper.php +++ /dev/null @@ -1,254 +0,0 @@ -. -// }}} - -/** - * Dynamic router loader and URLMapper interface atop Symfony's router - * - * Converts a path into a set of parameters, and vice versa - * - * @package GNUsocial - * @category URL - * - * @author Hugo Sales - * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org - * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - */ - -namespace App\Core\I18n; - -use App\Util\Formatting; -use InvalidArgumentException; -use Symfony\Contracts\Translation\TranslatorInterface; - -abstract class I18nHelper -{ - public static ?TranslatorInterface $translator = null; - - public static function setTranslator($trans): void - { - self::$translator = $trans; - } - - /** - * Looks for which plugin we've been called from to get the gettext domain; - * if not in a plugin subdirectory, we'll use the default 'core+intl-icu'. - * - * @return string - */ - public static function _mdomain(string $path): string - { - /* - 0 => - array - 'file' => string '/var/www/mublog/plugins/FeedSub/FeedSubPlugin.php' (length=49) - 'line' => int 77 - 'function' => string '_m' (length=2) - 'args' => - array - 0 => &string 'Feeds' (length=5) - */ - static $cached; - if (!isset($cached[$path])) { - $path = Formatting::normalizePath($path); - $cached[$path] = Formatting::pluginFromPath($path); - } - return $cached[$path] ?? 'core+intl-icu'; - } - - /** - * Content negotiation for language codes - * - * @param string $http_accept_lang_header HTTP Accept-Language header - * - * @return string language code for best language match, false otherwise - */ - public static function client_preferred_language(string $http_accept_lang_header): string - { - $client_langs = []; - $all_languages = Common::config('site', 'languages'); - - preg_match_all('"(((\S\S)-?(\S\S)?)(;q=([0-9.]+))?)\s*(,\s*|$)"', - strtolower($http_accept_lang_header), $http_langs); - - for ($i = 0; $i < count($http_langs); ++$i) { - if (!empty($http_langs[2][$i])) { - // if no q default to 1.0 - $client_langs[$http_langs[2][$i]] = ($http_langs[6][$i] ? (float) $http_langs[6][$i] : 1.0 - ($i * 0.01)); - } - if (!empty($http_langs[3][$i]) && empty($client_langs[$http_langs[3][$i]])) { - // if a catchall default 0.01 lower - $client_langs[$http_langs[3][$i]] = ($http_langs[6][$i] ? (float) $http_langs[6][$i] - 0.01 : 0.99); - } - } - // sort in descending q - arsort($client_langs); - - foreach ($client_langs as $lang => $q) { - if (isset($all_languages[$lang])) { - return $all_languages[$lang]['lang']; - } - } - return false; - } - - /** - * returns a simple code -> name mapping for languages - * - * @return array map of available languages by code to language name. - */ - public static function get_nice_language_list(): array - { - $nice_lang = []; - $all_languages = Common::config('site', 'languages'); - - foreach ($all_languages as $lang) { - $nice_lang[$lang['lang']] = $lang['name']; - } - return $nice_lang; - } - - /** - * Check whether a language is right-to-left - * - * @param string $lang_value language code of the language to check - * - * @return bool true if language is rtl - */ - public static function is_rtl(string $lang_value): bool - { - foreach (Common::config('site', 'languages') as $code => $info) { - if ($lang_value == $info['lang']) { - return $info['direction'] == 'rtl'; - } - } - throw new InvalidArgumentException('is_rtl function received an invalid lang to test. Lang was: ' . $lang_value); - } - - /** - * Get a list of all languages that are enabled in the default config - * - * @return array mapping of language codes to language info - */ - public static function get_all_languages(): array - { - return [ - 'af' => ['q' => 0.8, 'lang' => 'af', 'name' => 'Afrikaans', 'direction' => 'ltr'], - 'ar' => ['q' => 0.8, 'lang' => 'ar', 'name' => 'Arabic', 'direction' => 'rtl'], - 'ast' => ['q' => 1, 'lang' => 'ast', 'name' => 'Asturian', 'direction' => 'ltr'], - 'eu' => ['q' => 1, 'lang' => 'eu', 'name' => 'Basque', 'direction' => 'ltr'], - 'be-tarask' => ['q' => 0.5, 'lang' => 'be-tarask', 'name' => 'Belarusian (Taraškievica orthography)', 'direction' => 'ltr'], - 'br' => ['q' => 0.8, 'lang' => 'br', 'name' => 'Breton', 'direction' => 'ltr'], - 'bg' => ['q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'], - 'my' => ['q' => 1, 'lang' => 'my', 'name' => 'Burmese', 'direction' => 'ltr'], - 'ca' => ['q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'], - 'zh-cn' => ['q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'], - 'zh-hant' => ['q' => 0.2, 'lang' => 'zh_TW', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'], - 'ksh' => ['q' => 1, 'lang' => 'ksh', 'name' => 'Colognian', 'direction' => 'ltr'], - 'cs' => ['q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'], - 'da' => ['q' => 0.8, 'lang' => 'da', 'name' => 'Danish', 'direction' => 'ltr'], - 'nl' => ['q' => 0.5, 'lang' => 'nl', 'name' => 'Dutch', 'direction' => 'ltr'], - 'arz' => ['q' => 0.8, 'lang' => 'arz', 'name' => 'Egyptian Spoken Arabic', 'direction' => 'rtl'], - 'en' => ['q' => 1, 'lang' => 'en', 'name' => 'English', 'direction' => 'ltr'], - 'en-us' => ['q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'], - 'en-gb' => ['q' => 1, 'lang' => 'en_GB', 'name' => 'English (UK)', 'direction' => 'ltr'], - 'eo' => ['q' => 0.8, 'lang' => 'eo', 'name' => 'Esperanto', 'direction' => 'ltr'], - 'fi' => ['q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'], - 'fr' => ['q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'], - 'fr-fr' => ['q' => 1, 'lang' => 'fr', 'name' => 'French (France)', 'direction' => 'ltr'], - 'fur' => ['q' => 0.8, 'lang' => 'fur', 'name' => 'Friulian', 'direction' => 'ltr'], - 'gl' => ['q' => 0.8, 'lang' => 'gl', 'name' => 'Galician', 'direction' => 'ltr'], - 'ka' => ['q' => 0.8, 'lang' => 'ka', 'name' => 'Georgian', 'direction' => 'ltr'], - 'de' => ['q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'], - 'el' => ['q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'], - 'he' => ['q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'], - 'hu' => ['q' => 0.8, 'lang' => 'hu', 'name' => 'Hungarian', 'direction' => 'ltr'], - 'is' => ['q' => 0.1, 'lang' => 'is', 'name' => 'Icelandic', 'direction' => 'ltr'], - 'id' => ['q' => 1, 'lang' => 'id', 'name' => 'Indonesian', 'direction' => 'ltr'], - 'ia' => ['q' => 0.8, 'lang' => 'ia', 'name' => 'Interlingua', 'direction' => 'ltr'], - 'ga' => ['q' => 0.5, 'lang' => 'ga', 'name' => 'Irish', 'direction' => 'ltr'], - 'it' => ['q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'], - 'ja' => ['q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'], - 'ko' => ['q' => 0.9, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'], - 'lv' => ['q' => 1, 'lang' => 'lv', 'name' => 'Latvian', 'direction' => 'ltr'], - 'lt' => ['q' => 1, 'lang' => 'lt', 'name' => 'Lithuanian', 'direction' => 'ltr'], - 'lb' => ['q' => 1, 'lang' => 'lb', 'name' => 'Luxembourgish', 'direction' => 'ltr'], - 'mk' => ['q' => 0.5, 'lang' => 'mk', 'name' => 'Macedonian', 'direction' => 'ltr'], - 'mg' => ['q' => 1, 'lang' => 'mg', 'name' => 'Malagasy', 'direction' => 'ltr'], - 'ms' => ['q' => 1, 'lang' => 'ms', 'name' => 'Malay', 'direction' => 'ltr'], - 'ml' => ['q' => 0.5, 'lang' => 'ml', 'name' => 'Malayalam', 'direction' => 'ltr'], - 'ne' => ['q' => 1, 'lang' => 'ne', 'name' => 'Nepali', 'direction' => 'ltr'], - 'nb' => ['q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'], - 'no' => ['q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'], - 'nn' => ['q' => 1, 'lang' => 'nn', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'], - 'fa' => ['q' => 1, 'lang' => 'fa', 'name' => 'Persian', 'direction' => 'rtl'], - 'pl' => ['q' => 0.5, 'lang' => 'pl', 'name' => 'Polish', 'direction' => 'ltr'], - 'pt' => ['q' => 1, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'], - 'pt-br' => ['q' => 0.9, 'lang' => 'pt_BR', 'name' => 'Brazilian Portuguese', 'direction' => 'ltr'], - 'ru' => ['q' => 0.9, 'lang' => 'ru', 'name' => 'Russian', 'direction' => 'ltr'], - 'sr-ec' => ['q' => 1, 'lang' => 'sr-ec', 'name' => 'Serbian', 'direction' => 'ltr'], - 'es' => ['q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'], - 'sv' => ['q' => 0.8, 'lang' => 'sv', 'name' => 'Swedish', 'direction' => 'ltr'], - 'tl' => ['q' => 0.8, 'lang' => 'tl', 'name' => 'Tagalog', 'direction' => 'ltr'], - 'ta' => ['q' => 1, 'lang' => 'ta', 'name' => 'Tamil', 'direction' => 'ltr'], - 'te' => ['q' => 0.3, 'lang' => 'te', 'name' => 'Telugu', 'direction' => 'ltr'], - 'tr' => ['q' => 0.5, 'lang' => 'tr', 'name' => 'Turkish', 'direction' => 'ltr'], - 'uk' => ['q' => 1, 'lang' => 'uk', 'name' => 'Ukrainian', 'direction' => 'ltr'], - 'hsb' => ['q' => 0.8, 'lang' => 'hsb', 'name' => 'Upper Sorbian', 'direction' => 'ltr'], - 'ur' => ['q' => 1, 'lang' => 'ur_PK', 'name' => 'Urdu (Pakistan)', 'direction' => 'rtl'], - 'vi' => ['q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'], - ]; - } - - public static function formatICU(array $messages, array $params): string - { - $res = ''; - foreach (array_slice($params, 0, 1, true) as $var => $type) { - if (is_int($type)) { - $pref = '='; - $op = 'plural'; - } elseif (is_string($type)) { - $pref = ''; - $op = 'select'; - } else { - throw new Exception('Invalid variable type. (int|string) only'); - } - - $res = "{$var}, {$op}, "; - $i = 0; - $cnt = count($messages) - 1; - foreach ($messages as $val => $m) { - if ($i !== $cnt) { - $res .= "{$pref}{$val}"; - } else { - $res .= 'other'; - } - - if (is_array($m)) { - $res .= ' {' . self::formatICU($m, array_slice($params, 1, null, true)) . '} '; - } elseif (is_string($m)) { - $res .= " {{$m}} "; - } else { - throw new Exception('Invalid message array'); - } - ++$i; - } - } - return "{{$res}}"; - } -} diff --git a/src/Core/I18n/TransExtractor.php b/src/Core/I18n/TransExtractor.php index b0097ee8e0..08e70c3e4f 100644 --- a/src/Core/I18n/TransExtractor.php +++ b/src/Core/I18n/TransExtractor.php @@ -245,7 +245,7 @@ class TransExtractor extends AbstractFileExtractor implements ExtractorInterface for ($key = 0; $key < $tokenIterator->count(); ++$key) { foreach ($this->sequences as $sequence) { $message = ''; - $domain = I18nHelper::_mdomain($filename); + $domain = I18n::_mdomain($filename); $tokenIterator->seek($key); foreach ($sequence as $sequenceKey => $item) {