fix: translation too slow
This commit is contained in:
@@ -75,7 +75,7 @@ Before production use, test the plugin in a WordPress staging environment. Cover
|
|||||||
|
|
||||||
The Posts and Pages lists show a status column for every target language, including the latest successful translation time and a translation action. The Settings page provides **重新翻译所有内容**, **翻译所有文章**, and **翻译所有分类标签**. Each action shows a confirmation before scheduling a forced refresh of all supported content, only published posts and pages, or only categories and tags; this bypasses persisted translations and consumes Baidu API quota.
|
The Posts and Pages lists show a status column for every target language, including the latest successful translation time and a translation action. The Settings page provides **重新翻译所有内容**, **翻译所有文章**, and **翻译所有分类标签**. Each action shows a confirmation before scheduling a forced refresh of all supported content, only published posts and pages, or only categories and tags; this bypasses persisted translations and consumes Baidu API quota.
|
||||||
|
|
||||||
The Settings page also shows a live progress panel. It refreshes every five seconds and counts completed post/page and category/tag items for every configured target language. When **重新翻译所有内容** starts a batch, progress resets and counts only translations completed after that batch began, so historical translations do not make a new batch appear complete. Forced retranslation jobs are scheduled one minute apart, so a single WP-Cron request processes at most one batch item instead of timing out while processing every post. Progress reads only local WordPress and translation-table data; it does not send additional requests to Baidu.
|
The Settings page also shows a live progress panel. It refreshes every five seconds and reports the latest translation task: full translation, post/page translation, category/tag translation, or a single-post translation. It counts only completed items that belong to that task's queued content and target languages after the task began, so historical translations cannot make a new task appear complete. Forced retranslation jobs begin immediately and are scheduled two seconds apart, so a single WP-Cron request processes at most one task item instead of timing out while processing every post. Progress reads only local WordPress and translation-table data; it does not send additional requests to Baidu.
|
||||||
|
|
||||||
The **清除已翻译的缓存** action deletes every persisted translation, cancels pending translation jobs, and resets batch progress. The front end then falls back to source content until translations are explicitly queued again; regenerating them consumes Baidu API quota.
|
The **清除已翻译的缓存** action deletes every persisted translation, cancels pending translation jobs, and resets batch progress. The front end then falls back to source content until translations are explicitly queued again; regenerating them consumes Baidu API quota.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ find . -path './.git' -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 php
|
|||||||
|
|
||||||
文章和页面列表会显示每种目标语言的状态列,包括最近成功翻译时间和翻译操作。设置页提供 **重新翻译所有内容**、**翻译所有文章** 和 **翻译所有分类标签**。它们会在为相应内容安排强制刷新前显示确认警告;此操作会绕过已持久化的翻译并消耗百度 API 配额。
|
文章和页面列表会显示每种目标语言的状态列,包括最近成功翻译时间和翻译操作。设置页提供 **重新翻译所有内容**、**翻译所有文章** 和 **翻译所有分类标签**。它们会在为相应内容安排强制刷新前显示确认警告;此操作会绕过已持久化的翻译并消耗百度 API 配额。
|
||||||
|
|
||||||
设置页还显示实时进度面板。面板每五秒刷新一次,统计每种已配置目标语言中完成的文章/页面和分类/标签项目。当 **重新翻译所有内容** 启动批处理时,进度会重置,仅统计该批处理开始后完成的翻译,因此历史翻译不会让新的批处理看似已完成。强制重翻任务会按一分钟间隔排队,单次 WP-Cron 请求最多处理一个内容项,避免一次处理全部文章而超时。进度只读取本地 WordPress 和翻译表数据,不会发送额外请求。
|
设置页还显示实时进度面板。面板每五秒刷新一次,显示最近一次翻译任务的进度,覆盖全量翻译、文章和页面翻译、分类和标签翻译,以及单篇文章翻译。进度仅统计该任务启动后、且属于其已入队内容和目标语言的完成项目,因此历史翻译不会让新的任务看似已完成。强制重翻任务会立即开始,并按两秒间隔排队;单次 WP-Cron 请求最多处理一个内容项,避免一次处理全部内容而超时。进度只读取本地 WordPress 和翻译表数据,不会发送额外请求。
|
||||||
|
|
||||||
设置页的 **清除已翻译的缓存** 会删除所有持久化翻译记录、取消待执行的翻译任务并重置进度。清除后前台将显示源文,需使用相应翻译操作重新生成翻译;重新生成会再次消耗百度 API 配额。
|
设置页的 **清除已翻译的缓存** 会删除所有持久化翻译记录、取消待执行的翻译任务并重置进度。清除后前台将显示源文,需使用相应翻译操作重新生成翻译;重新生成会再次消耗百度 API 配额。
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class WPT_Admin {
|
|||||||
var progress = response.data;
|
var progress = response.data;
|
||||||
container.innerHTML = '<p><strong>' + progress.percent + '%</strong>(' + progress.completed + ' / ' + progress.total + ' 个内容项)</p>' +
|
container.innerHTML = '<p><strong>' + progress.percent + '%</strong>(' + progress.completed + ' / ' + progress.total + ' 个内容项)</p>' +
|
||||||
'<div style="max-width:480px;height:12px;background:#dcdcde"><div style="height:12px;width:' + progress.percent + '%;background:#2271b1"></div></div>' +
|
'<div style="max-width:480px;height:12px;background:#dcdcde"><div style="height:12px;width:' + progress.percent + '%;background:#2271b1"></div></div>' +
|
||||||
'<p class="description">文章和页面:' + progress.posts_completed + ' / ' + progress.posts_total + ';分类和标签:' + progress.terms_completed + ' / ' + progress.terms_total + '。显示最近一次全量重翻批次的进度,每 5 秒自动更新。</p>';
|
'<p class="description">最近任务:' + progress.task_label + '。文章和页面:' + progress.posts_completed + ' / ' + progress.posts_total + ';分类和标签:' + progress.terms_completed + ' / ' + progress.terms_total + '。每 5 秒自动更新。</p>';
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
container.innerHTML = '<p>暂时无法读取翻译进度。</p>';
|
container.innerHTML = '<p>暂时无法读取翻译进度。</p>';
|
||||||
@@ -146,17 +146,11 @@ class WPT_Admin {
|
|||||||
wp_die( '无效的翻译范围。' );
|
wp_die( '无效的翻译范围。' );
|
||||||
}
|
}
|
||||||
|
|
||||||
$batch_started_at = current_time( 'mysql', true );
|
|
||||||
update_option(
|
|
||||||
'wpt_retranslation_batch',
|
|
||||||
array(
|
|
||||||
'started_at' => $batch_started_at,
|
|
||||||
),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
$scheduled = 0;
|
$scheduled = 0;
|
||||||
$next_run = time() + MINUTE_IN_SECONDS;
|
$next_run = time();
|
||||||
|
$post_ids = array();
|
||||||
|
$term_ids = array();
|
||||||
|
$languages = WPT_Settings::target_languages();
|
||||||
|
|
||||||
if ( 'all' === $scope || 'posts' === $scope ) {
|
if ( 'all' === $scope || 'posts' === $scope ) {
|
||||||
$post_ids = get_posts(
|
$post_ids = get_posts(
|
||||||
@@ -168,33 +162,38 @@ class WPT_Admin {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ( $post_ids as $post_id ) {
|
|
||||||
foreach ( WPT_Settings::target_languages() as $target_language ) {
|
|
||||||
$this->clear_scheduled_event( 'wpt_process_translation', array( $post_id, $target_language, true ) );
|
|
||||||
wp_schedule_single_event( $next_run, 'wpt_process_translation', array( $post_id, $target_language, true ) );
|
|
||||||
$next_run += MINUTE_IN_SECONDS;
|
|
||||||
++$scheduled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( 'all' === $scope || 'terms' === $scope ) {
|
if ( 'all' === $scope || 'terms' === $scope ) {
|
||||||
$terms = get_terms(
|
$term_ids = get_terms(
|
||||||
array(
|
array(
|
||||||
'taxonomy' => array( 'category', 'post_tag' ),
|
'taxonomy' => array( 'category', 'post_tag' ),
|
||||||
'hide_empty' => false,
|
'hide_empty' => false,
|
||||||
'fields' => 'ids',
|
'fields' => 'ids',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if ( ! is_wp_error( $terms ) ) {
|
if ( is_wp_error( $term_ids ) ) {
|
||||||
foreach ( $terms as $term_id ) {
|
$term_ids = array();
|
||||||
foreach ( WPT_Settings::target_languages() as $target_language ) {
|
}
|
||||||
$this->clear_scheduled_event( 'wpt_process_term_translation', array( $term_id, $target_language, true ) );
|
}
|
||||||
wp_schedule_single_event( $next_run, 'wpt_process_term_translation', array( $term_id, $target_language, true ) );
|
|
||||||
$next_run += MINUTE_IN_SECONDS;
|
$this->update_translation_task( $post_ids, $term_ids, $languages );
|
||||||
++$scheduled;
|
|
||||||
}
|
foreach ( $post_ids as $post_id ) {
|
||||||
}
|
foreach ( $languages as $target_language ) {
|
||||||
|
$this->clear_scheduled_event( 'wpt_process_translation', array( $post_id, $target_language, true ) );
|
||||||
|
wp_schedule_single_event( $next_run, 'wpt_process_translation', array( $post_id, $target_language, true ) );
|
||||||
|
$next_run += 2;
|
||||||
|
++$scheduled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $term_ids as $term_id ) {
|
||||||
|
foreach ( $languages as $target_language ) {
|
||||||
|
$this->clear_scheduled_event( 'wpt_process_term_translation', array( $term_id, $target_language, true ) );
|
||||||
|
wp_schedule_single_event( $next_run, 'wpt_process_term_translation', array( $term_id, $target_language, true ) );
|
||||||
|
$next_run += 2;
|
||||||
|
++$scheduled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +216,7 @@ class WPT_Admin {
|
|||||||
wp_clear_scheduled_hook( 'wpt_process_translation' );
|
wp_clear_scheduled_hook( 'wpt_process_translation' );
|
||||||
wp_clear_scheduled_hook( 'wpt_process_term_translation' );
|
wp_clear_scheduled_hook( 'wpt_process_term_translation' );
|
||||||
wp_clear_scheduled_hook( 'wpt_process_seo_output_translation' );
|
wp_clear_scheduled_hook( 'wpt_process_seo_output_translation' );
|
||||||
|
delete_option( 'wpt_translation_task' );
|
||||||
delete_option( 'wpt_retranslation_batch' );
|
delete_option( 'wpt_retranslation_batch' );
|
||||||
|
|
||||||
wp_safe_redirect( add_query_arg( 'wpt_cache_cleared', 1, admin_url( 'options-general.php?page=wp-btranslate' ) ) );
|
wp_safe_redirect( add_query_arg( 'wpt_cache_cleared', 1, admin_url( 'options-general.php?page=wp-btranslate' ) ) );
|
||||||
@@ -242,22 +242,15 @@ class WPT_Admin {
|
|||||||
wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
|
wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
|
||||||
}
|
}
|
||||||
|
|
||||||
$post_count = (int) wp_count_posts( 'post' )->publish + (int) wp_count_posts( 'page' )->publish;
|
$task = (array) get_option( 'wpt_translation_task', array() );
|
||||||
$terms = get_terms(
|
$post_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['post_ids'] ?? array() ) ) ) );
|
||||||
array(
|
$term_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['term_ids'] ?? array() ) ) ) );
|
||||||
'taxonomy' => array( 'category', 'post_tag' ),
|
$languages = array_values( array_filter( array_map( 'sanitize_key', (array) ( $task['target_languages'] ?? array() ) ) ) );
|
||||||
'hide_empty' => false,
|
$started_at = isset( $task['started_at'] ) ? sanitize_text_field( $task['started_at'] ) : '';
|
||||||
'fields' => 'ids',
|
$counts = ( new WPT_Translation_Store() )->get_completed_item_counts( $languages, $post_ids, $term_ids, $started_at );
|
||||||
)
|
|
||||||
);
|
|
||||||
$term_count = is_wp_error( $terms ) ? 0 : count( $terms );
|
|
||||||
$languages = WPT_Settings::target_languages();
|
|
||||||
$batch = (array) get_option( 'wpt_retranslation_batch', array() );
|
|
||||||
$started_at = isset( $batch['started_at'] ) ? sanitize_text_field( $batch['started_at'] ) : '';
|
|
||||||
$counts = ( new WPT_Translation_Store() )->get_completed_item_counts( $languages, $started_at );
|
|
||||||
|
|
||||||
$posts_total = $post_count * count( $languages );
|
$posts_total = count( $post_ids ) * count( $languages );
|
||||||
$terms_total = $term_count * count( $languages );
|
$terms_total = count( $term_ids ) * count( $languages );
|
||||||
$total = $posts_total + $terms_total;
|
$total = $posts_total + $terms_total;
|
||||||
$posts_completed = min( $counts['posts'], $posts_total );
|
$posts_completed = min( $counts['posts'], $posts_total );
|
||||||
$terms_completed = min( $counts['terms'], $terms_total );
|
$terms_completed = min( $counts['terms'], $terms_total );
|
||||||
@@ -274,6 +267,7 @@ class WPT_Admin {
|
|||||||
'total' => $total,
|
'total' => $total,
|
||||||
'percent' => $percent,
|
'percent' => $percent,
|
||||||
'batch_started_at' => $started_at,
|
'batch_started_at' => $started_at,
|
||||||
|
'task_label' => $this->translation_task_label( $post_ids, $term_ids ),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -327,9 +321,43 @@ class WPT_Admin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_admin_referer( 'wpt_translate_post_' . $post_id . '_' . $target_language );
|
check_admin_referer( 'wpt_translate_post_' . $post_id . '_' . $target_language );
|
||||||
|
$this->update_translation_task( array( $post_id ), array(), array( $target_language ) );
|
||||||
wp_schedule_single_event( time() + 5, 'wpt_process_translation', array( $post_id, $target_language, $force_refresh ) );
|
wp_schedule_single_event( time() + 5, 'wpt_process_translation', array( $post_id, $target_language, $force_refresh ) );
|
||||||
|
|
||||||
wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php?post_type=' . get_post_type( $post_id ) ) );
|
wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php?post_type=' . get_post_type( $post_id ) ) );
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function update_translation_task( $post_ids, $term_ids, $target_languages ) {
|
||||||
|
update_option(
|
||||||
|
'wpt_translation_task',
|
||||||
|
array(
|
||||||
|
'started_at' => current_time( 'mysql', true ),
|
||||||
|
'post_ids' => array_values( array_filter( array_map( 'absint', (array) $post_ids ) ) ),
|
||||||
|
'term_ids' => array_values( array_filter( array_map( 'absint', (array) $term_ids ) ) ),
|
||||||
|
'target_languages' => array_values( array_filter( array_map( 'sanitize_key', (array) $target_languages ) ) ),
|
||||||
|
),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function translation_task_label( $post_ids, $term_ids ) {
|
||||||
|
if ( ! empty( $post_ids ) && ! empty( $term_ids ) ) {
|
||||||
|
return '全量翻译';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 1 === count( $post_ids ) ) {
|
||||||
|
return '单篇文章翻译';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $post_ids ) ) {
|
||||||
|
return '文章和页面翻译';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $term_ids ) ) {
|
||||||
|
return '分类和标签翻译';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '暂无翻译任务';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -93,10 +93,12 @@ class WPT_Translation_Store {
|
|||||||
return $wpdb->get_row( $sql, ARRAY_A );
|
return $wpdb->get_row( $sql, ARRAY_A );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_completed_item_counts( $target_languages, $since = '' ) {
|
public function get_completed_item_counts( $target_languages, $post_ids, $term_ids, $since = '' ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
$target_languages = array_values( array_filter( array_map( 'sanitize_key', (array) $target_languages ) ) );
|
$target_languages = array_values( array_filter( array_map( 'sanitize_key', (array) $target_languages ) ) );
|
||||||
|
$post_ids = array_values( array_filter( array_map( 'absint', (array) $post_ids ) ) );
|
||||||
|
$term_ids = array_values( array_filter( array_map( 'absint', (array) $term_ids ) ) );
|
||||||
if ( empty( $target_languages ) ) {
|
if ( empty( $target_languages ) ) {
|
||||||
return array(
|
return array(
|
||||||
'posts' => 0,
|
'posts' => 0,
|
||||||
@@ -104,29 +106,44 @@ class WPT_Translation_Store {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$table_name = self::table_name();
|
$table_name = self::table_name();
|
||||||
$placeholders = implode( ',', array_fill( 0, count( $target_languages ), '%s' ) );
|
$language_placeholders = implode( ',', array_fill( 0, count( $target_languages ), '%s' ) );
|
||||||
$since_clause = '' === $since ? '' : ' AND updated_at >= %s';
|
$since_clause = '' === $since ? '' : ' AND updated_at >= %s';
|
||||||
$query_args = $target_languages;
|
$post_sql = $this->get_completed_item_count_sql( 'post', $post_ids, $target_languages, $language_placeholders, $since, $since_clause, $table_name );
|
||||||
if ( '' !== $since ) {
|
$term_sql = $this->get_completed_item_count_sql( 'term', $term_ids, $target_languages, $language_placeholders, $since, $since_clause, $table_name );
|
||||||
$query_args[] = $since;
|
|
||||||
}
|
|
||||||
$post_sql = $wpdb->prepare(
|
|
||||||
"SELECT COUNT(DISTINCT CONCAT(target_language, '|', SUBSTRING_INDEX(field_context, ':', 2)))
|
|
||||||
FROM {$table_name}
|
|
||||||
WHERE status = 'complete' AND target_language IN ({$placeholders}) AND field_context LIKE 'post:%'{$since_clause}",
|
|
||||||
$query_args
|
|
||||||
);
|
|
||||||
$term_sql = $wpdb->prepare(
|
|
||||||
"SELECT COUNT(DISTINCT CONCAT(target_language, '|', SUBSTRING_INDEX(field_context, ':', 2)))
|
|
||||||
FROM {$table_name}
|
|
||||||
WHERE status = 'complete' AND target_language IN ({$placeholders}) AND field_context LIKE 'term:%'{$since_clause}",
|
|
||||||
$query_args
|
|
||||||
);
|
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'posts' => (int) $wpdb->get_var( $post_sql ),
|
'posts' => (int) $wpdb->get_var( $post_sql ),
|
||||||
'terms' => (int) $wpdb->get_var( $term_sql ),
|
'terms' => (int) $wpdb->get_var( $term_sql ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function get_completed_item_count_sql( $item_type, $item_ids, $target_languages, $language_placeholders, $since, $since_clause, $table_name ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if ( empty( $item_ids ) ) {
|
||||||
|
return 'SELECT 0';
|
||||||
|
}
|
||||||
|
|
||||||
|
$contexts = array_map(
|
||||||
|
static function ( $item_id ) use ( $item_type ) {
|
||||||
|
return $item_type . ':' . $item_id;
|
||||||
|
},
|
||||||
|
$item_ids
|
||||||
|
);
|
||||||
|
$context_placeholders = implode( ',', array_fill( 0, count( $contexts ), '%s' ) );
|
||||||
|
$query_args = array_merge( $target_languages, $contexts );
|
||||||
|
if ( '' !== $since ) {
|
||||||
|
$query_args[] = $since;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $wpdb->prepare(
|
||||||
|
"SELECT COUNT(DISTINCT CONCAT(target_language, '|', SUBSTRING_INDEX(field_context, ':', 2)))
|
||||||
|
FROM {$table_name}
|
||||||
|
WHERE status = 'complete'
|
||||||
|
AND target_language IN ({$language_placeholders})
|
||||||
|
AND SUBSTRING_INDEX(field_context, ':', 2) IN ({$context_placeholders}){$since_clause}",
|
||||||
|
$query_args
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,7 @@ function wpt_uninstall_site_data() {
|
|||||||
wp_clear_scheduled_hook( 'wpt_process_term_translation' );
|
wp_clear_scheduled_hook( 'wpt_process_term_translation' );
|
||||||
wp_clear_scheduled_hook( 'wpt_process_seo_output_translation' );
|
wp_clear_scheduled_hook( 'wpt_process_seo_output_translation' );
|
||||||
delete_option( 'wpt_settings' );
|
delete_option( 'wpt_settings' );
|
||||||
|
delete_option( 'wpt_translation_task' );
|
||||||
delete_option( 'wpt_retranslation_batch' );
|
delete_option( 'wpt_retranslation_batch' );
|
||||||
|
|
||||||
$table_name = $wpdb->prefix . 'wpt_translations';
|
$table_name = $wpdb->prefix . 'wpt_translations';
|
||||||
|
|||||||
Reference in New Issue
Block a user