Skip to content

Upgrade/Install: Show and select translation updates on the Updates screen - #11610

Open
s-a-s-k-i-a wants to merge 7 commits into
WordPress:trunkfrom
s-a-s-k-i-a:42281-list-translation-updates
Open

s-a-s-k-i-a wants to merge 7 commits into
WordPress:trunkfrom
s-a-s-k-i-a:42281-list-translation-updates

Conversation

@s-a-s-k-i-a

@s-a-s-k-i-a s-a-s-k-i-a commented Apr 20, 2026 •

Copy link
Copy Markdown

Trac ticket: https://core.trac.wordpress.org/ticket/42281

Summary

list_translation_updates() currently renders only a generic translations notice and an Update Translations button. Unlike the WordPress core, plugin, and theme update sections, it does not resolve or display the specific translation updates that are pending, and it does not allow individual translation updates to be deferred for later installation.

This PR brings the translations section closer to the existing plugin and theme update UX by:

  • listing the specific pending translation updates
  • rendering them in a checkbox table that matches the existing updates UI
  • allowing individual translation updates to be left unchecked and installed later
  • persisting those unchecked translation updates as deferred so they remain available and are skipped by background translation updates until selected again

Changes

  • add translation update display helpers in wp-admin/includes/update.php
  • add translation update identifiers and deferred-state helpers in wp-includes/update.php
  • resolve translation update names for core, themes, and plugins
  • map plugin translation slugs back to installed plugin files so WordPress.org-style slugs still display the correct plugin name
  • replace the plain translations list on update-core.php with a checkbox table styled like the existing plugin/theme update tables
  • persist unchecked translation updates in a site option and keep them visible for later installation
  • pass only selected translation updates to Language_Pack_Upgrader::bulk_upgrade()
  • skip deferred translation updates in WP_Automatic_Updater::should_update() and Language_Pack_Upgrader::async_upgrade()
  • add PHPUnit coverage for translation update display/deferred data, identifier handling, and deferred background/async update behavior

Test plan

  • php -l src/wp-admin/includes/update.php
  • php -l src/wp-admin/update-core.php
  • php -l src/wp-includes/update.php
  • php -l src/wp-admin/includes/class-language-pack-upgrader.php
  • php -l src/wp-admin/includes/class-wp-automatic-updater.php
  • php -l tests/phpunit/tests/admin/includesUpdate.php
  • php -l tests/phpunit/tests/admin/languagePackUpgrader.php
  • php -l tests/phpunit/tests/admin/wpAutomaticUpdater.php
  • vendor/bin/phpcs --standard=phpcs.xml.dist src/wp-admin/includes/update.php src/wp-admin/update-core.php src/wp-includes/update.php src/wp-admin/includes/class-language-pack-upgrader.php src/wp-admin/includes/class-wp-automatic-updater.php tests/phpunit/tests/admin/includesUpdate.php tests/phpunit/tests/admin/languagePackUpgrader.php tests/phpunit/tests/admin/wpAutomaticUpdater.php
  • git diff --check
  • npm run build:dev
  • Authenticated HTTP E2E validation against https://wpcore.wp-studio.dev/wp-admin/update-core.php using a temporary MU-plugin fixture and local language-pack ZIP fixtures:
  • verified the translations table rendered with 3 rows for WordPress, Hello Dolly, and Twenty Twenty-Four
  • verified all 3 translation rows were selected by default
  • submitted the form with only the core and theme translation updates selected
  • verified the upgrade screen completed with 2 successful translation updates
  • verified the remaining plugin translation update stayed available on the Updates screen and rendered unchecked
  • verified deferred_translation_updates persisted only the unchecked plugin translation update
  • verified WP_Automatic_Updater::should_update( 'translation', ... ) returned false for the deferred plugin translation update and true for a non-deferred translation update
  • verified Language_Pack_Upgrader::async_upgrade() left the deferred translation update pending
  • Current-head WordPress Playground E2E verified all-select, clear-all, defer-all, persisted unchecked rows, and malformed query handling with controlled core/plugin/theme translation fixtures
  • Official GitHub Actions: Coding Standards, PHP Compatibility, PHPStan, build, both E2E variants, performance, and upgrade tests passed
  • Official PHPUnit matrix: 176 jobs passed and 3 were skipped by matrix conditions. One PHP 7.4/MySQL 8.0 multisite job reached the workflow's 20-minute limit after its PHPUnit, AJAX, ms-files, and Xdebug test commands had all passed.

