Problem Background
While using the Polylang plugin to synchronize Chinese and English tags, I found that the batch translation script had an intermittent counting inaccuracy issue. The script showed that it processed 8324 tags, but there were actually 8328 Chinese tags in the backend, and the results were inconsistent with each execution.


Symptoms
Total source language tags: 8328 Accurate
New tags processed: 0 Inaccurate (theoretically should be 4)
Existing translations skipped: 9000 Inaccurate (should be 8324)
Processing rate: 108.07%Troubleshooting Process
First Investigation: Pagination Method Issue
Initially, I used the offset parameter for pagination:
$terms = get_terms([
'taxonomy' => 'post_tag',
'lang' => $source_lang,
'number' => $per_page,
'offset' => $offset,
]);Problem Analysis: When using offset for pagination, creating new target language tags during processing changes the number of records in the database, causing subsequent pagination offsets to be inaccurate.
Fix Attempt: Switched to the paged parameter instead of offset:
$terms = get_terms([
'taxonomy' => 'post_tag',
'lang' => $source_lang,
'number' => $per_page,
'paged' => $page,
]);Second Investigation: Database Table Does Not Exist
After the modification, a new error occurred:
WordPress database error Table 'wp_polylang_terms' doesn't existProblem Analysis: I directly queried a non-existent Polylang database table. I should have used the API functions provided by Polylang.
Fix Attempt: Referring to the existing merge-tags.php file in the project, I switched to using get_terms() with the lang parameter to retrieve the tag list.
Third Investigation: Counting Still Inaccurate
Even when using the paged parameter, the count was still inaccurate:
Existing translations skipped: 9000Problem Analysis: When using get_terms() for paginated queries, the newly created English tags were fetched by subsequent queries, causing the skipped count to be incorrect.
Root Cause: The English tags created during the loop processing were captured by subsequent get_terms() queries because the query conditions only restricted the language, but the newly created English tags might have been cached or re-indexed.
Final Solution
Core Idea: First, retrieve the ID list of all source language tags at once, and then process based on this static list to prevent subsequent queries from being affected by the newly created data.
// 先一次性获取所有源语言标签的ID列表
$source_term_ids = get_terms([
'taxonomy' => 'post_tag',
'lang' => $source_lang,
'hide_empty' => false,
'fields' => 'ids',
]);
// 使用 array_chunk 分批处理
foreach (array_chunk($source_term_ids, $per_page) as $batch) {
foreach ($batch as $term_id) {
// 处理每个标签...
}
}Key Improvements:
- Static List: Retrieve the complete source language tag ID list in advance to prevent subsequent queries from being polluted by new data
- Array Chunking: Use
array_chunk()for memory-friendly batch processing - Accurate Counting: Count based on the static list to ensure statistical accuracy
Complete Optimized Code
<?php
if (php_sapi_name() !== 'cli') {
die("❌ 请在命令行运行\n");
}
require __DIR__ . '/wp-config.php';
global $wpdb, $polylang;
$per_page = 1000;
$source_lang = 'zh';
$target_lang = 'en';
$processed = 0;
$skipped = 0;
// 先一次性获取所有源语言标签的ID列表
$source_term_ids = get_terms([
'taxonomy' => 'post_tag',
'lang' => $source_lang,
'hide_empty' => false,
'fields' => 'ids',
]);
$total_terms = count($source_term_ids);
// 分批处理
foreach (array_chunk($source_term_ids, $per_page) as $batch) {
foreach ($batch as $term_id) {
$term = get_term($term_id, 'post_tag');
// 检查是否已有翻译
$target_id = pll_get_term($term_id, $target_lang);
if ($target_id) {
$skipped++;
continue;
}
// 创建翻译标签...
$processed++;
}
}
// 验证阶段
$untranslated_count = 0;
foreach ($source_term_ids as $term_id) {
if (!pll_get_term($term_id, $target_lang)) {
$untranslated_count++;
}
}Fix Results
Execution results after the fix:
Total source language tags: 8328 ✅
New tags processed: 4 ✅
Existing translations skipped: 8324 ✅
Processing rate: 100.00% ✅
Lessons Learned
- Avoid Dynamic Pagination Pitfalls: In scenarios where data is modified during processing, avoid using dynamic paginated queries
- Use Static Datasets: Retrieve the complete ID list in advance to ensure processing stability
- Rely on Official APIs: Prioritize using API functions provided by the plugin to avoid directly operating on database tables
- Add Verification Steps: Perform verification after processing is complete to ensure all data is processed correctly
References
This article documents the troubleshooting process of the batch tag translation script, from discovering the issue to the final resolution, in hopes of helping developers who encounter similar problems.

