diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js index f067d600e..cafbc4861 100644 --- a/assets/js/plugin-check-admin.js +++ b/assets/js/plugin-check-admin.js @@ -218,6 +218,46 @@ return false; } + /** + * Counts the total number of errors and warnings in the aggregated results. + * + * @since 1.9.0 + * + * @return {Object} Object with errorCount and warningCount properties. + */ + function countResults() { + let errorCount = 0; + let warningCount = 0; + + // Count errors. + for ( const file of Object.keys( aggregatedResults.errors ) ) { + const lines = aggregatedResults.errors[ file ] || {}; + + for ( const line of Object.keys( lines ) ) { + const columns = lines[ line ] || {}; + + for ( const column of Object.keys( columns ) ) { + errorCount += ( columns[ column ] || [] ).length; + } + } + } + + // Count warnings. + for ( const file of Object.keys( aggregatedResults.warnings ) ) { + const lines = aggregatedResults.warnings[ file ] || {}; + + for ( const line of Object.keys( lines ) ) { + const columns = lines[ line ] || {}; + + for ( const column of Object.keys( columns ) ) { + warningCount += ( columns[ column ] || [] ).length; + } + } + } + + return { errorCount, warningCount }; + } + function defaultString( key ) { if ( pluginCheck.strings && @@ -544,10 +584,91 @@ * @param {boolean} isSuccessMessage Whether the message is a success message. */ function renderResultsMessage( isSuccessMessage ) { - const messageType = isSuccessMessage ? 'success' : 'error'; - const messageText = isSuccessMessage - ? pluginCheck.successMessage - : pluginCheck.errorMessage; + // Count errors and warnings to determine notice severity and compose the message. + const { errorCount, warningCount } = isSuccessMessage + ? { errorCount: 0, warningCount: 0 } + : countResults(); + + // Derive notice type from actual counts: errors → error, warnings-only → warning, none → success. + let messageType; + if ( errorCount > 0 ) { + messageType = 'error'; + } else if ( warningCount > 0 ) { + messageType = 'warning'; + } else { + messageType = 'success'; + } + + let messageText; + + if ( isSuccessMessage ) { + messageText = pluginCheck.successMessage; + } else { + /** + * Substitutes printf-style placeholders in a translated string. + * Handles both simple (%d, %s) and positional (%1$d, %2$s) placeholders. + * + * @param {string} template The translated format string. + * @param {...string} args Replacement values. + * @return {string} Formatted string with placeholders replaced. + */ + function sprintfReplace( template, ...args ) { + let i = 0; + return template.replace( + /%(\d+\$)?[ds]/g, + function ( _match, pos ) { + const index = pos ? parseInt( pos, 10 ) - 1 : i++; + return args[ index ] !== undefined + ? args[ index ] + : _match; + } + ); + } + + // Build the individual count parts with proper plural/singular forms. + let errorPart = ''; + if ( errorCount > 0 ) { + errorPart = sprintfReplace( + errorCount === 1 + ? pluginCheck.errorString + : pluginCheck.errorsString, + errorCount + ); + } + + let warningPart = ''; + if ( warningCount > 0 ) { + warningPart = sprintfReplace( + warningCount === 1 + ? pluginCheck.warningString + : pluginCheck.warningsString, + warningCount + ); + } + + // Assemble the final sentence from fully translatable PHP-provided templates + // so that word order and connector phrases can be adapted for all languages. + if ( errorPart && warningPart ) { + messageText = sprintfReplace( + pluginCheck.summaryBothTemplate, + errorPart, + warningPart + ); + } else if ( errorPart ) { + messageText = sprintfReplace( + pluginCheck.summarySingleTemplate, + errorPart + ); + } else if ( warningPart ) { + messageText = sprintfReplace( + pluginCheck.summarySingleTemplate, + warningPart + ); + } else { + // Fallback to default message if somehow no errors/warnings. + messageText = pluginCheck.errorMessage; + } + } resultsContainer.innerHTML = renderTemplate( 'plugin-check-results-complete', { diff --git a/includes/Admin/Admin_Page.php b/includes/Admin/Admin_Page.php index 89e8d182d..05e0fdc73 100644 --- a/includes/Admin/Admin_Page.php +++ b/includes/Admin/Admin_Page.php @@ -204,6 +204,18 @@ public function enqueue_scripts() { 'actionExportResults' => Admin_AJAX::ACTION_EXPORT_RESULTS, 'successMessage' => __( 'No errors found.', 'plugin-check' ), 'errorMessage' => __( 'Errors were found.', 'plugin-check' ), + /* translators: %d: Number of errors found. */ + 'errorString' => __( '%d error', 'plugin-check' ), + /* translators: %d: Number of errors found. */ + 'errorsString' => __( '%d errors', 'plugin-check' ), + /* translators: %d: Number of warnings found. */ + 'warningString' => __( '%d warning', 'plugin-check' ), + /* translators: %d: Number of warnings found. */ + 'warningsString' => __( '%d warnings', 'plugin-check' ), + /* translators: 1: Formatted error count string (e.g. "3 errors"), 2: Formatted warning count string (e.g. "2 warnings"). */ + 'summaryBothTemplate' => __( '%1$s and %2$s found.', 'plugin-check' ), + /* translators: %s: Formatted issue count string (e.g. "3 errors" or "2 warnings"). */ + 'summarySingleTemplate' => __( '%s found.', 'plugin-check' ), 'strings' => array( 'exportCsv' => __( 'Export CSV', 'plugin-check' ), 'exportJson' => __( 'Export JSON', 'plugin-check' ),