wp/wp-admin/js/updates.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
--- a/wp/wp-admin/js/updates.js	Tue Oct 22 16:11:46 2019 +0200
+++ b/wp/wp-admin/js/updates.js	Tue Dec 15 13:49:49 2020 +0100
@@ -11,8 +11,7 @@
  * @param {jQuery}  $                                   jQuery object.
  * @param {object}  wp                                  WP object.
  * @param {object}  settings                            WP Updates settings.
- * @param {string}  settings.ajax_nonce                 AJAX nonce.
- * @param {object}  settings.l10n                       Translation strings.
+ * @param {string}  settings.ajax_nonce                 Ajax nonce.
  * @param {object=} settings.plugins                    Base names of plugins in their different states.
  * @param {Array}   settings.plugins.all                Base names of all plugins.
  * @param {Array}   settings.plugins.active             Base names of active plugins.
@@ -27,7 +26,10 @@
  * @param {number}  settings.totals.count               Holds the amount of available updates.
  */
 (function( $, wp, settings ) {
-	var $document = $( document );
+	var $document = $( document ),
+		__ = wp.i18n.__,
+		_x = wp.i18n._x,
+		sprintf = wp.i18n.sprintf;
 
 	wp = wp || {};
 
@@ -41,6 +43,81 @@
 	wp.updates = {};
 
 	/**
+	 * Removed in 5.5.0, needed for back-compatibility.
+	 *
+	 * @since 4.2.0
+	 * @deprecated 5.5.0
+	 *
+	 * @type {object}
+	 */
+	wp.updates.l10n = {
+		searchResults: '',
+		searchResultsLabel: '',
+		noPlugins: '',
+		noItemsSelected: '',
+		updating: '',
+		pluginUpdated: '',
+		themeUpdated: '',
+		update: '',
+		updateNow: '',
+		pluginUpdateNowLabel: '',
+		updateFailedShort: '',
+		updateFailed: '',
+		pluginUpdatingLabel: '',
+		pluginUpdatedLabel: '',
+		pluginUpdateFailedLabel: '',
+		updatingMsg: '',
+		updatedMsg: '',
+		updateCancel: '',
+		beforeunload: '',
+		installNow: '',
+		pluginInstallNowLabel: '',
+		installing: '',
+		pluginInstalled: '',
+		themeInstalled: '',
+		installFailedShort: '',
+		installFailed: '',
+		pluginInstallingLabel: '',
+		themeInstallingLabel: '',
+		pluginInstalledLabel: '',
+		themeInstalledLabel: '',
+		pluginInstallFailedLabel: '',
+		themeInstallFailedLabel: '',
+		installingMsg: '',
+		installedMsg: '',
+		importerInstalledMsg: '',
+		aysDelete: '',
+		aysDeleteUninstall: '',
+		aysBulkDelete: '',
+		aysBulkDeleteThemes: '',
+		deleting: '',
+		deleteFailed: '',
+		pluginDeleted: '',
+		themeDeleted: '',
+		livePreview: '',
+		activatePlugin: '',
+		activateTheme: '',
+		activatePluginLabel: '',
+		activateThemeLabel: '',
+		activateImporter: '',
+		activateImporterLabel: '',
+		unknownError: '',
+		connectionError: '',
+		nonceError: '',
+		pluginsFound: '',
+		noPluginsFound: '',
+		autoUpdatesEnable: '',
+		autoUpdatesEnabling: '',
+		autoUpdatesEnabled: '',
+		autoUpdatesDisable: '',
+		autoUpdatesDisabling: '',
+		autoUpdatesDisabled: '',
+		autoUpdatesError: ''
+	};
+
+	wp.updates.l10n = window.wp.deprecateL10nObject( 'wp.updates.l10n', wp.updates.l10n );
+
+	/**
 	 * User nonce for ajax calls.
 	 *
 	 * @since 4.2.0
@@ -50,15 +127,6 @@
 	wp.updates.ajaxNonce = settings.ajax_nonce;
 
 	/**
-	 * Localized strings.
-	 *
-	 * @since 4.2.0
-	 *
-	 * @type {object}
-	 */
-	wp.updates.l10n = settings.l10n;
-
-	/**
 	 * Current search term.
 	 *
 	 * @since 4.6.0
@@ -157,7 +225,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}  data
+	 * @param {Object}  data
 	 * @param {*=}      data.selector      Optional. Selector of an element to be replaced with the admin notice.
 	 * @param {string=} data.id            Optional. Unique id that will be used as the notice's id attribute.
 	 * @param {string=} data.className     Optional. Class names that will be used in the admin notice.
@@ -201,7 +269,7 @@
 	 * @since 4.6.0
 	 *
 	 * @param {string} action The type of Ajax request ('update-plugin', 'install-theme', etc).
-	 * @param {object} data   Data that needs to be passed to the ajax callback.
+	 * @param {Object} data   Data that needs to be passed to the ajax callback.
 	 * @return {$.promise}    A jQuery promise that represents the request,
 	 *                        decorated with an abort() method.
 	 */
@@ -250,8 +318,8 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}  response
-	 * @param {array=}  response.debug     Optional. Debug information.
+	 * @param {Object}  response
+	 * @param {Array=}  response.debug     Optional. Debug information.
 	 * @param {string=} response.errorCode Optional. Error code for an error that occurred.
 	 */
 	wp.updates.ajaxAlways = function( response ) {
@@ -362,7 +430,7 @@
 	 * @since 4.2.0
 	 * @since 4.6.0 More accurately named `updatePlugin`.
 	 *
-	 * @param {object}               args         Arguments.
+	 * @param {Object}               args         Arguments.
 	 * @param {string}               args.plugin  Plugin basename.
 	 * @param {string}               args.slug    Plugin slug.
 	 * @param {updatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.updatePluginSuccess
@@ -381,23 +449,31 @@
 		if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
 			$updateRow = $( 'tr[data-plugin="' + args.plugin + '"]' );
 			$message   = $updateRow.find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' );
-			message    = wp.updates.l10n.pluginUpdatingLabel.replace( '%s', $updateRow.find( '.plugin-title strong' ).text() );
+			message    = sprintf(
+				/* translators: %s: Plugin name and version. */
+ 				_x( 'Updating %s...', 'plugin' ),
+				$updateRow.find( '.plugin-title strong' ).text()
+			);
 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
 			$card    = $( '.plugin-card-' + args.slug );
 			$message = $card.find( '.update-now' ).addClass( 'updating-message' );
-			message  = wp.updates.l10n.pluginUpdatingLabel.replace( '%s', $message.data( 'name' ) );
+			message    = sprintf(
+				/* translators: %s: Plugin name and version. */
+ 				_x( 'Updating %s...', 'plugin' ),
+				$message.data( 'name' )
+			);
 
 			// Remove previous error messages, if any.
 			$card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove();
 		}
 
-		if ( $message.html() !== wp.updates.l10n.updating ) {
+		if ( $message.html() !== __( 'Updating...' ) ) {
 			$message.data( 'originaltext', $message.html() );
 		}
 
 		$message
 			.attr( 'aria-label', message )
-			.text( wp.updates.l10n.updating );
+			.text( __( 'Updating...' ) );
 
 		$document.trigger( 'wp-plugin-updating', args );
 
@@ -409,8 +485,9 @@
 	 *
 	 * @since 4.2.0
 	 * @since 4.6.0 More accurately named `updatePluginSuccess`.
+	 * @since 5.5.0 Auto-update "time to next update" text cleared.
 	 *
-	 * @param {object} response            Response from the server.
+	 * @param {Object} response            Response from the server.
 	 * @param {string} response.slug       Slug of the plugin to be updated.
 	 * @param {string} response.plugin     Basename of the plugin to be updated.
 	 * @param {string} response.pluginName Name of the plugin to be updated.
@@ -431,6 +508,9 @@
 			// Update the version number in the row.
 			newText = $pluginRow.find( '.plugin-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
 			$pluginRow.find( '.plugin-version-author-uri' ).html( newText );
+
+			// Clear the "time to next auto-update" text.
+			$pluginRow.find( '.auto-update-time' ).empty();
 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
 			$updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' )
 				.removeClass( 'updating-message' )
@@ -438,10 +518,17 @@
 		}
 
 		$updateMessage
-			.attr( 'aria-label', wp.updates.l10n.pluginUpdatedLabel.replace( '%s', response.pluginName ) )
-			.text( wp.updates.l10n.pluginUpdated );
-
-		wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Plugin name and version. */
+					_x( '%s updated!', 'plugin' ),
+					response.pluginName
+				)
+			)
+			.text( _x( 'Updated!', 'plugin' ) );
+
+		wp.a11y.speak( __( 'Update completed successfully.' ) );
 
 		wp.updates.decrementCount( 'plugin' );
 
@@ -454,7 +541,7 @@
 	 * @since 4.2.0
 	 * @since 4.6.0 More accurately named `updatePluginError`.
 	 *
-	 * @param {object}  response              Response from the server.
+	 * @param {Object}  response              Response from the server.
 	 * @param {string}  response.slug         Slug of the plugin to be updated.
 	 * @param {string}  response.plugin       Basename of the plugin to be updated.
 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be updated.
@@ -472,7 +559,11 @@
 			return;
 		}
 
