File: //opt/.wp-cli/packages/vendor/wp-cli/language-command/src/WP_CLI/CommandWithTranslation.php
<?php
namespace WP_CLI;
use WP_CLI;
use WP_CLI_Command;
/**
* Base class for WP-CLI commands that deal with translations
*
* @package wp-cli
*/
abstract class CommandWithTranslation extends WP_CLI_Command {
protected $obj_type;
protected $obj_fields;
/**
* Callback to sort array by a 'language' key.
*/
protected function sort_translations_callback( $a, $b ) {
return strnatcasecmp( $a['language'], $b['language'] );
}
/**
* Updates installed languages for the current object type.
*
* @param string[] $args Positional arguments.
* @param array{'dry-run'?: bool, all?: bool} $assoc_args Associative arguments.
*/
public function update( $args, $assoc_args ) {
$updates = $this->get_translation_updates();
if ( empty( $updates ) ) {
WP_CLI::success( 'Translations are up to date.' );
return;
}
if ( empty( $args ) ) {
$args = array( null ); // Used for core.
}
$upgrader = 'WP_CLI\\LanguagePackUpgrader';
$results = array();
$num_to_update = 0;
foreach ( $args as $slug ) {
// Gets a list of all languages.
$all_languages = $this->get_all_languages( $slug );
$updates_per_type = array();
// Formats the updates list.
foreach ( $updates as $update ) {
if ( null !== $slug && $update->slug !== $slug ) {
continue;
}
$name = 'WordPress'; // Core.
if ( 'plugin' === $update->type ) {
/**
* @var array<array{Name: string}> $plugins
*/
$plugins = get_plugins( '/' . $update->slug );
/**
* @var array{Name: string}> $plugin_data
*/
$plugin_data = array_shift( $plugins );
$name = $plugin_data['Name'];
} elseif ( 'theme' === $update->type ) {
$theme_data = wp_get_theme( $update->slug );
/**
* @var string $name
*/
$name = $theme_data['Name'];
}
// Gets the translation data.
$translation = wp_list_filter( $all_languages, array( 'language' => $update->language ) );
$translation = (object) reset( $translation );
$update->Type = ucfirst( $update->type );
$update->Name = $name;
$update->Version = $update->version;
if ( isset( $translation->english_name ) ) {
$update->Language = $translation->english_name;
}
if ( ! isset( $updates_per_type[ $update->type ] ) ) {
$updates_per_type[ $update->type ] = array();
}
$updates_per_type[ $update->type ][] = $update;
}
$obj_type = rtrim( $this->obj_type, 's' );
$available_updates = isset( $updates_per_type[ $obj_type ] ) ? $updates_per_type[ $obj_type ] : null;
if ( ! is_array( $available_updates ) ) {
continue;
}
$num_to_update += count( $available_updates );
if ( ! Utils\get_flag_value( $assoc_args, 'dry-run' ) ) {
// Update translations.
foreach ( $available_updates as $update ) {
WP_CLI::line( "Updating '{$update->Language}' translation for {$update->Name} {$update->Version}..." );
/**
* @var \WP_CLI\LanguagePackUpgrader $upgrader_instance
*/
$upgrader_instance = Utils\get_upgrader( $upgrader );
$result = $upgrader_instance->upgrade( $update );
$results[] = $result;
}
}
}
// Only preview which translations would be updated.
if ( Utils\get_flag_value( $assoc_args, 'dry-run' ) ) {
$update_count = count( $updates );
WP_CLI::line(
sprintf(
'Found %d translation %s that would be processed:',
$update_count,
WP_CLI\Utils\pluralize( 'update', $update_count )
)
);
Utils\format_items( 'table', $updates, array( 'Type', 'Name', 'Version', 'Language' ) );
return;
}
$num_updated = count( array_filter( $results ) );
$line = sprintf( "Updated $num_updated/$num_to_update %s.", WP_CLI\Utils\pluralize( 'translation', $num_updated ) );
if ( $num_to_update === $num_updated ) {
WP_CLI::success( $line );
} elseif ( $num_updated > 0 ) {
WP_CLI::warning( $line );
} else {
WP_CLI::error( $line );
}
}
/**
* Get all updates available for all translations.
*
* @see wp_get_translation_updates()
*
* @return array
*/
protected function get_translation_updates() {
$available = $this->get_installed_languages();
$func = function () use ( $available ) {
return $available;
};
switch ( $this->obj_type ) {
case 'plugins':
add_filter( 'plugins_update_check_locales', $func );
wp_clean_plugins_cache();
// Check for Plugin translation updates.
wp_update_plugins();
remove_filter( 'plugins_update_check_locales', $func );
$transient = 'update_plugins';
break;
case 'themes':
add_filter( 'themes_update_check_locales', $func );
wp_clean_themes_cache();
// Check for Theme translation updates.
wp_update_themes();
remove_filter( 'themes_update_check_locales', $func );
$transient = 'update_themes';
break;
default:
delete_site_transient( 'update_core' );
// Check for Core translation updates.
wp_version_check();
$transient = 'update_core';
break;
}
$updates = array();
/**
* @var object{translations: array} $transient
*/
$transient = get_site_transient( $transient );
if ( empty( $transient->translations ) ) {
return $updates;
}
foreach ( $transient->translations as $translation ) {
$updates[] = (object) $translation;
}
return $updates;
}
/**
* Download a language pack.
*
* @see wp_download_language_pack()
*
* @param string $download Language code to download.
* @param string $slug Plugin or theme slug. Not used for core.
* @return string|\WP_Error Returns the language code if successfully downloaded, or a WP_Error object on failure.
*/
protected function download_language_pack( $download, $slug = null ) {
$translations = $this->get_all_languages( $slug );
$translation_to_load = null;
foreach ( $translations as $translation ) {
if ( $translation['language'] === $download ) {
$translation_to_load = $translation;
break;
}
}
if ( ! $translation_to_load ) {
return new \WP_Error( 'not_found', $slug ? "Language '{$download}' for '{$slug}' not available." : "Language '{$download}' not available." );
}
$translation = (object) $translation_to_load;
$translation->type = rtrim( $this->obj_type, 's' );
// Make sure caching in LanguagePackUpgrader works.
if ( ! isset( $translation->slug ) ) {
$translation->slug = $slug;
}
$upgrader = 'WP_CLI\\LanguagePackUpgrader';
/**
* @var \WP_CLI\LanguagePackUpgrader $upgrader_instance
*/
$upgrader_instance = Utils\get_upgrader( $upgrader );
// Incorrect docblock in WordPress core.
// @phpstan-ignore argument.type
$result = $upgrader_instance->upgrade( $translation, array( 'clear_update_cache' => false ) );
if ( is_wp_error( $result ) ) {
return $result;
}
if ( ! $result ) {
return new \WP_Error( 'not_installed', $slug ? "Could not install language '{$download}' for '{$slug}'." : "Could not install language '{$download}'." );
}
return $translation->language;
}
/**
* Return a list of installed languages.
*
* @param string $slug Optional. Plugin or theme slug. Defaults to 'default' for core.
*
* @return string[]
*/
protected function get_installed_languages( $slug = 'default' ) {
/**
* @var array<string, array<string, array<string, mixed>>> $available
*/
$available = wp_get_installed_translations( $this->obj_type );
$available = ! empty( $available[ $slug ] ) ? array_keys( $available[ $slug ] ) : array();
$available[] = 'en_US';
return $available;
}
/**
* Return a list of all languages.
*
* @param string $slug Optional. Plugin or theme slug. Not used for core.
*
* @return array<array{language: string, english_name: string, native_name: string, updated: string}>
*/
protected function get_all_languages( $slug = null ) {
require_once ABSPATH . '/wp-admin/includes/translation-install.php';
require ABSPATH . WPINC . '/version.php'; // Include an unmodified $wp_version
$args = array(
// False positive, because it's defined in version.php
// @phpstan-ignore variable.undefined
'version' => $wp_version,
);
if ( $slug ) {
$args['slug'] = $slug;
if ( 'plugins' === $this->obj_type ) {
$plugins = get_plugins( '/' . $slug );
$plugin_data = array_shift( $plugins );
if ( isset( $plugin_data['Version'] ) ) {
$args['version'] = $plugin_data['Version'];
}
} elseif ( 'themes' === $this->obj_type ) {
$theme_data = wp_get_theme( $slug );
if ( isset( $theme_data['Version'] ) ) {
$args['version'] = $theme_data['Version'];
}
}
}
$response = translations_api( $this->obj_type, $args );
if ( is_wp_error( $response ) ) {
WP_CLI::error( $response );
}
$translations = ! empty( $response['translations'] ) ? $response['translations'] : array();
$en_us = array(
'language' => 'en_US',
'english_name' => 'English (United States)',
'native_name' => 'English (United States)',
'updated' => '',
);
$translations[] = $en_us;
uasort( $translations, array( $this, 'sort_translations_callback' ) );
return $translations;
}
/**
* Get Formatter object based on supplied parameters.
*
* @param array $assoc_args Parameters passed to command. Determines formatting.
* @return Formatter
*/
protected function get_formatter( &$assoc_args ) {
return new Formatter( $assoc_args, $this->obj_fields, $this->obj_type );
}
}