[TASK] Improve LocalizationUtility logic and feedback 18/54118/6
authorClaus Due <claus@namelesscoder.net>
Mon, 11 Sep 2017 14:43:19 +0000 (16:43 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Fri, 2 Mar 2018 00:02:26 +0000 (01:02 +0100)
This patch does two things to improve the translation
flow in LocalizationUtility::translate:

* Early return null on empty $key (would cause null anyway)
* Feedback message on failure to sprintf

In order to provide failure feedback for formatted
strings, vsprintf had to be replaced with sprintf and
array unrolling, since vsprintf does not return false
on errors and sprintf does. The error is returned as
translation result so even if an unexpected failure
occurs, at least a partially meaningful text is shown.

Change-Id: I568be30b701f0c374289ed44fc5b31b13f492483
Resolves: #82453
Releases: master
Reviewed-on: https://review.typo3.org/54118
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php

index 9ebcd36..1f00f33 100644 (file)
@@ -64,10 +64,14 @@ class LocalizationUtility
      * @param string[] $alternativeLanguageKeys The alternative language keys if no translation was found. If null and we are in the frontend, then the language_alt from TypoScript setup will be used
      * @return string|null The value from LOCAL_LANG or null if no translation was found.
      * @api
      * @param string[] $alternativeLanguageKeys The alternative language keys if no translation was found. If null and we are in the frontend, then the language_alt from TypoScript setup will be used
      * @return string|null The value from LOCAL_LANG or null if no translation was found.
      * @api
-     * @todo : If vsprintf gets a malformed string, it returns FALSE! Should we throw an exception there?
      */
     public static function translate($key, $extensionName = null, $arguments = null, string $languageKey = null, array $alternativeLanguageKeys = null)
     {
      */
     public static function translate($key, $extensionName = null, $arguments = null, string $languageKey = null, array $alternativeLanguageKeys = null)
     {
+        if ((string)$key === '') {
+            // Early return guard: returns null if the key was empty, because the key may be a dynamic value
+            // (from for example Fluid). Returning null allows null coalescing to a default value when that happens.
+            return null;
+        }
         $value = null;
         if (GeneralUtility::isFirstPartOfStr($key, 'LLL:')) {
             $keyParts = explode(':', $key);
         $value = null;
         if (GeneralUtility::isFirstPartOfStr($key, 'LLL:')) {
             $keyParts = explode(':', $key);
@@ -120,7 +124,10 @@ class LocalizationUtility
         }
 
         if (is_array($arguments) && $value !== null) {
         }
 
         if (is_array($arguments) && $value !== null) {
-            return vsprintf($value, $arguments);
+            // This unrolls arguments from $arguments - instead of calling vsprintf which receives arguments as an array.
+            // The reason is that only sprintf() will return an error message if the number of arguments does not match
+            // the number of placeholders in the format string. Whereas, vsprintf would silently return nothing.
+            return sprintf($value, ...$arguments) ?: sprintf('Error: could not translate key "%s" with value "%s" and %d argument(s)!', $key, $value, count($arguments));
         }
         return $value;
     }
         }
         return $value;
     }