diff --git a/features/language-core.feature b/features/language-core.feature index 4fe8e115..829e1e6e 100644 --- a/features/language-core.feature +++ b/features/language-core.feature @@ -476,3 +476,74 @@ Feature: Manage core translation files for a WordPress install | en_US | active | | nl_NL | installed | And STDERR should be empty + + @require-wp-4.0 + Scenario: Core translation update with format flag + Given a WP install + And an empty cache + + When I run `wp language core update --format=json` + Then STDOUT should be: + """ + [] + """ + And STDERR should be empty + + When I run `wp language core update --format=csv` + Then STDOUT should be empty + And STDERR should be empty + + When I run `wp language core update --format=summary` + Then STDOUT should contain: + """ + Success: Translations are up to date. + """ + And STDERR should be empty + + @require-wp-6.0 @require-php-7.2 + Scenario Outline: Core translation update with dry-run and format flag + Given an empty directory + And WP files + And a database + And I run `wp core download --version= --force` + And wp-config.php + And I run `wp core install --url='localhost:8001' --title='Test' --admin_user=wpcli --admin_email=admin@example.com --admin_password=1` + + When I run `wp language core install en_CA ja` + Then STDERR should be empty + + Given I try `wp core download --version= --force` + Then the return code should be 0 + And I run `wp core update-db` + + When I run `wp language core list --fields=language,status,update` + Then STDOUT should be a table containing rows: + | language | status | update | + | en_CA | installed | available | + | ja | installed | available | + + When I run `wp language core update --dry-run --format=json` + Then STDOUT should be JSON containing: + """ + [{"Type":"Core","Name":"WordPress","Version":""}] + """ + And STDERR should be empty + + When I run `wp language core update --dry-run --format=csv` + Then STDOUT should contain: + """ + Type,Name,Version,Language + """ + And STDOUT should contain: + """ + Core,WordPress, + """ + And STDERR should be empty + + When I run `wp language core update --dry-run --format=summary` + Then STDOUT should be empty + And STDERR should be empty + + Examples: + | original | update | + | 6.5 | 6.6 | diff --git a/features/language-plugin.feature b/features/language-plugin.feature index 7fe1f286..36eea902 100644 --- a/features/language-plugin.feature +++ b/features/language-plugin.feature @@ -446,6 +446,119 @@ Feature: Manage plugin translation files for a WordPress install And STDERR should be empty @require-wp-4.0 + Scenario: Plugin translation update with format flag + Given a WP install + + When I run `wp plugin install hello-dolly --force` + Then STDERR should be empty + + When I run `wp language plugin update hello-dolly --format=json` + Then STDOUT should be: + """ + [] + """ + And STDERR should be empty + + When I run `wp language plugin update --all --format=csv` + Then STDOUT should be empty + And STDERR should be empty + + When I run `wp language plugin update --all --format=summary` + Then STDOUT should contain: + """ + Success: Translations are up to date. + """ + And STDERR should be empty + + @require-wp-4.0 + Scenario: Plugin translation update with format flag and actual updates + Given a WP install + And an empty cache + + When I run `wp plugin install akismet --version=3.2 --force` + Then STDERR should be empty + + When I run `wp language plugin install akismet de_DE` + Then STDERR should be empty + + When I run `wp plugin install akismet --version=4.0 --force` + And I run `wp language plugin list akismet --fields=plugin,language,update,status` + Then STDOUT should be a table containing rows: + | plugin | language | update | status | + | akismet | de_DE | available | installed | + + When I run `wp language plugin update akismet --format=json` + Then STDOUT should be JSON containing: + """ + [{"slug":"akismet","language":"de_DE","status":"updated"}] + """ + And STDERR should be empty + + When I run `wp language plugin uninstall akismet de_DE` + And I run `wp plugin install akismet --version=3.2 --force` + And I run `wp language plugin install akismet de_DE` + And I run `wp plugin install akismet --version=4.0 --force` + And I run `wp language plugin update akismet --format=csv` + Then STDOUT should contain: + """ + slug,language,status + """ + And STDOUT should contain: + """ + akismet,de_DE,updated + """ + And STDERR should be empty + + When I run `wp language plugin uninstall akismet de_DE` + And I run `wp plugin install akismet --version=3.2 --force` + And I run `wp language plugin install akismet de_DE` + And I run `wp plugin install akismet --version=4.0 --force` + And I run `wp language plugin update akismet --format=summary` + Then STDOUT should contain: + """ + Success: Updated 1/1 translation. + """ + And STDERR should be empty + + @require-wp-4.0 + Scenario: Plugin translation update with dry-run and format flag + Given a WP install + And an empty cache + + When I run `wp plugin install akismet --version=3.2 --force` + Then STDERR should be empty + + When I run `wp language plugin install akismet de_DE` + Then STDERR should be empty + + When I run `wp plugin install akismet --version=4.0 --force` + And I run `wp language plugin list akismet --fields=plugin,language,update,status` + Then STDOUT should be a table containing rows: + | plugin | language | update | status | + | akismet | de_DE | available | installed | + + When I run `wp language plugin update akismet --dry-run --format=json` + Then STDOUT should be JSON containing: + """ + [{"Type":"Plugin","Name":"Akismet Anti-spam: Spam Protection"}] + """ + And STDERR should be empty + + When I run `wp language plugin update akismet --dry-run --format=csv` + Then STDOUT should contain: + """ + Type,Name,Version,Language + """ + And STDOUT should contain: + """ + Plugin,Akismet Anti-spam: Spam Protection + """ + And STDERR should be empty + + When I run `wp language plugin update akismet --dry-run --format=summary` + Then STDOUT should be empty + And STDERR should be empty + Scenario: Handle plugins with text domain different from slug Given a WP install And an empty cache diff --git a/features/language-theme.feature b/features/language-theme.feature index 67e798c2..cd038bbf 100644 --- a/features/language-theme.feature +++ b/features/language-theme.feature @@ -330,6 +330,119 @@ Feature: Manage theme translation files for a WordPress install And STDERR should be empty @require-wp-4.0 + Scenario: Theme translation update with format flag + Given a WP install + + When I try `wp theme install twentyten` + Then STDOUT should not be empty + + When I run `wp language theme update twentyten --format=json` + Then STDOUT should be: + """ + [] + """ + And STDERR should be empty + + When I run `wp language theme update --all --format=csv` + Then STDOUT should be empty + And STDERR should be empty + + When I run `wp language theme update --all --format=summary` + Then STDOUT should contain: + """ + Success: Translations are up to date. + """ + And STDERR should be empty + + @require-wp-4.0 + Scenario: Theme translation update with format flag and actual updates + Given a WP install + And an empty cache + + When I run `wp theme install twentyfifteen --version=2.0 --force` + Then STDERR should be empty + + When I run `wp language theme install twentyfifteen de_DE` + Then STDERR should be empty + + When I run `wp theme install twentyfifteen --version=2.5 --force` + And I run `wp language theme list twentyfifteen --fields=theme,language,update,status` + Then STDOUT should be a table containing rows: + | theme | language | update | status | + | twentyfifteen | de_DE | available | installed | + + When I run `wp language theme update twentyfifteen --format=json` + Then STDOUT should be JSON containing: + """ + [{"slug":"twentyfifteen","language":"de_DE","status":"updated"}] + """ + And STDERR should be empty + + When I run `wp language theme uninstall twentyfifteen de_DE` + And I run `wp theme install twentyfifteen --version=2.0 --force` + And I run `wp language theme install twentyfifteen de_DE` + And I run `wp theme install twentyfifteen --version=2.5 --force` + And I run `wp language theme update twentyfifteen --format=csv` + Then STDOUT should contain: + """ + slug,language,status + """ + And STDOUT should contain: + """ + twentyfifteen,de_DE,updated + """ + And STDERR should be empty + + When I run `wp language theme uninstall twentyfifteen de_DE` + And I run `wp theme install twentyfifteen --version=2.0 --force` + And I run `wp language theme install twentyfifteen de_DE` + And I run `wp theme install twentyfifteen --version=2.5 --force` + And I run `wp language theme update twentyfifteen --format=summary` + Then STDOUT should contain: + """ + Success: Updated 1/1 translation. + """ + And STDERR should be empty + + @require-wp-4.0 + Scenario: Theme translation update with dry-run and format flag + Given a WP install + And an empty cache + + When I run `wp theme install twentyfifteen --version=2.0 --force` + Then STDERR should be empty + + When I run `wp language theme install twentyfifteen de_DE` + Then STDERR should be empty + + When I run `wp theme install twentyfifteen --version=2.5 --force` + And I run `wp language theme list twentyfifteen --fields=theme,language,update,status` + Then STDOUT should be a table containing rows: + | theme | language | update | status | + | twentyfifteen | de_DE | available | installed | + + When I run `wp language theme update twentyfifteen --dry-run --format=json` + Then STDOUT should be JSON containing: + """ + [{"Type":"Theme","Name":"Twenty Fifteen"}] + """ + And STDERR should be empty + + When I run `wp language theme update twentyfifteen --dry-run --format=csv` + Then STDOUT should contain: + """ + Type,Name,Version,Language + """ + And STDOUT should contain: + """ + Theme,Twenty Fifteen + """ + And STDERR should be empty + + When I run `wp language theme update twentyfifteen --dry-run --format=summary` + Then STDOUT should be empty + And STDERR should be empty + Scenario: Handle themes with text domain different from slug Given a WP install And an empty cache diff --git a/src/Core_Language_Command.php b/src/Core_Language_Command.php index c1b01af1..e66384d5 100644 --- a/src/Core_Language_Command.php +++ b/src/Core_Language_Command.php @@ -339,6 +339,18 @@ public function uninstall( $args ) { * [--dry-run] * : Preview which translations would be updated. * + * [--format=] + * : Render output in a particular format. When not specified, updates show + * progress messages and success/warning/error messages. When specified, + * the output format changes based on the selected format. + * --- + * options: + * - table + * - csv + * - json + * - summary + * --- + * * ## EXAMPLES * * # Update installed core languages packs. @@ -351,7 +363,7 @@ public function uninstall( $args ) { * @subcommand update * * @param string[] $args Positional arguments. - * @param array{'dry-run'?: bool} $assoc_args Associative arguments. + * @param array{'dry-run'?: bool, format?: string} $assoc_args Associative arguments. */ public function update( $args, $assoc_args ) { // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod.Found -- Overruling the documentation, so not useless ;-). parent::update( $args, $assoc_args ); diff --git a/src/Plugin_Language_Command.php b/src/Plugin_Language_Command.php index 532e5110..2acd93a0 100644 --- a/src/Plugin_Language_Command.php +++ b/src/Plugin_Language_Command.php @@ -588,6 +588,18 @@ public function uninstall( $args, $assoc_args ) { * [--dry-run] * : Preview which translations would be updated. * + * [--format=] + * : Render output in a particular format. When not specified, updates show + * progress messages and success/warning/error messages. When specified, + * the output format changes based on the selected format. + * --- + * options: + * - table + * - csv + * - json + * - summary + * --- + * * ## EXAMPLES * * # Update all installed language packs for all plugins. @@ -600,7 +612,7 @@ public function uninstall( $args, $assoc_args ) { * @subcommand update * * @param string[] $args Positional arguments. - * @param array{'dry-run'?: bool, all?: bool} $assoc_args Associative arguments. + * @param array{'dry-run'?: bool, all?: bool, format?: string} $assoc_args Associative arguments. */ public function update( $args, $assoc_args ) { $all = \WP_CLI\Utils\get_flag_value( $assoc_args, 'all', false ); diff --git a/src/Theme_Language_Command.php b/src/Theme_Language_Command.php index a9589627..c30361e0 100644 --- a/src/Theme_Language_Command.php +++ b/src/Theme_Language_Command.php @@ -607,6 +607,18 @@ public function uninstall( $args, $assoc_args ) { * [--dry-run] * : Preview which translations would be updated. * + * [--format=] + * : Render output in a particular format. When not specified, updates show + * progress messages and success/warning/error messages. When specified, + * the output format changes based on the selected format. + * --- + * options: + * - table + * - csv + * - json + * - summary + * --- + * * ## EXAMPLES * * # Update all installed language packs for all themes. @@ -619,7 +631,7 @@ public function uninstall( $args, $assoc_args ) { * @subcommand update * * @param string[] $args Positional arguments. - * @param array{'dry-run'?: bool, all?: bool} $assoc_args Associative arguments. + * @param array{'dry-run'?: bool, all?: bool, format?: string} $assoc_args Associative arguments. */ public function update( $args, $assoc_args ) { $all = \WP_CLI\Utils\get_flag_value( $assoc_args, 'all', false ); diff --git a/src/WP_CLI/CommandWithTranslation.php b/src/WP_CLI/CommandWithTranslation.php index 8d9a763d..12536973 100644 --- a/src/WP_CLI/CommandWithTranslation.php +++ b/src/WP_CLI/CommandWithTranslation.php @@ -26,13 +26,20 @@ protected function sort_translations_callback( $a, $b ) { * Updates installed languages for the current object type. * * @param string[] $args Positional arguments. - * @param array{'dry-run'?: bool, all?: bool} $assoc_args Associative arguments. + * @param array{'dry-run'?: bool, all?: bool, format?: string} $assoc_args Associative arguments. */ public function update( $args, $assoc_args ) { $updates = $this->get_translation_updates(); + $format = Utils\get_flag_value( $assoc_args, 'format' ); if ( empty( $updates ) ) { - WP_CLI::success( 'Translations are up to date.' ); + if ( $format && in_array( $format, array( 'json', 'csv' ), true ) ) { + Utils\format_items( $format, array(), array() ); + } + + if ( ! $format || in_array( $format, array( 'table', 'summary' ), true ) ) { + WP_CLI::success( 'Translations are up to date.' ); + } return; } @@ -41,9 +48,17 @@ public function update( $args, $assoc_args ) { $args = array( null ); // Used for core. } + if ( $format && in_array( $format, array( 'json', 'csv' ), true ) ) { + $logger = new \WP_CLI\Loggers\Quiet(); + WP_CLI::set_logger( $logger ); + } + $upgrader = 'WP_CLI\\LanguagePackUpgrader'; $results = array(); + $results_data = array(); $num_to_update = 0; + $obj_type = rtrim( $this->obj_type, 's' ); + $slug_key = 'core' === $obj_type ? 'version' : 'slug'; foreach ( $args as $slug ) { // Gets a list of all languages. @@ -96,7 +111,6 @@ public function update( $args, $assoc_args ) { $updates_per_type[ $update->type ][] = $update; } - $obj_type = rtrim( $this->obj_type, 's' ); $available_updates = isset( $updates_per_type[ $obj_type ] ) ? $updates_per_type[ $obj_type ] : null; if ( ! is_array( $available_updates ) ) { @@ -108,7 +122,7 @@ public function update( $args, $assoc_args ) { if ( ! Utils\get_flag_value( $assoc_args, 'dry-run' ) ) { // Update translations. foreach ( $available_updates as $update ) { - WP_CLI::line( "Updating '{$update->Language}' translation for {$update->Name} {$update->Version}..." ); + WP_CLI::log( "Updating '{$update->Language}' translation for {$update->Name} {$update->Version}..." ); /** * @var \WP_CLI\LanguagePackUpgrader $upgrader_instance @@ -118,6 +132,16 @@ public function update( $args, $assoc_args ) { $result = $upgrader_instance->upgrade( $update ); $results[] = $result; + + // Capture data for formatted output (skip for summary format). + if ( $format && 'summary' !== $format ) { + $slug_value = 'version' === $slug_key ? $update->Version : $update->slug; + $results_data[] = array( + $slug_key => $slug_value, + 'language' => $update->language, + 'status' => $result ? 'updated' : 'failed', + ); + } } } } @@ -126,29 +150,43 @@ public function update( $args, $assoc_args ) { if ( Utils\get_flag_value( $assoc_args, 'dry-run' ) ) { $update_count = count( $updates ); - WP_CLI::line( - sprintf( - 'Found %d translation %s that would be processed:', - $update_count, - WP_CLI\Utils\pluralize( 'update', $update_count ) - ) - ); - - Utils\format_items( 'table', $updates, array( 'Type', 'Name', 'Version', 'Language' ) ); + if ( $format && in_array( $format, array( 'json', 'csv' ), true ) ) { + // For json/csv formats, just output the formatted data without the message + Utils\format_items( $format, $updates, array( 'Type', 'Name', 'Version', 'Language' ) ); + } elseif ( ! $format || 'table' === $format ) { + // For table or no format, show the message and table + WP_CLI::line( + sprintf( + 'Found %d translation %s that would be processed:', + $update_count, + WP_CLI\Utils\pluralize( 'update', $update_count ) + ) + ); + + Utils\format_items( 'table', $updates, array( 'Type', 'Name', 'Version', 'Language' ) ); + } + // For 'summary' format, do nothing (no output for dry-run mode) return; } $num_updated = count( array_filter( $results ) ); - $line = sprintf( "Updated $num_updated/$num_to_update %s.", WP_CLI\Utils\pluralize( 'translation', $num_updated ) ); + // Format output if --format is specified. + if ( $format && 'summary' !== $format ) { + Utils\format_items( $format, $results_data, array( $slug_key, 'language', 'status' ) ); + } + + if ( ! $format || in_array( $format, array( 'table', 'summary' ), true ) ) { + $line = sprintf( "Updated $num_updated/$num_to_update %s.", WP_CLI\Utils\pluralize( 'translation', $num_updated ) ); - if ( $num_to_update === $num_updated ) { - WP_CLI::success( $line ); - } elseif ( $num_updated > 0 ) { - WP_CLI::warning( $line ); - } else { - WP_CLI::error( $line ); + if ( $num_to_update === $num_updated ) { + WP_CLI::success( $line ); + } elseif ( $num_updated > 0 ) { + WP_CLI::warning( $line ); + } else { + WP_CLI::error( $line ); + } } }