238 lines
6.7 KiB
PHP
238 lines
6.7 KiB
PHP
<?php
|
|
/**
|
|
* WP-CLI commands for the AI Translator plugin.
|
|
*
|
|
* @package ROBOTSTXT\AI_Translator
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
if ( ! class_exists( 'AI_Translator_CLI' ) ) {
|
|
|
|
/**
|
|
* Translate posts from the command line.
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* # Translate a single post to Spanish.
|
|
* $ wp ai-translator translate --post-id=42 --locale=es_ES
|
|
*
|
|
* # Translate every post of a given type, preview only.
|
|
* $ wp ai-translator translate --post-type=post --locale=fr_FR --dry-run
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class AI_Translator_CLI {
|
|
|
|
/**
|
|
* Settings handler.
|
|
*
|
|
* @since 1.0.0
|
|
* @var AI_Translator_Settings
|
|
*/
|
|
private $settings;
|
|
|
|
/**
|
|
* Translator handler.
|
|
*
|
|
* @since 1.0.0
|
|
* @var AI_Translator_Translator
|
|
*/
|
|
private $translator;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @since 1.0.0
|
|
*
|
|
* @param AI_Translator_Settings $settings Settings handler.
|
|
* @param AI_Translator_Translator $translator Translator handler.
|
|
*/
|
|
public function __construct( AI_Translator_Settings $settings, AI_Translator_Translator $translator ) {
|
|
$this->settings = $settings;
|
|
$this->translator = $translator;
|
|
}
|
|
|
|
/**
|
|
* Registers the command with WP-CLI.
|
|
*
|
|
* @since 1.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register() {
|
|
if ( ! class_exists( 'WP_CLI' ) ) {
|
|
return;
|
|
}
|
|
|
|
WP_CLI::add_command( 'ai-translator', $this );
|
|
}
|
|
|
|
/**
|
|
* Translates one or more posts to a target locale.
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* --locale=<locale>
|
|
* : WordPress locale to translate to. Must be installed on the site.
|
|
*
|
|
* [--post-id=<id>]
|
|
* : ID of a single post to translate. Mutually exclusive with --post-type.
|
|
*
|
|
* [--post-type=<type>]
|
|
* : Post type to translate in bulk. Defaults to 'post' when --post-id is omitted.
|
|
*
|
|
* [--status=<status>]
|
|
* : Post status filter when running in bulk mode. Default: 'publish'.
|
|
*
|
|
* [--limit=<n>]
|
|
* : Maximum number of posts to process when running in bulk mode. Default: 50.
|
|
*
|
|
* [--dry-run]
|
|
* : Show what would be translated without calling the AI provider and without writing to the database.
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp ai-translator translate --post-id=42 --locale=es_ES
|
|
* wp ai-translator translate --post-type=page --locale=fr_FR --limit=10 --dry-run
|
|
*
|
|
* @since 1.0.0
|
|
*
|
|
* @param array<int,string> $args Positional arguments (unused).
|
|
* @param array<string,string> $assoc_args Associative arguments.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function translate( $args, $assoc_args ) {
|
|
unset( $args );
|
|
|
|
$locale = isset( $assoc_args['locale'] ) ? (string) $assoc_args['locale'] : '';
|
|
$dry_run = isset( $assoc_args['dry-run'] );
|
|
$post_id = isset( $assoc_args['post-id'] ) ? (int) $assoc_args['post-id'] : 0;
|
|
$type = isset( $assoc_args['post-type'] ) ? (string) $assoc_args['post-type'] : '';
|
|
$status = isset( $assoc_args['status'] ) ? (string) $assoc_args['status'] : 'publish';
|
|
$limit = isset( $assoc_args['limit'] ) ? max( 1, (int) $assoc_args['limit'] ) : 50;
|
|
|
|
if ( '' === $locale ) {
|
|
WP_CLI::error( __( 'The --locale flag is required.', 'robotstxt-ai-translator' ) );
|
|
}
|
|
|
|
if ( $post_id > 0 && '' !== $type ) {
|
|
WP_CLI::error( __( 'The --post-id and --post-type flags are mutually exclusive.', 'robotstxt-ai-translator' ) );
|
|
}
|
|
|
|
$current_user = wp_get_current_user();
|
|
if ( ! $current_user || 0 === (int) $current_user->ID ) {
|
|
WP_CLI::warning( __( 'No user context. Pass --user=<id|login> so post updates carry an author and capability checks apply.', 'robotstxt-ai-translator' ) );
|
|
}
|
|
|
|
if ( ! $dry_run && ! $this->translator->is_available() ) {
|
|
WP_CLI::error( __( 'The WordPress AI plugin is not active.', 'robotstxt-ai-translator' ) );
|
|
}
|
|
|
|
if ( ! $dry_run && ! $this->translator->is_supported() ) {
|
|
WP_CLI::error( __( 'The WordPress AI plugin has no provider configured for text generation.', 'robotstxt-ai-translator' ) );
|
|
}
|
|
|
|
if ( ! in_array( $locale, $this->translator->get_installed_locales(), true ) ) {
|
|
WP_CLI::error(
|
|
sprintf(
|
|
/* translators: %s: locale code. */
|
|
__( 'The locale %s is not installed on this site.', 'robotstxt-ai-translator' ),
|
|
$locale
|
|
)
|
|
);
|
|
}
|
|
|
|
$settings = $this->settings->get_settings();
|
|
|
|
if ( empty( $settings['translate_title'] ) && empty( $settings['translate_content'] ) ) {
|
|
WP_CLI::error( __( 'No translation fields are enabled in the plugin settings.', 'robotstxt-ai-translator' ) );
|
|
}
|
|
|
|
if ( $post_id > 0 ) {
|
|
$posts = array( get_post( $post_id ) );
|
|
} else {
|
|
$type = '' !== $type ? $type : 'post';
|
|
$posts = get_posts(
|
|
array(
|
|
'post_type' => $type,
|
|
'post_status' => $status,
|
|
'numberposts' => $limit,
|
|
'ignore_sticky_posts' => true,
|
|
'suppress_filters' => false,
|
|
)
|
|
);
|
|
}
|
|
|
|
$posts = array_filter(
|
|
$posts,
|
|
static function ( $post ) {
|
|
return $post instanceof WP_Post;
|
|
}
|
|
);
|
|
|
|
if ( empty( $posts ) ) {
|
|
WP_CLI::warning( __( 'No posts matched the given criteria.', 'robotstxt-ai-translator' ) );
|
|
return;
|
|
}
|
|
|
|
$successes = 0;
|
|
$failures = 0;
|
|
|
|
foreach ( $posts as $post ) {
|
|
if ( $dry_run ) {
|
|
WP_CLI::log( sprintf( '[dry-run] would translate post #%d (%s) to %s.', $post->ID, $post->post_title, $locale ) );
|
|
++$successes;
|
|
continue;
|
|
}
|
|
|
|
$update = array( 'ID' => $post->ID );
|
|
|
|
if ( ! empty( $settings['translate_title'] ) ) {
|
|
$translated_title = $this->translator->translate( $post->post_title, $locale );
|
|
if ( is_wp_error( $translated_title ) ) {
|
|
WP_CLI::warning( sprintf( '#%d title: %s', $post->ID, $translated_title->get_error_message() ) );
|
|
++$failures;
|
|
continue;
|
|
}
|
|
$update['post_title'] = $translated_title;
|
|
}
|
|
|
|
if ( ! empty( $settings['translate_content'] ) ) {
|
|
$translated_content = $this->translator->translate( $post->post_content, $locale );
|
|
if ( is_wp_error( $translated_content ) ) {
|
|
WP_CLI::warning( sprintf( '#%d content: %s', $post->ID, $translated_content->get_error_message() ) );
|
|
++$failures;
|
|
continue;
|
|
}
|
|
$update['post_content'] = $translated_content;
|
|
}
|
|
|
|
$result = wp_update_post( $update, true );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
WP_CLI::warning( sprintf( '#%d save: %s', $post->ID, $result->get_error_message() ) );
|
|
++$failures;
|
|
continue;
|
|
}
|
|
|
|
WP_CLI::log( sprintf( 'Updated post #%d.', $post->ID ) );
|
|
++$successes;
|
|
}
|
|
|
|
WP_CLI::success(
|
|
sprintf(
|
|
/* translators: 1: success count, 2: failure count, 3: locale. */
|
|
__( '%1$d translated, %2$d failed (locale: %3$s).', 'robotstxt-ai-translator' ),
|
|
$successes,
|
|
$failures,
|
|
$locale
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|