-		errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage );
+		errorMessage = sprintf(
+			/* translators: %s: Error string for a failed update. */
+			__( 'Update failed: %s' ),
+			response.errorMessage
+		);
 
 		if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
 			if ( response.plugin ) {
@@ -484,7 +575,14 @@
 
 			if ( response.pluginName ) {
 				$message.find( 'p' )
-					.attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) );
+					.attr(
+						'aria-label',
+						sprintf(
+							/* translators: %s: Plugin name and version. */
+							_x( '%s update failed.', 'plugin' ),
+							response.pluginName
+						)
+					);
 			} else {
 				$message.find( 'p' ).removeAttr( 'aria-label' );
 			}
@@ -497,11 +595,19 @@
 				} ) );
 
 			$card.find( '.update-now' )
-				.text( wp.updates.l10n.updateFailedShort ).removeClass( 'updating-message' );
+				.text(  __( 'Update failed.' ) )
+				.removeClass( 'updating-message' );
 
 			if ( response.pluginName ) {
 				$card.find( '.update-now' )
-					.attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) );
+					.attr(
+						'aria-label',
+						sprintf(
+							/* translators: %s: Plugin name and version. */
+							_x( '%s update failed.', 'plugin' ),
+							response.pluginName
+						)
+					);
 			} else {
 				$card.find( '.update-now' ).removeAttr( 'aria-label' );
 			}
@@ -516,7 +622,7 @@
 
 					$card.find( '.update-now' )
 						.attr( 'aria-label', false )
-						.text( wp.updates.l10n.updateNow );
+						.text( __( 'Update Now' ) );
 				}, 200 );
 			} );
 		}
@@ -531,7 +637,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}                args         Arguments.
+	 * @param {Object}                args         Arguments.
 	 * @param {string}                args.slug    Plugin identifier in the WordPress.org Plugin repository.
 	 * @param {installPluginSuccess=} args.success Optional. Success callback. Default: wp.updates.installPluginSuccess
 	 * @param {installPluginError=}   args.error   Optional. Error callback. Default: wp.updates.installPluginError
@@ -551,16 +657,23 @@
 			$message = $( '[data-slug="' + args.slug + '"]' );
 		}
 
-		if ( $message.html() !== wp.updates.l10n.installing ) {
+		if ( $message.html() !== __( 'Installing...' ) ) {
 			$message.data( 'originaltext', $message.html() );
 		}
 
 		$message
 			.addClass( 'updating-message' )
-			.attr( 'aria-label', wp.updates.l10n.pluginInstallingLabel.replace( '%s', $message.data( 'name' ) ) )
-			.text( wp.updates.l10n.installing );
-
-		wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Plugin name and version. */
+					_x( 'Installing %s...', 'plugin' ),
+					$message.data( 'name' )
+				)
+			)
+			.text( __( 'Installing...' ) );
+
+		wp.a11y.speak( __( 'Installing... please wait.' ) );
 
 		// Remove previous error messages, if any.
 		$card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove();
@@ -575,7 +688,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response             Response from the server.
+	 * @param {Object} response             Response from the server.
 	 * @param {string} response.slug        Slug of the installed plugin.
 	 * @param {string} response.pluginName  Name of the installed plugin.
 	 * @param {string} response.activateUrl URL to activate the just installed plugin.
@@ -586,10 +699,17 @@
 		$message
 			.removeClass( 'updating-message' )
 			.addClass( 'updated-message installed button-disabled' )
