Index: /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/fields/plugin/class-screenshots.php
===================================================================
--- /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/fields/plugin/class-screenshots.php	(revision 8728)
+++ /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/fields/plugin/class-screenshots.php	(revision 8729)
@@ -9,5 +9,5 @@
 
 	/**
-	 * Banners constructor.
+	 * Screenshots constructor.
 	 */
 	public function __construct() {
@@ -28,22 +28,14 @@
 	 */
 	public function get_screenshots( $object ) {
-		$descriptions = get_post_meta( $object['id'], 'screenshots', true ) ?: [];
-		$screen_shots = get_post_meta( $object['id'], 'assets_screenshots', true ) ?: [];
-		$response     = [];
-
-		foreach ( $screen_shots as $image ) {
-			$screen_shot = [
-				'src' => esc_url( Template::get_asset_url( $object['id'], $image ) ),
-			];
-
-			if ( $descriptions && ! empty( $descriptions[ (int) $image['resolution'] ] ) ) {
-				$caption                = $descriptions[ (int) $image['resolution'] ];
-				$screen_shot['caption'] = Plugin_I18n::instance()->translate( 'screenshot-' . $image['resolution'], $caption, [ 'post_id' => $object['id'] ] );
-			}
-
-			$response[] = $screen_shot;
-		}
-
-		return $response;
+		// Reduce the Screenshots to the required fields.
+		return array_values( array_map(
+			function( $image ) {
+				return [
+					'src'     => $image['src'],
+					'caption' => $image['caption'],
+				];
+			},
+			Template::get_screenshots( $object['id'] )
+		) );
 	}
 }
Index: /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin.php
===================================================================
--- /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin.php	(revision 8728)
+++ /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin.php	(revision 8729)
@@ -155,22 +155,14 @@
 		$result['download_link']     = Template::download_link( $post );
 
-		$result['screenshots'] = array();
-		$descriptions          = get_post_meta( $post->ID, 'screenshots', true ) ?: array();
-		$screen_shots          = get_post_meta( $post->ID, 'assets_screenshots', true ) ?: array();
-
-		/*
-		 * Find the image that corresponds with the text.
-		 * The image numbers are stored within the 'resolution' key.
-		 */
-		foreach ( $screen_shots as $image ) {
-			$src     = Template::get_asset_url( $post, $image );
-			$caption = '';
-			if ( $descriptions && ! empty( $descriptions[ (int) $image['resolution'] ] ) ) {
-				$caption = $descriptions[ (int) $image['resolution'] ];
-				$caption = Plugin_I18n::instance()->translate( 'screenshot-' . $image['resolution'], $caption, [ 'post_id' => $post->ID ] );
-			}
-
-			$result['screenshots'][ $image['resolution'] ] = compact( 'src', 'caption' );
-		}
+		// Reduce images to caption + src
+		$result['screenshots'] = array_map(
+			function( $image ) {
+				return [
+					'src'     => $image['src'],
+					'caption' => $image['caption'],
+				];
+			},
+			Template::get_screenshots( $post )
+		);
 
 		if ( $result['screenshots'] ) {
Index: /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php
===================================================================
--- /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php	(revision 8728)
+++ /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php	(revision 8729)
@@ -365,5 +365,5 @@
 			'developers',
 		);
-		if ( ! get_post_meta( $plugin->ID, 'screenshots', true ) && ! get_post_meta( $plugin->ID, 'assets_screenshots', true ) ) {
+		if ( ! get_post_meta( $plugin->ID, 'assets_screenshots', true ) ) {
 			unset( $default_sections[ array_search( 'screenshots', $default_sections ) ] );
 		}
@@ -980,3 +980,85 @@
 		return $link;
 	}
