From ab0dc19c9fe525920e6e3dd97ad610a644dc6e6d Mon Sep 17 00:00:00 2001 From: Jakub Vrana Date: Fri, 4 Apr 2025 17:16:18 +0200 Subject: [PATCH] Plugins: Allow formatting translations using Adminer\lang_format() --- CHANGELOG.md | 1 + adminer/include/lang.inc.php | 74 ++++++++++++++++++++---------------- compile.php | 34 ++++++----------- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdd63adc..7c163939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - MySQL: Display number of found rows in group queries (regression from 5.1.1) - non-MySQL: Parse '--' without trailing space as comment in SQL command (bug SF-842) - CSS: Add logo +- Plugins: Allow formatting translations using Adminer\lang_format() - New plugin: Set up driver, server and database in Adminer Editor ## Adminer 5.1.1 (released 2025-04-02) diff --git a/adminer/include/lang.inc.php b/adminer/include/lang.inc.php index 71b81713..0c537631 100644 --- a/adminer/include/lang.inc.php +++ b/adminer/include/lang.inc.php @@ -1,7 +1,48 @@ $translation +* @param float|string $number +*/ +function lang_format($translation, $number = null): string { + if (is_array($translation)) { + // this is matched by compile.php + $pos = ($number == 1 ? 0 + : (LANG == 'cs' || LANG == 'sk' ? ($number && $number < 5 ? 1 : 2) // different forms for 1, 2-4, other + : (LANG == 'fr' ? (!$number ? 0 : 1) // different forms for 0-1, other + : (LANG == 'pl' ? ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2) // different forms for 1, 2-4 except 12-14, other + : (LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other + : (LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other + : (LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0 + : (in_array(LANG, array('bs', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other + : 1)))))))) // different forms for 1, other + ; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html + $translation = $translation[$pos]; + } + $translation = str_replace("'", '’', $translation); // translations can contain HTML or be used in optionlist (we couldn't escape them here) but they can also be used e.g. in title='' //! escape plaintext translations + $args = func_get_args(); + array_shift($args); + $format = str_replace("%d", "%s", $translation); + if ($format != $translation) { + $args[0] = format_number($number); + } + return vsprintf($format, $args); +} + +// this is matched by compile.php +// not used in a single language version from here /** Get available languages * @return string[] @@ -56,37 +97,6 @@ function langs(): array { ); } -/** Translate string -* @param literal-string $idf -* @param float|string $number -*/ -function lang(string $idf, $number = null): string { - // this is matched by compile.php - $translation = (Lang::$translations[$idf] ?: $idf); - if (is_array($translation)) { - // this is matched by compile.php - $pos = ($number == 1 ? 0 - : (LANG == 'cs' || LANG == 'sk' ? ($number && $number < 5 ? 1 : 2) // different forms for 1, 2-4, other - : (LANG == 'fr' ? (!$number ? 0 : 1) // different forms for 0-1, other - : (LANG == 'pl' ? ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2) // different forms for 1, 2-4 except 12-14, other - : (LANG == 'sl' ? ($number % 100 == 1 ? 0 : ($number % 100 == 2 ? 1 : ($number % 100 == 3 || $number % 100 == 4 ? 2 : 3))) // different forms for 1, 2, 3-4, other - : (LANG == 'lt' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1, 12-19, other - : (LANG == 'lv' ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number ? 1 : 2)) // different forms for 1 except 11, other, 0 - : (in_array(LANG, array('bs', 'ru', 'sr', 'uk')) ? ($number % 10 == 1 && $number % 100 != 11 ? 0 : ($number % 10 > 1 && $number % 10 < 5 && $number / 10 % 10 != 1 ? 1 : 2)) // different forms for 1 except 11, 2-4 except 12-14, other - : 1)))))))) // different forms for 1, other - ; // http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html - $translation = $translation[$pos]; - } - $translation = str_replace("'", '’', $translation); // translations can contain HTML or be used in optionlist (we couldn't escape them here) but they can also be used e.g. in title='' //! escape plaintext translations - $args = func_get_args(); - array_shift($args); - $format = str_replace("%d", "%s", $translation); - if ($format != $translation) { - $args[0] = format_number($number); - } - return vsprintf($format, $args); -} - function switch_lang(): void { echo "
\n
"; echo lang('Language') . ": " . html_select("lang", langs(), LANG, "this.form.submit();"); diff --git a/compile.php b/compile.php index fc5c44da..e7a79d18 100755 --- a/compile.php +++ b/compile.php @@ -21,7 +21,7 @@ function remove_lang($match) { $idf = strtr($match[2], array("\\'" => "'", "\\\\" => "\\")); $s = (Adminer\Lang::$translations[$idf] ?: $idf); if ($match[3] == ",") { // lang() has parameters - return $match[1] . (is_array($s) ? "lang(array('" . implode("', '", array_map('add_apo_slashes', $s)) . "')," : "sprintf('" . add_apo_slashes($s) . "',"); + return $match[1] . (is_array($s) ? "lang_format(array('" . implode("', '", array_map('add_apo_slashes', $s)) . "')," : "sprintf('" . add_apo_slashes($s) . "',"); } return ($match[1] && $match[4] ? $s : "$match[1]'" . add_apo_slashes($s) . "'$match[4]"); } @@ -84,8 +84,8 @@ function put_file($match) { } } } - if (basename($match[2]) != "lang.inc.php" || !$_SESSION["lang"]) { - if (basename($match[2]) == "lang.inc.php") { + if (basename($match[2]) == "lang.inc.php") { + if (!$_SESSION["lang"]) { $return = str_replace('function lang(string $idf, $number = null): string {', 'function lang($idf, $number = null) { if (is_string($idf)) { // compiled version uses numbers, string comes from a plugin // English translation is closest to the original identifiers //! pluralized translations are not found @@ -97,27 +97,17 @@ function put_file($match) { if (!$count) { echo "lang() not found\n"; } + } else { + $return = preg_replace('~// not used in a single language version from here\n.*~s', '', $return); + $return = preg_replace_callback('~(\$pos = (.+\n).+;)~sU', function ($match) { + return "\$pos = $match[2]\t\t\t: " . (preg_match("~'$_SESSION[lang]'.* \\? (.+)\n~U", $match[1], $match2) ? $match2[1] : "1") . "\n\t\t);"; + }, $return); + $return = str_replace('Lang::$translations[$idf] ?: $idf', '$idf', $return); // lang() is used only by old plugins + $return .= "define('Adminer\\LANG', '$_SESSION[lang]');\n"; } - $tokens = token_get_all($return); // to find out the last token - return "?>\n$return" . (in_array($tokens[count($tokens) - 1][0], array(T_CLOSE_TAG, T_INLINE_HTML), true) ? "\n$return" . (in_array($tokens[count($tokens) - 1][0], array(T_CLOSE_TAG, T_INLINE_HTML), true) ? "