-			.attr( 'aria-label', wp.updates.l10n.pluginInstalledLabel.replace( '%s', response.pluginName ) )
-			.text( wp.updates.l10n.pluginInstalled );
-
-		wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Plugin name and version. */
+					_x( '%s installed!', 'plugin' ),
+					response.pluginName
+				)
+			)
+			.text( _x( 'Installed!', 'plugin' ) );
+
+		wp.a11y.speak( __( 'Installation completed successfully.' ) );
 
 		$document.trigger( 'wp-plugin-install-success', response );
 
@@ -597,10 +717,33 @@
 			setTimeout( function() {
 
 				// Transform the 'Install' button into an 'Activate' button.
-				$message.removeClass( 'install-now installed button-disabled updated-message' ).addClass( 'activate-now button-primary' )
-					.attr( 'href', response.activateUrl )
-					.attr( 'aria-label', wp.updates.l10n.activatePluginLabel.replace( '%s', response.pluginName ) )
-					.text( wp.updates.l10n.activatePlugin );
+				$message.removeClass( 'install-now installed button-disabled updated-message' )
+					.addClass( 'activate-now button-primary' )
+					.attr( 'href', response.activateUrl );
+
+				if ( 'plugins-network' === pagenow ) {
+					$message
+						.attr(
+							'aria-label',
+							sprintf(
+								/* translators: %s: Plugin name. */
+								_x( 'Network Activate %s', 'plugin' ),
+								response.pluginName
+							)
+						)
+						.text( __( 'Network Activate' ) );
+				} else {
+					$message
+						.attr(
+							'aria-label',
+							sprintf(
+								/* translators: %s: Plugin name. */
+								_x( 'Activate %s', 'plugin' ),
+								response.pluginName
+							)
+						)
+						.text( __( 'Activate' ) );
+				}
 			}, 1000 );
 		}
 	};
@@ -610,7 +753,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}  response              Response from the server.
+	 * @param {Object}  response              Response from the server.
 	 * @param {string}  response.slug         Slug of the plugin to be installed.
 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
 	 * @param {string}  response.errorCode    Error code for the error that occurred.
@@ -629,7 +772,11 @@
 			return;
 		}
 
-		errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage );
+		errorMessage = sprintf(
+			/* translators: %s: Error string for a failed installation. */
+			__( 'Installation failed: %s' ),
+			response.errorMessage
+		);
 
 		$card
 			.addClass( 'plugin-card-update-failed' )
@@ -647,8 +794,15 @@
 
 		$button
 			.removeClass( 'updating-message' ).addClass( 'button-disabled' )
-			.attr( 'aria-label', wp.updates.l10n.pluginInstallFailedLabel.replace( '%s', $button.data( 'name' ) ) )
-			.text( wp.updates.l10n.installFailedShort );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Plugin name and version. */
+					_x( '%s installation failed', 'plugin' ),
+					$button.data( 'name' )
+				)
+			)
+			.text( __( 'Installation failed.' ) );
 
 		wp.a11y.speak( errorMessage, 'assertive' );
 
@@ -660,7 +814,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response             Response from the server.
+	 * @param {Object} response             Response from the server.
 	 * @param {string} response.slug        Slug of the installed plugin.
 	 * @param {string} response.pluginName  Name of the installed plugin.
 	 * @param {string} response.activateUrl URL to activate the just installed plugin.
@@ -669,7 +823,11 @@
 		wp.updates.addAdminNotice( {
 			id:        'install-success',
 			className: 'notice-success is-dismissible',
-			message:   wp.updates.l10n.importerInstalledMsg.replace( '%s', response.activateUrl + '&from=import' )
+			message:   sprintf(
+				/* translators: %s: Activation URL. */
+				__( 'Importer installed successfully. <a href="%s">Run importer</a>' ),
+				response.activateUrl + '&from=import'
+			)
 		} );
 
 		$( '[data-slug="' + response.slug + '"]' )
@@ -677,11 +835,15 @@
 			.addClass( 'activate-now' )
 			.attr({
 				'href': response.activateUrl + '&from=import',
-				'aria-label': wp.updates.l10n.activateImporterLabel.replace( '%s', response.pluginName )
+				'aria-label':sprintf(
+					/* translators: %s: Importer name. */
+					__( 'Run %s' ),
+					response.pluginName
+				)
 			})
-			.text( wp.updates.l10n.activateImporter );
-
-		wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
+			.text( __( 'Run Importer' ) );
+
+		wp.a11y.speak( __( 'Installation completed successfully.' ) );
 
 		$document.trigger( 'wp-importer-install-success', response );
 	};
@@ -691,14 +853,18 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}  response              Response from the server.
+	 * @param {Object}  response              Response from the server.
 	 * @param {string}  response.slug         Slug of the plugin to be installed.
 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
 	 * @param {string}  response.errorCode    Error code for the error that occurred.
 	 * @param {string}  response.errorMessage The error that occurred.
 	 */
 	wp.updates.installImporterError = function( response ) {
-		var errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ),
+		var errorMessage = sprintf(
+				/* translators: %s: Error string for a failed installation. */
+				__( 'Installation failed: %s' ),
+				response.errorMessage
+			),
 			$installLink = $( '[data-slug="' + response.slug + '"]' ),
 			pluginName = $installLink.data( 'name' );
 
@@ -718,8 +884,15 @@
 
 		$installLink
 			.removeClass( 'updating-message' )
-			.text( wp.updates.l10n.installNow )
-			.attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', pluginName ) );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Plugin name. */
+					_x( 'Install %s now', 'plugin' ),
+					pluginName
+				)
+			)
+			.text( __( 'Install Now' ) );
 
 		wp.a11y.speak( errorMessage, 'assertive' );
 
@@ -731,7 +904,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}               args         Arguments.
+	 * @param {Object}               args         Arguments.
 	 * @param {string}               args.plugin  Basename of the plugin to be deleted.
 	 * @param {string}               args.slug    Slug of the plugin to be deleted.
 	 * @param {deletePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.deletePluginSuccess
@@ -747,13 +920,13 @@
 			error: wp.updates.deletePluginError
 		}, args );
 
-		if ( $link.html() !== wp.updates.l10n.deleting ) {
+		if ( $link.html() !== __( 'Deleting...' ) ) {
 			$link
 				.data( 'originaltext', $link.html() )
-				.text( wp.updates.l10n.deleting );
+				.text( __( 'Deleting...' ) );
 		}
 
-		wp.a11y.speak( wp.updates.l10n.deleting, 'polite' );
+		wp.a11y.speak( __( 'Deleting...' ) );
 
 		$document.trigger( 'wp-plugin-deleting', args );
 
@@ -843,12 +1016,12 @@
 				$views.find( '.all' ).remove();
 
 				if ( ! $form.find( 'tr.no-items' ).length ) {
-					$form.find( '#the-list' ).append( '<tr class="no-items"><td class="colspanchange" colspan="' + columnCount + '">' + wp.updates.l10n.noPlugins + '</td></tr>' );
+					$form.find( '#the-list' ).append( '<tr class="no-items"><td class="colspanchange" colspan="' + columnCount + '">' + __( 'No plugins are currently available.' ) + '</td></tr>' );
 				}
 			}
 		} );
 
-		wp.a11y.speak( wp.updates.l10n.pluginDeleted, 'polite' );
+		wp.a11y.speak( _x( 'Deleted!', 'plugin' ) );
 
 		$document.trigger( 'wp-plugin-delete-success', response );
 	};
@@ -858,7 +1031,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}  response              Response from the server.
+	 * @param {Object}  response              Response from the server.
 	 * @param {string}  response.slug         Slug of the plugin to be deleted.
 	 * @param {string}  response.plugin       Base name of the plugin to be deleted
 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be deleted.
@@ -915,7 +1088,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}              args         Arguments.
+	 * @param {Object}              args         Arguments.
 	 * @param {string}              args.slug    Theme stylesheet.
 	 * @param {updateThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.updateThemeSuccess
 	 * @param {updateThemeError=}   args.error   Optional. Error callback. Default: wp.updates.updateThemeError
@@ -953,12 +1126,12 @@
 			$notice = $notice.addClass( 'updating-message' ).find( 'p' );
 		}
 
-		if ( $notice.html() !== wp.updates.l10n.updating ) {
+		if ( $notice.html() !== __( 'Updating...' ) ) {
 			$notice.data( 'originaltext', $notice.html() );
 		}
 
-		wp.a11y.speak( wp.updates.l10n.updatingMsg, 'polite' );
-		$notice.text( wp.updates.l10n.updating );
+		wp.a11y.speak( __( 'Updating... please wait.' ) );
+		$notice.text( __( 'Updating...' ) );
 
 		$document.trigger( 'wp-theme-updating', args );
 
@@ -969,10 +1142,11 @@
 	 * Updates the UI appropriately after a successful theme update.
 	 *
 	 * @since 4.6.0
+	 * @since 5.5.0 Auto-update "time to next update" text cleared.
 	 *
-	 * @param {object} response
+	 * @param {Object} response
 	 * @param {string} response.slug       Slug of the theme to be updated.
-	 * @param {object} response.theme      Updated theme.
+	 * @param {Object} response.theme      Updated theme.
 	 * @param {string} response.oldVersion Old version of the theme.
 	 * @param {string} response.newVersion New version of the theme.
 	 */
@@ -981,7 +1155,7 @@
 			$theme         = $( '[data-slug="' + response.slug + '"]' ),
 			updatedMessage = {
 				className: 'updated-message notice-success notice-alt',
-				message:   wp.updates.l10n.themeUpdated
+				message:   _x( 'Updated!', 'theme' )
 			},
 			$notice, newText;
 
@@ -1002,19 +1176,23 @@
 			// Update the version number in the row.
 			newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
 			$theme.find( '.theme-version-author-uri' ).html( newText );
+
+			// Clear the "time to next auto-update" text.
+			$theme.find( '.auto-update-time' ).empty();
 		} else {
 			$notice = $( '.theme-info .notice' ).add( $theme.find( '.update-message' ) );
 
 			// Focus on Customize button after updating.
 			if ( isModalOpen ) {
 				$( '.load-customize:visible' ).focus();
+				$( '.theme-info .theme-autoupdate' ).find( '.auto-update-time' ).empty();
 			} else {
 				$theme.find( '.load-customize' ).focus();
 			}
 		}
 
 		wp.updates.addAdminNotice( _.extend( { selector: $notice }, updatedMessage ) );
-		wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' );
+		wp.a11y.speak( __( 'Update completed successfully.' ) );
 
 		wp.updates.decrementCount( 'theme' );
 
@@ -1031,14 +1209,18 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response              Response from the server.
+	 * @param {Object} response              Response from the server.
 	 * @param {string} response.slug         Slug of the theme to be updated.
 	 * @param {string} response.errorCode    Error code for the error that occurred.
 	 * @param {string} response.errorMessage The error that occurred.
 	 */
 	wp.updates.updateThemeError = function( response ) {
 		var $theme       = $( '[data-slug="' + response.slug + '"]' ),
-			errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage ),
+			errorMessage = sprintf(
+				/* translators: %s: Error string for a failed update. */
+				 __( 'Update failed: %s' ),
+				response.errorMessage
+			),
 			$notice;
 
 		if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
@@ -1067,7 +1249,7 @@
 			message:   errorMessage
 		} );
 
-		wp.a11y.speak( errorMessage, 'polite' );
+		wp.a11y.speak( errorMessage );
 
 		$document.trigger( 'wp-theme-update-error', response );
 	};
@@ -1077,7 +1259,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}               args
+	 * @param {Object}               args
 	 * @param {string}               args.slug    Theme stylesheet.
 	 * @param {installThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.installThemeSuccess
 	 * @param {installThemeError=}   args.error   Optional. Error callback. Default: wp.updates.installThemeError
@@ -1094,14 +1276,22 @@
 
 		$message.addClass( 'updating-message' );
 		$message.parents( '.theme' ).addClass( 'focus' );
-		if ( $message.html() !== wp.updates.l10n.installing ) {
+		if ( $message.html() !== __( 'Installing...' ) ) {
 			$message.data( 'originaltext', $message.html() );
 		}
 
 		$message
-			.text( wp.updates.l10n.installing )
-			.attr( 'aria-label', wp.updates.l10n.themeInstallingLabel.replace( '%s', $message.data( 'name' ) ) );
-		wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Theme name and version. */
+					_x( 'Installing %s...', 'theme' ),
+					$message.data( 'name' )
+				)
+			)
+			.text( __( 'Installing...' ) );
+
+		wp.a11y.speak( __( 'Installing... please wait.' ) );
 
 		// Remove previous error messages, if any.
 		$( '.install-theme-info, [data-slug="' + args.slug + '"]' ).removeClass( 'theme-install-failed' ).find( '.notice.notice-error' ).remove();
