Index: src/js/_enqueues/wp/customize/controls.js
--- src/js/_enqueues/wp/customize/controls.js
+++ src/js/_enqueues/wp/customize/controls.js
@@ -2,7 +2,7 @@
  * @output wp-admin/js/customize-controls.js
  */
 
-/* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer, console, confirm */
+/* global _, _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer, console, confirm */
 (function( exports, $ ){
 	var Container, focus, normalizedTransitionendEventName, api = wp.customize;
 
@@ -2135,7 +2135,16 @@
 							}
 						});
 						if ( 'local' !== section.params.filter_type ) {
-							wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) );
+							wp.a11y.speak(
+								wp.i18n.sprintf(
+									wp.i18n._n(
+										'%d theme found',
+										'%d themes found',
+										data.info.results
+									),
+									data.info.results
+								)
+							);
 						}
 					}
 
@@ -2439,28 +2448,45 @@
 		 *
 		 * @since 4.9.0
 		 *
+		 * @param {number} count New theme count.
 		 * @return {void}
 		 */
 		updateCount: function( count ) {
-			var section = this, countEl, displayed;
+			var section = this, i18n = wp.i18n, countHtml, displayed;
 
 			if ( ! count && 0 !== count ) {
 				count = section.getVisibleCount();
 			}
 
 			displayed = section.contentContainer.find( '.themes-displayed' );
-			countEl = section.contentContainer.find( '.theme-count' );
+			countHtml = i18n.sprintf(
+				i18n._n(
+					'%s theme',
+					'%s themes',
+					count
+				),
+				'<span class="theme-count">' + count + '</span>'
+			);
 
 			if ( 0 === count ) {
-				countEl.text( '0' );
+				displayed.html( countHtml );
 			} else {
 
 				// Animate the count change for emphasis.
 				displayed.fadeOut( 180, function() {
-					countEl.text( count );
+					displayed.html( countHtml );
 					displayed.fadeIn( 180 );
 				} );
-				wp.a11y.speak( api.settings.l10n.announceThemeCount.replace( '%d', count ) );
+				wp.a11y.speak(
+					i18n.sprintf(
+						i18n._n(
+							'Displaying %d theme',
+							'Displaying %d themes',
+							count
+						),
+						count
+					)
+				);
 			}
 		},
 