Local PHPUnit execution was unavailable because this environment does not have the required WordPress test database/Docker runtime; the official GitHub Actions matrix provides the PHPUnit validation above.

Use of AI Tools

AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5-based Codex
Used for: implementation, test scaffolding, server-side E2E validation orchestration, and PR drafting; outputs were reviewed and validated with syntax checks, PHPCS, git diff --check, and authenticated admin-page testing.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions

Copy link
Copy Markdown

Hi @s-a-s-k-i-a! 👋

Thank you for your contribution to WordPress! 💖

It looks like this is your first pull request to wordpress-develop. Here are a few things to be aware of that may help you out!

No one monitors this repository for new pull requests. Pull requests must be attached to a Trac ticket to be considered for inclusion in WordPress Core. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description.

Pull requests are never merged on GitHub. The WordPress codebase continues to be managed through the SVN repository that this GitHub repository mirrors. Please feel free to open pull requests to work on any contribution you are making.

More information about how GitHub pull requests can be used to contribute to WordPress can be found in the Core Handbook.

Please include automated tests. Including tests in your pull request is one way to help your patch be considered faster. To learn about WordPress' test suites, visit the Automated Testing page in the handbook.

If you have not had a chance, please review the Contribute with Code page in the WordPress Core Handbook.

The Developer Hub also documents the various coding standards that are followed:

Thank you,
The WordPress Project

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@s-a-s-k-i-a

Copy link
Copy Markdown
Author

Playground test passed. Manually tested and reviewed. Works.

Test this pull request with WordPress Playground.

@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from a43e354 to 7955a63 Compare April 21, 2026 09:53
@s-a-s-k-i-a s-a-s-k-i-a changed the title Upgrade/Install: List available translation updates on the Updates screen Upgrade/Install: Show and select translation updates on the Updates screen Apr 21, 2026
@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from c8dd62d to d9cbf3e Compare April 21, 2026 16:34
@s-a-s-k-i-a
s-a-s-k-i-a marked this pull request as ready for review April 21, 2026 16:35
@github-actions

github-actions Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props jyria, tfrommen.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from d9cbf3e to c507717 Compare April 21, 2026 17:58

@tfrommen tfrommen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋 Hey there!

Since wp_is_translation_update_deferred is performing one or more DB queries under the hood, you might want to call it only if needed. If $update is already false, there's no point in possibly setting it to the same value again. 😉

Comment thread src/wp-admin/includes/class-language-pack-upgrader.php Outdated
Comment thread src/wp-admin/includes/class-wp-automatic-updater.php Outdated
@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from c507717 to 460f52f Compare May 21, 2026 07:12
@s-a-s-k-i-a

Copy link
Copy Markdown
Author

Quick note on the remaining failing PHPUnit jobs in the current run:

  • PHP 8.1 / MariaDB 11.4 fails during npm run env:install with Database connection error (2002) Connection refused.
  • PHP 8.0 / MariaDB 11.8 fails during gutenberg:verify while downloading the Gutenberg artifact with Download/extraction failed: fetch failed.

These both appear to be CI/infrastructure issues rather than failures caused by the translation update changeset. The other workflows for the current head are passing, and the targeted syntax, PHPCS, smoke, and E2E validation for this patch remain green.

Happy to take another look if either failure reproduces consistently on a rerun.

@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from 460f52f to 8ce1f44 Compare June 16, 2026 16:03
@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from 8ce1f44 to 528b252 Compare July 24, 2026 06:17
Copilot AI review requested due to automatic review settings July 24, 2026 06:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the WordPress Updates screen translation section to mirror the plugin/theme update UX: it lists individual pending translation updates in a checkbox table, allows deferring specific translation updates, and ensures deferred translation updates are skipped by background/async translation updates until re-selected.