@@ -1116,7 +1306,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response              Response from the server.
+	 * @param {Object} response              Response from the server.
 	 * @param {string} response.slug         Slug of the theme to be installed.
 	 * @param {string} response.customizeUrl URL to the Customizer for the just installed theme.
 	 * @param {string} response.activateUrl  URL to activate the just installed theme.
@@ -1130,10 +1320,17 @@
 		$message = $card.find( '.button-primary' )
 			.removeClass( 'updating-message' )
 			.addClass( 'updated-message disabled' )
-			.attr( 'aria-label', wp.updates.l10n.themeInstalledLabel.replace( '%s', response.themeName ) )
-			.text( wp.updates.l10n.themeInstalled );
-
-		wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Theme name and version. */
+					_x( '%s installed!', 'theme' ),
+					response.themeName
+				)
+			)
+			.text( _x( 'Installed!', 'theme' ) );
+
+		wp.a11y.speak( __( 'Installation completed successfully.' ) );
 
 		setTimeout( function() {
 
@@ -1143,9 +1340,31 @@
 				$message
 					.attr( 'href', response.activateUrl )
 					.removeClass( 'theme-install updated-message disabled' )
-					.addClass( 'activate' )
-					.attr( 'aria-label', wp.updates.l10n.activateThemeLabel.replace( '%s', response.themeName ) )
-					.text( wp.updates.l10n.activateTheme );
+					.addClass( 'activate' );
+
+				if ( 'themes-network' === pagenow ) {
+					$message
+						.attr(
+							'aria-label',
+							sprintf(
+								/* translators: %s: Theme name. */
+								_x( 'Network Activate %s', 'theme' ),
+								response.themeName
+							)
+						)
+						.text( __( 'Network Enable' ) );
+				} else {
+					$message
+						.attr(
+							'aria-label',
+							sprintf(
+								/* translators: %s: Theme name. */
+								_x( 'Activate %s', 'theme' ),
+								response.themeName
+							)
+						)
+						.text( __( 'Activate' ) );
+				}
 			}
 
 			if ( response.customizeUrl ) {
@@ -1155,7 +1374,7 @@
 					return $( '<a>' )
 						.attr( 'href', response.customizeUrl )
 						.addClass( 'button load-customize' )
-						.text( wp.updates.l10n.livePreview );
+						.text( __( 'Live Preview' ) );
 				} );
 			}
 		}, 1000 );
@@ -1166,14 +1385,18 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response              Response from the server.
+	 * @param {Object} response              Response from the server.
 	 * @param {string} response.slug         Slug of the theme to be installed.
 	 * @param {string} response.errorCode    Error code for the error that occurred.
 	 * @param {string} response.errorMessage The error that occurred.
 	 */
 	wp.updates.installThemeError = function( response ) {
 		var $card, $button,
-			errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ),
+			errorMessage = sprintf(
+				/* translators: %s: Error string for a failed installation. */
+				__( 'Installation failed: %s' ),
+				response.errorMessage
+			),
 			$message     = wp.updates.adminNotice( {
 				className: 'update-message notice-error notice-alt',
 				message:   errorMessage
@@ -1208,8 +1431,15 @@
 
 		$button
 			.removeClass( 'updating-message' )
-			.attr( 'aria-label', wp.updates.l10n.themeInstallFailedLabel.replace( '%s', $button.data( 'name' ) ) )
-			.text( wp.updates.l10n.installFailedShort );
+			.attr(
+				'aria-label',
+				sprintf(
+					/* translators: %s: Theme name and version. */
+					_x( '%s installation failed', 'theme' ),
+					$button.data( 'name' )
+				)
+			)
+			.text( __( 'Installation failed.' ) );
 
 		wp.a11y.speak( errorMessage, 'assertive' );
 
@@ -1221,7 +1451,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object}              args
+	 * @param {Object}              args
 	 * @param {string}              args.slug    Theme stylesheet.
 	 * @param {deleteThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.deleteThemeSuccess
 	 * @param {deleteThemeError=}   args.error   Optional. Error callback. Default: wp.updates.deleteThemeError
@@ -1242,13 +1472,13 @@
 			error: wp.updates.deleteThemeError
 		}, args );
 
-		if ( $button && $button.html() !== wp.updates.l10n.deleting ) {
+		if ( $button && $button.html() !== __( 'Deleting...' ) ) {
 			$button
 				.data( 'originaltext', $button.html() )
-				.text( wp.updates.l10n.deleting );
+				.text( __( 'Deleting...' ) );
 		}
 
-		wp.a11y.speak( wp.updates.l10n.deleting, 'polite' );
+		wp.a11y.speak( __( 'Deleting...' ) );
 
 		// Remove previous error messages, if any.
 		$( '.theme-info .update-message' ).remove();
@@ -1263,7 +1493,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response      Response from the server.
+	 * @param {Object} response      Response from the server.
 	 * @param {string} response.slug Slug of the theme that was deleted.
 	 */
 	wp.updates.deleteThemeSuccess = function( response ) {
@@ -1311,7 +1541,7 @@
 			} );
 		}
 
-		wp.a11y.speak( wp.updates.l10n.themeDeleted, 'polite' );
+		wp.a11y.speak( _x( 'Deleted!', 'theme' ) );
 
 		$document.trigger( 'wp-theme-delete-success', response );
 	};
@@ -1321,7 +1551,7 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response              Response from the server.
+	 * @param {Object} response              Response from the server.
 	 * @param {string} response.slug         Slug of the theme to be deleted.
 	 * @param {string} response.errorCode    Error code for the error that occurred.
 	 * @param {string} response.errorMessage The error that occurred.
@@ -1331,7 +1561,11 @@
 			$button      = $( '.theme-actions .delete-theme' ),
 			updateRow    = wp.template( 'item-update-row' ),
 			$updateRow   = $themeRow.siblings( '#' + response.slug + '-update' ),