@@ -5516,11 +5542,15 @@
 			control.setting.notifications.remove( 'csslint_error' );
 
 			if ( 0 !== errorAnnotations.length ) {
-				if ( 1 === errorAnnotations.length ) {
-					message = api.l10n.customCssError.singular.replace( '%d', '1' );
-				} else {
-					message = api.l10n.customCssError.plural.replace( '%d', String( errorAnnotations.length ) );
-				}
+				message = wp.i18n.sprintf(
+					wp.i18n._n(
+						'There is %d error which must be fixed before you can save.',
+						'There are %d errors which must be fixed before you can save.',
+						errorAnnotations.length
+					),
+					errorAnnotations.length
+				);
+
 				control.setting.notifications.add( new api.Notification( 'csslint_error', {
 					message: message,
 					type: 'error'
@@ -7543,7 +7573,14 @@
 
 						if ( invalidSettings.length ) {
 							api.notifications.add( new api.Notification( errorCode, {
-								message: ( 1 === invalidSettings.length ? api.l10n.saveBlockedError.singular : api.l10n.saveBlockedError.plural ).replace( /%s/g, String( invalidSettings.length ) ),
+								message: wp.i18n.sprintf(
+									wp.i18n._n(
+										'Unable to save due to %s invalid setting.',
+										'Unable to save due to %s invalid settings.',
+										invalidSettings.length
+									),
+									invalidSettings.length
+								),
 								type: 'error',
 								dismissible: true,
 								saveFailure: true
Index: src/wp-includes/class-wp-customize-manager.php
--- src/wp-includes/class-wp-customize-manager.php
+++ src/wp-includes/class-wp-customize-manager.php
@@ -4947,15 +4947,17 @@
 			'previewableDevices'     => $this->get_previewable_devices(),
 			'l10n'                   => array(
 				'confirmDeleteTheme'   => __( 'Are you sure you want to delete this theme?' ),
-				/* translators: %d: Number of theme search results, which cannot currently consider singular vs. plural forms. */
-				'themeSearchResults'   => __( '%d themes found' ),
-				/* translators: %d: Number of themes being displayed, which cannot currently consider singular vs. plural forms. */
-				'announceThemeCount'   => __( 'Displaying %d themes' ),
 				/* translators: %s: Theme name. */
 				'announceThemeDetails' => __( 'Showing details for theme: %s' ),
 			),
 		);
 
+		// These strings are here for backwards compatibility; the translations now occur in JavaScript.
+		/* translators: %d: Number of theme search results. Note there is a newer translation of this string with singular and plural forms. */
+		$settings['l10n']['themeSearchResults'] = __( '%d themes found' );
+		/* translators: %d: Number of themes being displayed. Note there is a newer translation of this string with singular and plural forms. */
+		$settings['l10n']['announceThemeCount'] = __( 'Displaying %d themes' );
+
 		// Temporarily disable installation in Customizer. See #42184.
 		$filesystem_method = get_filesystem_method();
 		ob_start();
Index: src/wp-includes/customize/class-wp-customize-themes-section.php
--- src/wp-includes/customize/class-wp-customize-themes-section.php
+++ src/wp-includes/customize/class-wp-customize-themes-section.php
@@ -166,7 +166,7 @@
 				<span class="themes-displayed">
 					<?php
 					/* translators: %s: Number of themes displayed. */
-					printf( __( '%s themes' ), '<span class="theme-count">0</span>' );
+					printf( _n( '%s theme', '%s themes', 0 ), '<span class="theme-count">0</span>' );
 					?>
 				</span>
 			</div>
Index: src/wp-includes/script-loader.php
--- src/wp-includes/script-loader.php
+++ src/wp-includes/script-loader.php
@@ -1283,7 +1283,7 @@
 	$scripts->add( 'customize-preview', "/wp-includes/js/customize-preview$suffix.js", array( 'wp-a11y', 'customize-base' ), false, 1 );
 	$scripts->add( 'customize-models', '/wp-includes/js/customize-models.js', array( 'underscore', 'backbone' ), false, 1 );
 	$scripts->add( 'customize-views', '/wp-includes/js/customize-views.js', array( 'jquery', 'underscore', 'imgareaselect', 'customize-models', 'media-editor', 'media-views' ), false, 1 );
-	$scripts->add( 'customize-controls', "/wp-admin/js/customize-controls$suffix.js", array( 'customize-base', 'wp-a11y', 'wp-util', 'jquery-ui-core' ), false, 1 );
+	$scripts->add( 'customize-controls', "/wp-admin/js/customize-controls$suffix.js", array( 'customize-base', 'wp-a11y', 'wp-i18n', 'wp-util', 'jquery-ui-core' ), false, 1 );
 	did_action( 'init' ) && $scripts->localize(
 		'customize-controls',
 		'_wpCustomizeControlsL10n',
@@ -1326,31 +1326,30 @@
 			'videoHeaderNotice'       => __( 'This theme does not support video headers on this page. Navigate to the front page or another page that supports video headers.' ),
 			// Used for overriding the file types allowed in Plupload.
 			'allowedFiles'            => __( 'Allowed Files' ),
+			'pageOnFrontError'        => __( 'Homepage and posts page must be different.' ),
+			'scheduleDescription'     => __( 'Schedule your customization changes to publish ("go live") at a future date.' ),
+			'themePreviewUnavailable' => __( 'Sorry, you cannot preview new themes when you have changes scheduled or saved as a draft. Please publish your changes, or wait until they publish to preview new themes.' ),
+			'themeInstallUnavailable' => sprintf(
+				/* translators: %s: URL to Add Themes admin screen. */
+				__( 'You will not be able to install new themes from here yet since your install requires SFTP credentials. For now, please <a href="%s">add themes in the admin</a>.' ),
+				esc_url( admin_url( 'theme-install.php' ) )
+			),
+			'publishSettings'         => __( 'Publish Settings' ),
+			'invalidDate'             => __( 'Invalid date.' ),
+			'invalidValue'            => __( 'Invalid value.' ),
+			// These strings are here for backwards compatibility; the translations now occur in JavaScript.
 			'customCssError'          => array(
 				/* translators: %d: Error count. */
 				'singular' => _n( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.', 1 ),
 				/* translators: %d: Error count. */
 				'plural'   => _n( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.', 2 ),
-				// @todo This is lacking, as some languages have a dedicated dual form. For proper handling of plurals in JS, see #20491.
 			),
-			'pageOnFrontError'        => __( 'Homepage and posts page must be different.' ),
 			'saveBlockedError'        => array(
 				/* translators: %s: Number of invalid settings. */
 				'singular' => _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', 1 ),
 				/* translators: %s: Number of invalid settings. */
 				'plural'   => _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', 2 ),
-				// @todo This is lacking, as some languages have a dedicated dual form. For proper handling of plurals in JS, see #20491.
 			),
-			'scheduleDescription'     => __( 'Schedule your customization changes to publish ("go live") at a future date.' ),
-			'themePreviewUnavailable' => __( 'Sorry, you cannot preview new themes when you have changes scheduled or saved as a draft. Please publish your changes, or wait until they publish to preview new themes.' ),
-			'themeInstallUnavailable' => sprintf(
-				/* translators: %s: URL to Add Themes admin screen. */
-				__( 'You will not be able to install new themes from here yet since your install requires SFTP credentials. For now, please <a href="%s">add themes in the admin</a>.' ),
-				esc_url( admin_url( 'theme-install.php' ) )
-			),
-			'publishSettings'         => __( 'Publish Settings' ),
-			'invalidDate'             => __( 'Invalid date.' ),
-			'invalidValue'            => __( 'Invalid value.' ),
 			'blockThemeNotification'  => sprintf(
 				/* translators: 1: Link to Site Editor documentation on HelpHub, 2: HTML button. */
 				__( 'Hurray! Your theme supports site editing with blocks. <a href="%1$s">Tell me more</a>. %2$s' ),
