@@ -149,6 +149,9 @@ public function check_update( $args, $assoc_args ) {
149149 * [--extract]
150150 * : Whether to extract the downloaded file. Defaults to true.
151151 *
152+ * [--skip-locale-check]
153+ * : If specified, allows downloading an older version of WordPress when the requested locale is not available for the latest release.
154+ *
152155 * ## EXAMPLES
153156 *
154157 * $ wp core download --locale=nl_NL
@@ -160,7 +163,7 @@ public function check_update( $args, $assoc_args ) {
160163 * @when before_wp_load
161164 *
162165 * @param array{0?: string} $args Positional arguments.
163- * @param array{path?: string, locale?: string, version?: string, 'skip-content'?: bool, force?: bool, insecure?: bool, extract?: bool} $assoc_args Associative arguments.
166+ * @param array{path?: string, locale?: string, version?: string, 'skip-content'?: bool, force?: bool, insecure?: bool, extract?: bool, 'skip-locale-check'?: bool } $assoc_args Associative arguments.
164167 */
165168 public function download ( $ args , $ assoc_args ) {
166169 /**
@@ -233,14 +236,22 @@ public function download( $args, $assoc_args ) {
233236
234237 $ download_url = $ this ->get_download_url ( $ version , $ locale , $ extension );
235238 } else {
239+ $ wp_org_api = new WpOrgApi ( [ 'insecure ' => $ insecure ] );
236240 try {
237- $ offer = ( new WpOrgApi ( [ 'insecure ' => $ insecure ] ) )
238- ->get_core_download_offer ( $ locale );
241+ $ offer = $ wp_org_api ->get_core_download_offer ( $ locale );
239242 } catch ( Exception $ exception ) {
240243 WP_CLI ::error ( $ exception );
241244 }
242245 if ( ! $ offer ) {
243- WP_CLI ::error ( "The requested locale ( {$ locale }) was not found. " );
246+ if ( Utils \get_flag_value ( $ assoc_args , 'skip-locale-check ' , false ) ) {
247+ $ offer = $ this ->find_latest_offer_for_locale ( $ locale , $ insecure );
248+ if ( is_array ( $ offer ) ) {
249+ WP_CLI ::warning ( "The latest WordPress version is not yet available in the {$ locale } locale. Downloading version {$ offer ['current ' ]} instead. " );
250+ }
251+ }
252+ if ( ! $ offer ) {
253+ WP_CLI ::error ( "The requested locale ( {$ locale }) was not found. " );
254+ }
244255 }
245256 $ version = $ offer ['current ' ];
246257 $ download_url = $ offer ['download ' ];
@@ -1671,6 +1682,57 @@ private function get_download_url( $version, $locale = 'en_US', $file_type = 'zi
16711682 return "https:// {$ locale_subdomain }wordpress.org/wordpress- {$ version }{$ locale_suffix }. {$ file_type }" ;
16721683 }
16731684
1685+ /**
1686+ * Finds the latest available WordPress download offer for a given locale by consulting
1687+ * the WordPress.org translations API.
1688+ *
1689+ * Used as a fallback when the primary version-check API does not return an offer for
1690+ * the requested locale (e.g., when a new WordPress release hasn't been translated yet).
1691+ *
1692+ * @param string $locale Locale to find an offer for.
1693+ * @param bool $insecure Whether to disable SSL verification.
1694+ * @return array{current: string, download: string}|false Offer array on success, false on failure.
1695+ */
1696+ private function find_latest_offer_for_locale ( $ locale , $ insecure ) {
1697+ $ headers = [ 'Accept ' => 'application/json ' ];
1698+ $ options = [
1699+ 'timeout ' => 30 ,
1700+ 'insecure ' => $ insecure ,
1701+ ];
1702+
1703+ try {
1704+ /** @var \WpOrg\Requests\Response $response */
1705+ $ response = Utils \http_request ( 'GET ' , 'https://api.wordpress.org/translations/core/1.0/ ' , null , $ headers , $ options );
1706+ } catch ( Exception $ exception ) {
1707+ return false ;
1708+ }
1709+
1710+ if ( $ response ->status_code < 200 || $ response ->status_code >= 300 ) {
1711+ return false ;
1712+ }
1713+
1714+ /** @var array{translations: array<int, array{language: string, version: string}>}|null $body */
1715+ $ body = json_decode ( $ response ->body , true );
1716+
1717+ if ( ! is_array ( $ body ) || empty ( $ body ['translations ' ] ) ) {
1718+ return false ;
1719+ }
1720+
1721+ foreach ( $ body ['translations ' ] as $ translation ) {
1722+ if (
1723+ isset ( $ translation ['language ' ], $ translation ['version ' ] )
1724+ && $ locale === $ translation ['language ' ]
1725+ ) {
1726+ return [
1727+ 'current ' => $ translation ['version ' ],
1728+ 'download ' => $ this ->get_download_url ( $ translation ['version ' ], $ locale , 'zip ' ),
1729+ ];
1730+ }
1731+ }
1732+
1733+ return false ;
1734+ }
1735+
16741736 /**
16751737 * Returns update information.
16761738 *
0 commit comments