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 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.
|
||||
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ find . -path './.git' -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 php
|
||||
|
||||
文章和页面列表会显示每种目标语言的状态列,包括最近成功翻译时间和翻译操作。设置页提供 **重新翻译所有内容**、**翻译所有文章** 和 **翻译所有分类标签**。它们会在为相应内容安排强制刷新前显示确认警告;此操作会绕过已持久化的翻译并消耗百度 API 配额。
|
||||
|
||||
设置页还显示实时进度面板。面板每五秒刷新一次,统计每种已配置目标语言中完成的文章/页面和分类/标签项目。当 **重新翻译所有内容** 启动批处理时,进度会重置,仅统计该批处理开始后完成的翻译,因此历史翻译不会让新的批处理看似已完成。强制重翻任务会按一分钟间隔排队,单次 WP-Cron 请求最多处理一个内容项,避免一次处理全部文章而超时。进度只读取本地 WordPress 和翻译表数据,不会发送额外请求。
|
||||
设置页还显示实时进度面板。面板每五秒刷新一次,显示最近一次翻译任务的进度,覆盖全量翻译、文章和页面翻译、分类和标签翻译,以及单篇文章翻译。进度仅统计该任务启动后、且属于其已入队内容和目标语言的完成项目,因此历史翻译不会让新的任务看似已完成。强制重翻任务会立即开始,并按两秒间隔排队;单次 WP-Cron 请求最多处理一个内容项,避免一次处理全部内容而超时。进度只读取本地 WordPress 和翻译表数据,不会发送额外请求。
|
||||
|
||||
设置页的 **清除已翻译的缓存** 会删除所有持久化翻译记录、取消待执行的翻译任务并重置进度。清除后前台将显示源文,需使用相应翻译操作重新生成翻译;重新生成会再次消耗百度 API 配额。
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ class WPT_Admin {
|
||||
var progress = response.data;
|
||||
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>' +
|
||||
'<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 () {
|
||||
container.innerHTML = '<p>暂时无法读取翻译进度。</p>';
|
||||
@@ -146,17 +146,11 @@ class WPT_Admin {
|
||||
wp_die( '无效的翻译范围。' );
|
||||
}
|
||||
|
||||
$batch_started_at = current_time( 'mysql', true );
|
||||
update_option(
|
||||
'wpt_retranslation_batch',
|
||||
array(
|
||||
'started_at' => $batch_started_at,
|
||||
),
|
||||
false
|
||||
);
|
||||
|
||||
$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 ) {
|
||||
$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 ) {
|
||||
$terms = get_terms(
|
||||
$term_ids = get_terms(
|
||||
array(
|
||||
'taxonomy' => array( 'category', 'post_tag' ),
|
||||
'hide_empty' => false,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
if ( ! is_wp_error( $terms ) ) {
|
||||
foreach ( $terms as $term_id ) {
|
||||
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;
|
||||
++$scheduled;
|
||||
}
|
||||
}
|
||||
if ( is_wp_error( $term_ids ) ) {
|
||||
$term_ids = array();
|
||||
}
|
||||
}
|
||||
|
||||
$this->update_translation_task( $post_ids, $term_ids, $languages );
|
||||
|
||||
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_term_translation' );
|
||||
wp_clear_scheduled_hook( 'wpt_process_seo_output_translation' );
|
||||
delete_option( 'wpt_translation_task' );
|
||||
delete_option( 'wpt_retranslation_batch' );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
$post_count = (int) wp_count_posts( 'post' )->publish + (int) wp_count_posts( 'page' )->publish;
|
||||
$terms = get_terms(
|
||||
array(
|
||||
'taxonomy' => array( 'category', 'post_tag' ),
|
||||
'hide_empty' => false,
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
$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 );
|
||||
$task = (array) get_option( 'wpt_translation_task', array() );
|
||||
$post_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['post_ids'] ?? array() ) ) ) );
|
||||
$term_ids = array_values( array_filter( array_map( 'absint', (array) ( $task['term_ids'] ?? array() ) ) ) );
|
||||
$languages = array_values( array_filter( array_map( 'sanitize_key', (array) ( $task['target_languages'] ?? array() ) ) ) );
|
||||
$started_at = isset( $task['started_at'] ) ? sanitize_text_field( $task['started_at'] ) : '';
|
||||
$counts = ( new WPT_Translation_Store() )->get_completed_item_counts( $languages, $post_ids, $term_ids, $started_at );
|
||||
|
||||
$posts_total = $post_count * count( $languages );
|
||||
$terms_total = $term_count * count( $languages );
|
||||
$posts_total = count( $post_ids ) * count( $languages );
|
||||
$terms_total = count( $term_ids ) * count( $languages );
|
||||
$total = $posts_total + $terms_total;
|
||||
$posts_completed = min( $counts['posts'], $posts_total );
|
||||
$terms_completed = min( $counts['terms'], $terms_total );
|
||||
@@ -274,6 +267,7 @@ class WPT_Admin {
|
||||
'total' => $total,
|
||||
'percent' => $percent,
|
||||
'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 );
|
||||
$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_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php?post_type=' . get_post_type( $post_id ) ) );
|
||||
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 );
|
||||
}
|
||||
|
||||
public function get_completed_item_counts( $target_languages, $since = '' ) {
|
||||
public function get_completed_item_counts( $target_languages, $post_ids, $term_ids, $since = '' ) {
|
||||
global $wpdb;
|
||||
|
||||
$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 ) ) {
|
||||
return array(
|
||||
'posts' => 0,
|
||||
@@ -104,29 +106,44 @@ class WPT_Translation_Store {
|
||||
);
|
||||
}
|
||||
|
||||
$table_name = self::table_name();
|
||||
$placeholders = implode( ',', array_fill( 0, count( $target_languages ), '%s' ) );
|
||||
$since_clause = '' === $since ? '' : ' AND updated_at >= %s';
|
||||
$query_args = $target_languages;
|
||||
if ( '' !== $since ) {
|
||||
$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
|
||||
);
|
||||
$table_name = self::table_name();
|
||||
$language_placeholders = implode( ',', array_fill( 0, count( $target_languages ), '%s' ) );
|
||||
$since_clause = '' === $since ? '' : ' AND updated_at >= %s';
|
||||
$post_sql = $this->get_completed_item_count_sql( 'post', $post_ids, $target_languages, $language_placeholders, $since, $since_clause, $table_name );
|
||||
$term_sql = $this->get_completed_item_count_sql( 'term', $term_ids, $target_languages, $language_placeholders, $since, $since_clause, $table_name );
|
||||
|
||||
return array(
|
||||
'posts' => (int) $wpdb->get_var( $post_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_seo_output_translation' );
|
||||
delete_option( 'wpt_settings' );
|
||||
delete_option( 'wpt_translation_task' );
|
||||
delete_option( 'wpt_retranslation_batch' );
|
||||
|
||||
$table_name = $wpdb->prefix . 'wpt_translations';
|
||||
|
||||
Reference in New Issue
Block a user