Changes:

  • Added stable identifiers and site-option persistence helpers to defer/restore translation updates across requests.
  • Replaced the translations section on update-core.php with a checkbox table and updated the translation upgrade handler to install only selected translation updates while persisting unchecked ones as deferred.
  • Updated background translation update logic to skip deferred translation updates, and added PHPUnit coverage for the new helpers and display data.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/phpunit/tests/admin/includesUpdate.php Adds PHPUnit tests for translation update display data, stable IDs, and deferred-state behavior.
src/wp-includes/update.php Introduces translation update stable IDs and deferred update get/set helpers stored in a site option.
src/wp-admin/update-core.php Renders translation updates in a checkbox table and processes selected vs deferred updates during translation upgrades; adds an admin notice for deferred selections.
src/wp-admin/includes/update.php Adds display helpers to resolve translation update names/languages and build display-ready translation update rows.
src/wp-admin/includes/class-wp-automatic-updater.php Skips deferred translation updates when deciding whether to auto-update translations.
src/wp-admin/includes/class-language-pack-upgrader.php Skips deferred translation updates during async translation upgrades.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wp-admin/update-core.php Outdated
Comment thread src/wp-admin/includes/update.php Outdated
Copilot AI review requested due to automatic review settings July 24, 2026 06:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment thread src/wp-admin/update-core.php
@s-a-s-k-i-a

s-a-s-k-i-a commented Jul 24, 2026 •

Copy link
Copy Markdown
Author

@tfrommen The branch is rebased to current trunk, and your previous feedback remains addressed. The final review follow-up in ac83634 removes the duplicate translation-update lookup and adds focused regression coverage for both WP_Automatic_Updater::should_update() and Language_Pack_Upgrader::async_upgrade(); all review threads are resolved.

Current-head validation is complete: Coding Standards, PHP Compatibility, PHPStan, build, both E2E variants, performance, and upgrade tests pass. In the PHPUnit matrix, 176 jobs pass and 3 are skipped by matrix conditions. One PHP 7.4/MySQL 8.0 multisite job reaches the workflow's 20-minute limit after PHPUnit, AJAX, ms-files, and Xdebug have all passed; rerunning upstream requires repository admin rights.

If you have bandwidth for another look, I would appreciate it. Thank you!

Copilot AI review requested due to automatic review settings July 24, 2026 07:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Comment thread src/wp-admin/update-core.php Outdated
Comment thread src/wp-admin/includes/class-wp-automatic-updater.php
Comment thread src/wp-admin/includes/class-language-pack-upgrader.php
Copilot AI review requested due to automatic review settings July 24, 2026 07:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from ac83634 to c4019f6 Compare September 4, 2026 07:21
@s-a-s-k-i-a
s-a-s-k-i-a force-pushed the 42281-list-translation-updates branch from c4019f6 to 67819ce Compare September 6, 2026 17:56

@tfrommen tfrommen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned on Trac, I gave this another review and left some inline feedback. Overall, I still think this is good.

The @since annotations need a refresh.

Some code could do with (more) tests, which also requires some minor refactoring.