+
+	/**
+	 * Fetch plugin Screenshots, accounting for localised screenshots.
+	 *
+	 * @static
+	 *
+	 * @param object|int $plugin The plugin to fetch screenshots for. Optional.
+	 * @param string     $locale The locale requested. Optional.
+	 * @return array Screenshots for the plugin, localised if possible.
+	 */
+	public static function get_screenshots( $plugin = null, $locale = null ) {
+		$plugin = get_post( $plugin );
+
+		if ( ! $locale ) {
+			$locale = get_locale();
+		}
+
+		// All indexed from 1. The Image 'number' is stored in the 'resolution' key
+		$screen_shots = get_post_meta( $plugin->ID, 'assets_screenshots', true ) ?: array();
+		$descriptions = get_post_meta( $plugin->ID, 'screenshots', true ) ?: array();
+
+		if ( empty( $screen_shots ) ) {
+			return array();
+		}
+
+		$sorted = array();
+		foreach ( $screen_shots as $image ) {
+			if ( ! isset( $sorted[ $image['resolution'] ] ) ) {
+				$sorted[ $image['resolution'] ] = array();
+			}
+
+			if ( empty( $image['locale'] ) ) {
+				// if the image has no locale, always insert to the last element (lowerst priority).
+				$sorted[ $image['resolution'] ][] = $image;
+			} elseif ( $locale === $image['locale'] ) {
+				// if the locale is a full match, always insert to the first element (highest priority).
+				array_unshift( $sorted[ $image['resolution'] ], $image );
+			} else {
+				// TODO: de_DE_informal should probably fall back to de_DE before de_CH. Maybe this can wait until Core properly supports locale hierarchy.
+
+				$image_locale_parts = explode( '_', $image['locale'] );
+				$locale_parts       = explode( '_', $locale );
+				// if only the language matches.
+				if ( $image_locale_parts[0] === $locale_parts[0] ) {
+					// image with locale has a higher priority than image without locale.
+					$last_image = end( $sorted[ $image['resolution'] ] );
+					if ( empty( $last_image['locale'] ) ) {
+						array_splice( $sorted[ $image['resolution'] ], count( $sorted[ $image['resolution'] ] ), 0, array( $image ) );
+					} else {
+						$sorted[ $image['resolution'] ][] = $image;
+					}
+				}
+			}
+		}
+
+		// Sort
+		ksort( $sorted, SORT_NATURAL );
+
+		// Reduce images to singulars and attach metadata
+		foreach ( $sorted as $index => $items ) {
+			// The highest priority image is the first.
+			$image = $items[0];
+
+			// Attach caption data
+			$image['caption'] = false;
+			if ( isset( $descriptions[ (int) $index ] ) ) {
+				$image['caption'] = $descriptions[ (int) $index ];
+				$image['caption'] = Plugin_I18n::instance()->translate(
+					'screenshot-' . $image['resolution'],
+					$image['caption'],
+					[ 'post_id' => $plugin->ID ]
+				);
+			}
+
+			// Attach URL information for the asset
+			$image['src'] = Template::get_asset_url( $plugin, $image );
+
+			$sorted[ $index ] = $image;
+		}
+
+		return $sorted;
+	}
 }
Index: /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php
===================================================================
--- /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php	(revision 8728)
+++ /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php	(revision 8729)
@@ -365,6 +365,6 @@
 		if ( $svn_assets_folder ) { // /assets/ may not exist.
 			foreach ( $svn_assets_folder as $asset ) {
-				// screenshot-0(-rtl).(png|jpg|jpeg|gif)  ||  icon.svg
-				if ( ! preg_match( '!^(?P<type>screenshot|banner|icon)(-(?P<resolution>[\dx]+)(-rtl)?\.(png|jpg|jpeg|gif)|\.svg)$!i', $asset['filename'], $m ) ) {
+				// screenshot-0(-rtl)(-de_DE).(png|jpg|jpeg|gif)  ||  icon.svg
+				if ( ! preg_match( '!^(?P<type>screenshot|banner|icon)(?:-(?P<resolution>[\dx]+)(-rtl)?(?:-(?P<locale>[a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?))?\.(png|jpg|jpeg|gif)|\.svg)$!i', $asset['filename'], $m ) ) {
 					continue;
 				}
@@ -375,6 +375,7 @@
 				$location   = 'assets';
 				$resolution = isset( $m['resolution'] ) ? $m['resolution'] : false;
-
-				$assets[ $type ][ $asset['filename'] ] = compact( 'filename', 'revision', 'resolution', 'location' );
+				$locale     = isset( $m['locale'] )     ? $m['locale']     : false;
+
+				$assets[ $type ][ $asset['filename'] ] = compact( 'filename', 'revision', 'resolution', 'location', 'locale' );
 			}
 		}
Index: /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-screenshots.php
===================================================================
--- /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-screenshots.php	(revision 8728)
+++ /sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-screenshots.php	(revision 8729)
@@ -16,32 +16,20 @@
 	 */
 	static function display() {
-		$plugin = get_post();
 		$output = '';
 
-		// All indexed from 1.
-		$descriptions = get_post_meta( $plugin->ID, 'screenshots', true ) ?: array();
-		$screen_shots = get_post_meta( $plugin->ID, 'assets_screenshots', true ) ?: array();
+		$screenshots = Template::get_screenshots();
 
-		if ( empty( $screen_shots ) ) {
+		if ( ! $screenshots ) {
 			return '';
 		}
 
-		ksort( $screen_shots, SORT_NATURAL );
-
-		/*
-		 * Find the image that corresponds with the text.
-		 * The image numbers are stored within the 'resolution' key.
-		 */
-		foreach ( $screen_shots as $image ) {
+		foreach ( $screenshots as $image ) {
 			$screen_shot = sprintf(
 				'<a href="%1$s" rel="nofollow"><img class="screenshot" src="%1$s" alt="" /></a>',
-				esc_url( Template::get_asset_url( $plugin, $image ) )
+				esc_url( $image['src'] )
 			);
 
-			if ( $descriptions && ! empty( $descriptions[ (int) $image['resolution'] ] ) ) {
-				$caption = $descriptions[ (int) $image['resolution'] ];
-				$caption = Plugin_I18n::instance()->translate( 'screenshot-' . $image['resolution'], $caption, [ 'post_id' => $plugin->ID ] );
-
-				$screen_shot .= '<figcaption>' . $caption . '</figcaption>';
+			if ( $image['caption'] ) {
+				$screen_shot .= '<figcaption>' . $image['caption'] . '</figcaption>';
 			}
 