-			errorMessage = wp.updates.l10n.deleteFailed.replace( '%s', response.errorMessage ),
+			errorMessage = sprintf(
+				/* translators: %s: Error string for a failed deletion. */
+				__( 'Deletion failed: %s' ),
+				response.errorMessage
+			),
 			$message     = wp.updates.adminNotice( {
 				className: 'update-message notice-error notice-alt',
 				message:   errorMessage
@@ -1372,9 +1606,9 @@
 	 * @since 4.6.0
 	 * @private
 	 *
-	 * @param {object} data   AJAX payload.
+	 * @param {Object} data   Ajax payload.
 	 * @param {string} action The type of request to perform.
-	 * @return {object} The AJAX payload with the appropriate callbacks.
+	 * @return {Object} The Ajax payload with the appropriate callbacks.
 	 */
 	wp.updates._addCallbacks = function( data, action ) {
 		if ( 'import' === pagenow && 'install-plugin' === action ) {
@@ -1566,7 +1800,7 @@
 	 *
 	 * @since 4.2.0
 	 *
-	 * @param {object} response Ajax response.
+	 * @param {Object} response Ajax response.
 	 * @param {string} action   The type of request to perform.
 	 */
 	wp.updates.credentialError = function( response, action ) {
@@ -1594,11 +1828,11 @@
 	 *
 	 * @since 4.6.0
 	 *
-	 * @param {object} response              Response from the server.
+	 * @param {Object} response              Response from the server.
 	 * @param {string} response.errorCode    Error code for the error that occurred.
 	 * @param {string} response.errorMessage The error that occurred.
 	 * @param {string} action                The type of request to perform.
-	 * @returns {boolean} Whether there is an error that needs to be handled or not.
+	 * @return {boolean} Whether there is an error that needs to be handled or not.
 	 */
 	wp.updates.maybeHandleCredentialError = function( response, action ) {
 		if ( wp.updates.shouldRequestFilesystemCredentials && response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) {
@@ -1610,11 +1844,11 @@
 	};
 
 	/**
-	 * Validates an AJAX response to ensure it's a proper object.
+	 * Validates an Ajax response to ensure it's a proper object.
 	 *
 	 * If the response deems to be invalid, an admin notice is being displayed.
 	 *
-	 * @param {(object|string)} response              Response from the server.
+	 * @param {(Object|string)} response              Response from the server.
 	 * @param {function=}       response.always       Optional. Callback for when the Deferred is resolved or rejected.
 	 * @param {string=}         response.statusText   Optional. Status message corresponding to the status code.
 	 * @param {string=}         response.responseText Optional. Request response as text.
@@ -1622,8 +1856,8 @@
 	 *                                                'update' or 'install'.
 	 */
 	wp.updates.isValidResponse = function( response, action ) {
-		var error = wp.updates.l10n.unknownError,
-		    errorMessage;
+		var error = __( 'Something went wrong.' ),
+			errorMessage;
 
 		// Make sure the response is a valid data object and not a Promise object.
 		if ( _.isObject( response ) && ! _.isFunction( response.always ) ) {
@@ -1631,11 +1865,11 @@
 		}
 
 		if ( _.isString( response ) && '-1' === response ) {
-			error = wp.updates.l10n.nonceError;
+			error = __( 'An error has occurred. Please reload the page and try again.' );
 		} else if ( _.isString( response ) ) {
 			error = response;
 		} else if ( 'undefined' !== typeof response.readyState && 0 === response.readyState ) {
-			error = wp.updates.l10n.connectionError;
+			error = __( 'Connection lost or the server is busy. Please try again later.' );
 		} else if ( _.isString( response.responseText ) && '' !== response.responseText ) {
 			error = response.responseText;
 		} else if ( _.isString( response.statusText ) ) {
@@ -1644,15 +1878,18 @@
 
 		switch ( action ) {
 			case 'update':
-				errorMessage = wp.updates.l10n.updateFailed;
+				/* translators: %s: Error string for a failed update. */
+				errorMessage = __( 'Update failed: %s' );
 				break;
 
 			case 'install':
-				errorMessage = wp.updates.l10n.installFailed;
+				/* translators: %s: Error string for a failed installation. */
+				errorMessage = __( 'Installation failed: %s' );
 				break;
 
 			case 'delete':
-				errorMessage = wp.updates.l10n.deleteFailed;
+				/* translators: %s: Error string for a failed deletion. */
+				errorMessage = __( 'Deletion failed: %s' );
 				break;
 		}
 
@@ -1676,7 +1913,7 @@
 			.removeClass( 'updating-message' )
 			.removeAttr( 'aria-label' )
 			.prop( 'disabled', true )
-			.text( wp.updates.l10n.updateFailedShort );
+			.text( __( 'Update failed.' ) );
 
 		$( '.updating-message:not(.button):not(.thickbox)' )
 			.removeClass( 'updating-message notice-warning' )
@@ -1700,7 +1937,7 @@
 	 */
 	wp.updates.beforeunload = function() {
 		if ( wp.updates.ajaxLocked ) {
-			return wp.updates.l10n.beforeunload;
+			return __( 'Updates may not complete if you navigate away from this page.' );
 		}
 	};
 
@@ -1813,14 +2050,28 @@
 
 				if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
 					if ( 'update-plugin' === job.action ) {
-						$message.attr( 'aria-label', wp.updates.l10n.pluginUpdateNowLabel.replace( '%s', $message.data( 'name' ) ) );
+						$message.attr(
+							'aria-label',
+							sprintf(
+								/* translators: %s: Plugin name and version. */
+								_x( 'Update %s now', 'plugin' ),
+								$message.data( 'name' )
+							)
+						);
 					} else if ( 'install-plugin' === job.action ) {
-						$message.attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', $message.data( 'name' ) ) );
+						$message.attr(
+							'aria-label',
+							sprintf(
+								/* translators: %s: Plugin name. */
+								_x( 'Install %s now', 'plugin' ),
+								$message.data( 'name' )
+							)
+						);
 					}
 				}
 			}
 
-			wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
+			wp.a11y.speak( __( 'Update canceled.' ) );
 		} );
 
 		/**
@@ -1896,9 +2147,9 @@
 
 					$message
 						.removeClass( 'updating-message' )
-						.text( wp.updates.l10n.installNow );
-
-					wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
+						.text( __( 'Install Now' ) );
+
+					wp.a11y.speak( __( 'Update canceled.' ) );
 				} );
 			}
 
@@ -1931,10 +2182,17 @@
 
 					$button
 						.removeClass( 'updating-message' )
-						.text( wp.updates.l10n.installNow )
-						.attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', pluginName ) );
-
-					wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
+						.attr(
+							'aria-label',
+							sprintf(
+								/* translators: %s: Plugin name. */
+								_x( 'Install %s now', 'plugin' ),
+								pluginName
+							)
+						)
+						.text( __( 'Install Now' ) );
+
+					wp.a11y.speak( __( 'Update canceled.' ) );
 				} );
 			}
 
@@ -1954,11 +2212,26 @@
 		 * @param {Event} event Event interface.
 		 */
 		$bulkActionForm.on( 'click', '[data-plugin] a.delete', function( event ) {
-			var $pluginRow = $( event.target ).parents( 'tr' );
+			var $pluginRow = $( event.target ).parents( 'tr' ),
+				confirmMessage;
+
+			if ( $pluginRow.hasClass( 'is-uninstallable' ) ) {
+				confirmMessage = sprintf(
+					/* translators: %s: Plugin name. */
+					__( 'Are you sure you want to delete %s and its data?' ),
+					$pluginRow.find( '.plugin-title strong' ).text()
+				);
+			} else {
+				confirmMessage = sprintf(
+					/* translators: %s: Plugin name. */
+					__( 'Are you sure you want to delete %s?' ),
+					$pluginRow.find( '.plugin-title strong' ).text()
+				);
+			}
 
 			event.preventDefault();
 
-			if ( ! window.confirm( wp.updates.l10n.aysDeleteUninstall.replace( '%s', $pluginRow.find( '.plugin-title strong' ).text() ) ) ) {
+			if ( ! window.confirm( confirmMessage ) ) {
 				return;
 			}
 
@@ -2005,11 +2278,16 @@
 		 * @param {Event} event Event interface.
 		 */
 		$document.on( 'click', '.themes-php.network-admin a.delete', function( event ) {
-			var $themeRow = $( event.target ).parents( 'tr' );
+			var $themeRow = $( event.target ).parents( 'tr' ),
+				confirmMessage = sprintf(
+					/* translators: %s: Theme name. */
+					__( 'Are you sure you want to delete %s?' ),
+					$themeRow.find( '.theme-title strong' ).text()
+				);
 
 			event.preventDefault();
 
-			if ( ! window.confirm( wp.updates.l10n.aysDelete.replace( '%s', $themeRow.find( '.theme-title strong' ).text() ) ) ) {
+			if ( ! window.confirm( confirmMessage ) ) {
 				return;
 			}
 
@@ -2060,7 +2338,7 @@
 				return wp.updates.addAdminNotice( {
 					id:        'no-items-selected',
 					className: 'notice-error is-dismissible',
-					message:   wp.updates.l10n.noItemsSelected
+					message:   __( 'Please select at least one item to perform this action on.' )
 				} );
 			}
 
@@ -2071,7 +2349,11 @@
 					break;
 
 				case 'delete-selected':
-					if ( ! window.confirm( 'plugin' === type ? wp.updates.l10n.aysBulkDelete : wp.updates.l10n.aysBulkDeleteThemes ) ) {
+					var confirmMessage = 'plugin' === type ?
+						__( 'Are you sure you want to delete the selected plugins and their data?' ) :
+						__( 'Caution: These themes may be active on other sites in the network. Are you sure you want to proceed?' );
+
+					if ( ! window.confirm( confirmMessage ) ) {
 						event.preventDefault();
 						return;
 					}
@@ -2208,7 +2490,7 @@
 					.append( $( '<a />', {
 						'class': 'current',
 						'href': searchLocation,
-						'text': wp.updates.l10n.searchResultsLabel
+						'text': __( 'Search Results' )
 					} ) );
 
 				$( '.wp-filter .filter-links .current' )
@@ -2231,12 +2513,18 @@
 				delete wp.updates.searchRequest;
 
 				if ( 0 === response.count ) {
-					wp.a11y.speak( wp.updates.l10n.noPluginsFound );
+					wp.a11y.speak( __( 'You do not appear to have any plugins available at this time.' ) );
 				} else {
-					wp.a11y.speak( wp.updates.l10n.pluginsFound.replace( '%d', response.count ) );
+					wp.a11y.speak(
+						sprintf(
+							/* translators: %s: Number of plugins. */
+							__( 'Number of plugins found: %d' ),
+							response.count
+						)
+					);
 				}
 			} );
-		}, 500 ) );
+		}, 1000 ) );
 
 		if ( $pluginSearch.length ) {
 			$pluginSearch.attr( 'aria-describedby', 'live-search-desc' );
@@ -2289,7 +2577,12 @@
 			wp.updates.searchRequest = wp.ajax.post( 'search-plugins', data ).done( function( response ) {
 
 				// Can we just ditch this whole subtitle business?
-				var $subTitle    = $( '<span />' ).addClass( 'subtitle' ).html( wp.updates.l10n.searchResults.replace( '%s', _.escape( data.s ) ) ),
+				var $subTitle    = $( '<span />' ).addClass( 'subtitle' ).html(
+					sprintf(
+						/* translators: %s: Search query. */
+						__( 'Search results for &#8220;%s&#8221;' ),
+						_.escape( data.s )
+					) ),
 					$oldSubTitle = $( '.wrap .subtitle' );
 
 				if ( ! data.s.length ) {
@@ -2306,9 +2599,15 @@
 				delete wp.updates.searchRequest;
 
 				if ( 0 === response.count ) {
-					wp.a11y.speak( wp.updates.l10n.noPluginsFound );
+					wp.a11y.speak( __( 'No plugins found. Try a different search.'  ) );
 				} else {
-					wp.a11y.speak( wp.updates.l10n.pluginsFound.replace( '%d', response.count ) );
+					wp.a11y.speak(
+						sprintf(
+							/* translators: %s: Number of plugins. */
+							__( 'Number of plugins found: %d' ),
+							response.count
+						)
+					);
 				}
 			} );
 		}, 500 ) );
@@ -2416,7 +2715,7 @@
 		 */
 		$( window ).on( 'message', function( event ) {
 			var originalEvent  = event.originalEvent,
-				expectedOrigin = document.location.protocol + '//' + document.location.hostname,
+				expectedOrigin = document.location.protocol + '//' + document.location.host,
 				message;
 
 			if ( originalEvent.origin !== expectedOrigin ) {
@@ -2461,5 +2760,171 @@
 		 * @since 4.2.0
 		 */
 		$( window ).on( 'beforeunload', wp.updates.beforeunload );
+
+		/**
+		 * Prevents the page form scrolling when activating auto-updates with the Spacebar key.
+		 *
+		 * @since 5.5.0
+		 */
+		$document.on( 'keydown', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) {
+			if ( 32 === event.which ) {
+				event.preventDefault();
+			}
+		} );
+
+		/**
+		 * Click and keyup handler for enabling and disabling plugin and theme auto-updates.
+		 *
+		 * These controls can be either links or buttons. When JavaScript is enabled,
+		 * we want them to behave like buttons. An ARIA role `button` is added via
+		 * the JavaScript that targets elements with the CSS class `aria-button-if-js`.
+		 *
+		 * @since 5.5.0
+		 */
+		$document.on( 'click keyup', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) {
+			var data, asset, type, $parent,
+				$toggler = $( this ),
+				action = $toggler.attr( 'data-wp-action' ),
+				$label = $toggler.find( '.label' );
+
+			if ( 'keyup' === event.type && 32 !== event.which ) {
+				return;
+			}
+
+			if ( 'themes' !== pagenow ) {
+				$parent = $toggler.closest( '.column-auto-updates' );
+			} else {
+				$parent = $toggler.closest( '.theme-autoupdate' );
+			}
+
+			event.preventDefault();
+
+			// Prevent multiple simultaneous requests.
+			if ( $toggler.attr( 'data-doing-ajax' ) === 'yes' ) {
+				return;
+			}
+
+			$toggler.attr( 'data-doing-ajax', 'yes' );
+
+			switch ( pagenow ) {
+				case 'plugins':
+				case 'plugins-network':
+					type = 'plugin';
+					asset = $toggler.closest( 'tr' ).attr( 'data-plugin' );
+					break;
+				case 'themes-network':
+					type = 'theme';
+					asset = $toggler.closest( 'tr' ).attr( 'data-slug' );
+					break;
+				case 'themes':
+					type = 'theme';
+					asset = $toggler.attr( 'data-slug' );
+					break;
+			}
+
+			// Clear any previous errors.
+			$parent.find( '.notice.notice-error' ).addClass( 'hidden' );
+
+			// Show loading status.
+			if ( 'enable' === action ) {
+				$label.text( __( 'Enabling...' ) );
+			} else {
+				$label.text( __( 'Disabling...' ) );
+			}
+
+			$toggler.find( '.dashicons-update' ).removeClass( 'hidden' );
+
+			data = {
+				action: 'toggle-auto-updates',
+				_ajax_nonce: settings.ajax_nonce,
+				state: action,
+				type: type,
+				asset: asset
+			};
+
+			$.post( window.ajaxurl, data )
+				.done( function( response ) {
+					var $enabled, $disabled, enabledNumber, disabledNumber, errorMessage,
+						href = $toggler.attr( 'href' );
+
+					if ( ! response.success ) {
+						// if WP returns 0 for response (which can happen in a few cases),
+						// output the general error message since we won't have response.data.error.
+						if ( response.data && response.data.error ) {
+							errorMessage = response.data.error;
+						} else {
+							errorMessage = __( 'The request could not be completed.' );
+						}
+
+						$parent.find( '.notice.notice-error' ).removeClass( 'hidden' ).find( 'p' ).text( errorMessage );
+						wp.a11y.speak( errorMessage, 'assertive' );
+						return;
+					}
+
+					// Update the counts in the enabled/disabled views if on a screen
+					// with a list table.
+					if ( 'themes' !== pagenow ) {
+						$enabled       = $( '.auto-update-enabled span' );
+						$disabled      = $( '.auto-update-disabled span' );
+						enabledNumber  = parseInt( $enabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0;
+						disabledNumber = parseInt( $disabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0;
+
+						switch ( action ) {
+							case 'enable':
+								++enabledNumber;
+								--disabledNumber;
+								break;
+							case 'disable':
+								--enabledNumber;
+								++disabledNumber;
+								break;
+						}
+
+						enabledNumber = Math.max( 0, enabledNumber );
+						disabledNumber = Math.max( 0, disabledNumber );
+
+						$enabled.text( '(' + enabledNumber + ')' );
+						$disabled.text( '(' + disabledNumber + ')' );
+					}
+
+					if ( 'enable' === action ) {
+						// The toggler control can be either a link or a button.
+						if ( $toggler[ 0 ].hasAttribute( 'href' ) ) {
+							href = href.replace( 'action=enable-auto-update', 'action=disable-auto-update' );
+							$toggler.attr( 'href', href );
+						}
+						$toggler.attr( 'data-wp-action', 'disable' );
+
+						$label.text( __( 'Disable auto-updates' ) );
+						$parent.find( '.auto-update-time' ).removeClass( 'hidden' );
+						wp.a11y.speak( __( 'Auto-updates enabled' ) );
+					} else {
+						// The toggler control can be either a link or a button.
+						if ( $toggler[ 0 ].hasAttribute( 'href' ) ) {
+							href = href.replace( 'action=disable-auto-update', 'action=enable-auto-update' );
+							$toggler.attr( 'href', href );
+						}
+						$toggler.attr( 'data-wp-action', 'enable' );
+
+						$label.text( __( 'Enable auto-updates' ) );
+						$parent.find( '.auto-update-time' ).addClass( 'hidden' );
+						wp.a11y.speak( __( 'Auto-updates disabled' ) );
+					}
+
+					$document.trigger( 'wp-auto-update-setting-changed', { state: action, type: type, asset: asset } );
+				} )
+				.fail( function() {
+					$parent.find( '.notice.notice-error' )
+						.removeClass( 'hidden' )
+						.find( 'p' )
+						.text( __( 'The request could not be completed.' ) );
+
+					wp.a11y.speak( __( 'The request could not be completed.' ), 'assertive' );
+				} )
+				.always( function() {
+					$toggler.removeAttr( 'data-doing-ajax' ).find( '.dashicons-update' ).addClass( 'hidden' );
+				} );
+			}
+		);
 	} );
 })( jQuery, window.wp, window._wpUpdatesSettings );