Comment thread src/wp-admin/includes/update.php Outdated
/**
* Gets plugin names for translation updates.
*
* @since 7.1.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue:

All new functionality is currently marked with 7.1.0. This needs to be refreshed.

@s-a-s-k-i-a s-a-s-k-i-a Oct 16, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the new @since annotations to 7.2.0 in d9930c0. That's our target release, not a confirmed inclusion. The final version will need to match the release this lands in.

Comment thread src/wp-admin/update-core.php Outdated

check_admin_referer( 'upgrade-translations' );

if ( empty( $_POST['translations'] ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note:

There is a behavior change in handling requests. Previously, any POST request upgraded all translations. Now, plugins, scripts, or cached forms that post without translations[] do nothing, with no notice. I believe this is the intended behavior, but I wanted to point out the change nevertheless.

@s-a-s-k-i-a s-a-s-k-i-a Oct 16, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is intentional: a request without the form's update IDs installs nothing and leaves saved preferences unchanged. Unchecking everything in the current form is different: those updates are saved for later, and a confirmation is shown. Tests cover both cases and outdated forms.

Comment thread src/wp-admin/update-core.php Outdated
Comment on lines +1381 to +1399
$selected_translation_updates = array();

if ( ! empty( $_POST['checked'] ) ) {
$selected_translation_update_ids = array_unique(
array_map(
'sanitize_text_field',
wp_unslash( (array) $_POST['checked'] )
)
);

$selected_translation_updates = array_intersect_key( $translation_updates, array_flip( $selected_translation_update_ids ) );
}

$deferred_translation_updates = array_intersect_key(
$current_translation_updates,
wp_get_deferred_translation_updates( $current_translation_updates )
);
$deferred_translation_updates = array_diff_key( $deferred_translation_updates, $translation_updates );
$deferred_translation_updates += array_diff_key( $translation_updates, $selected_translation_updates );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion:

Since this core logic lives directly in the file scope, it cannot be (and thus is not) unit-tested. I'd recommend extracting it into a function (e.g., in wp-admin/includes/update.php) and add unit tests for stale entries, items that are not in the form, and all-unchecked.

@s-a-s-k-i-a s-a-s-k-i-a Oct 16, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the selection logic into wp_process_translation_update_selection() in d9930c0. Permission and nonce checks stay in the controller. Tests cover outdated entries, updates outside the form and unchecking everything, and verify which updates are selected or saved for later.

/**
* @ticket 42281
*/
public function test_wp_get_translation_update_data_loads_plugin_names_once() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion:

This test is brittle. It checks hard-coded 2 transient reads. That number breaks when any code adds a read. The property that matters is that reads do not grow with the number of plugin rows.

A better test could look like this (I did not actually run this):

public function test_wp_get_translation_update_data_reads_plugin_updates_independent_of_row_count() {
	$count_reads = function ( $row_count ) {
		$translations = array();
		for ( $i = 1; $i <= $row_count; $i++ ) {
			$translations[] = array(
				'type'     => 'plugin',
				'slug'     => "plugin-$i",
				'language' => 'de_DE',
				'version'  => '1.0.0',
			);
		}

		set_site_transient( 'update_plugins', (object) array( 'translations' => $translations ) );

		$reads  = 0;
		$filter = static function ( $value ) use ( &$reads ) {
			++$reads;
			return $value;
		};

		add_filter( 'pre_site_transient_update_plugins', $filter );
		wp_get_translation_update_data();
		remove_filter( 'pre_site_transient_update_plugins', $filter );

		return $reads;
	};

	$this->assertSame(
		$count_reads( 1 ),
		$count_reads( 10 ),
		'Plugin update reads must not grow with the number of translation rows.'
	);
}

You may also want to add a test with zero plugin updates to confirm that the plugin-name lookup does not run at all.

@s-a-s-k-i-a s-a-s-k-i-a Oct 16, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test now compares one plugin translation row with ten instead of requiring exactly two reads. Separate tests check that no plugin-name lookup is added when there are no updates or only Core/theme updates. All these tests pass in d9930c0.

Extract and test translation selection processing, avoid redundant update-map work, and refresh the intended release annotations. Document explicit-update scope and clear matching deferrals after successful language-pack installation.

See https://core.trac.wordpress.org/ticket/42281.
@s-a-s-k-i-a

s-a-s-k-i-a commented Oct 16, 2026 •

Copy link
Copy Markdown
Author

I've pushed d9930c0 and replied to each of your comments in its own thread.

Explicit updates, including WP-CLI, can still install translations saved for later. The shared upgrader now clears the saved deferral only after a successful installation; failed updates remain deferred.

The tests passed for manual selection, background updates and WP-CLI, including real WordPress.org downloads. All 10 GitHub workflows completed successfully: 121 checks passed and 20 skipped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants