WP Rocket 多語系快取:儲存草稿時翻譯版快取消失的原因與解法

WP Rocket 多語系快取是依 TranslatePress 每個翻譯版網址,分別存放在語言資料夾底下的頁面快取。在 Nginx + Apache 架構的 WordPress 伺服器上,從 WP Rocket 的程式碼追查每次儲存草稿時,翻譯版快取連同整個語言資料夾一起消失的原因後,改用一段程式碼解決:讓尚未發佈的文章不參與翻譯版快取刪除,並把自行刪除的網址重新告知預先載入。
在同一台伺服器內量到的首位元組,有翻譯版快取時是 0.015 秒,沒有時是 2.492 秒。語言資料夾一旦清空,翻譯版的訪客在有人重新開啟該網址、快取重新建立之前,都會收到這樣緩慢的回應。
1️⃣ WP Rocket 多語系快取結構:各語言資料夾與翻譯版刪除
翻譯版是把原文文章翻譯後顯示的頁面,所以很容易以為它的快取和原文綁在一起,但 WP Rocket 會把翻譯版網址當成另一個頁面來快取。快取資料夾底下,[en]、[ja] 等語言資料夾會和原文文章資料夾並列,翻譯版文章則以翻譯後的代稱存放在其中。
修改文章時,WP Rocket 的 TranslatePress 整合也會一併刪除該文章的翻譯版快取。官方文件也說明,一般修改會清除所有翻譯版,而在 TranslatePress 翻譯編輯器中修改時,只會清除有變更的語言。
// wp-rocket/inc/ThirdParty/Plugins/I18n/TranslatePress.php (3.23.5.1)
'post_updated' => 'clear_post_languages', // 每次儲存文章時執行
public function clear_post_languages( $post_id ) {
// …
$default_permalink = get_permalink( $post_id ); // 不檢查文章狀態
foreach ( $trp_settings['translation-languages'] as $language ) {
if ( $language === $trp_settings['default-language'] ) {
continue;
}
$clear_urls[] = $converter->get_url_for_language( $language, $default_permalink, '' );
}
// …
rocket_clean_files( $clear_urls );
}整合函式會把文章網址轉換成各語言的翻譯版網址,再交給快取檔案清理函式 [rocket_clean_files]。這個過程沒有任何一行檢查文章狀態,所以不論文章已發佈還是草稿,都會以同樣的順序產生翻譯版網址並刪除。
2️⃣ 儲存草稿:整個語言資料夾遭到刪除
儲存草稿和翻譯版快取並非無關。發佈之前,文章的永久連結不是代稱,而是 [?p=文章編號] 的形式,而 TranslatePress 會把這個網址轉換成加上語言路徑的網址,例如 [/en/?p=文章編號]。
[rocket_clean_files] 會從網址拿掉查詢,只依路徑找出快取位置。剩下的路徑如果是 [/en/],就代表該語言的整個快取資料夾;目標如果是資料夾,連子資料夾也會遞迴刪除。
// wp-rocket/inc/functions/files.php (3.23.5.1) — rocket_clean_files() 內
$parsed_url = get_rocket_parse_url( $url ); // 分開路徑與查詢
// …
$entry = $dir . $parsed_url['path']; // …/example.com/en/
// …
if ( $filesystem->is_dir( $entry ) ) {
rocket_rrmdir( $entry, [], $filesystem ); // 連同資料夾遞迴刪除
} else {
$filesystem->delete( $entry );
}首頁也會經過同樣的路徑。首頁網址 [/] 會轉換成 [/en/],所以就算儲存已發佈的首頁,也會刪除整個語言資料夾。只修改草稿標題再儲存一次,四個語言的快取資料夾就全部清空;從儲存時間與快取刪除記錄也確認,在草稿儲存集中的一個晚上,同樣的刪除重複了 30 次以上。
從程式碼來看,編輯器自動儲存也走同樣的路徑。你編輯已發佈文章時儲存的修訂版本,同樣使用 [?p=] 形式的網址。
在 Nginx + Apache 架構的測試網站上,用編輯器修改已發佈文章並檢查語言資料夾的檔案數。第一次自動儲存後,英文與日文資料夾仍各有 20 個檔案;1 分鐘後第二次自動儲存時,兩個資料夾都變成 0 個。
第一次自動儲存會建立新的修訂版本,從第二次起則是修改同一個修訂版本後儲存,因此會執行 [post_updated]。第一次自動儲存後如果繼續修改內文,就算不按儲存按鈕,翻譯版快取也會連同語言資料夾一起被刪除。
同樣的症狀自 2023 年 8 月 31 日起,就以 issue #6130 開在 WP Rocket 官方儲存庫中。修正它的 pull request #7616 在 2025 年 9 月提出,但尚未合併,2026 年 10 月 2 日推出的 3.23.5.1 變更記錄中也沒有修正內容。
3️⃣ 修正程式碼:只刪除已發佈文章的翻譯版快取
在官方修正推出之前,做法是拆下整合函式,改用只在網址確定時才刪除翻譯版的函式。issue 留言中分享的繞過方法也是同一個方向,也就是文章尚未發佈時就直接離開整合函式。
不過,只檢查文章狀態的條件還會留下兩種情況。首頁即使是已發佈狀態,儲存時仍會刪除整個語言資料夾;而把已發佈文章改回草稿時,翻譯版快取不會被刪除,下架的文章仍能在翻譯版網址開啟。
<?php
/**
* Plugin Name: Rocket TranslatePress Draft Fix
* Description: 把 WP Rocket TranslatePress 整合的翻譯版快取刪除限制在網址確定的情況
*/
// 1. 從 post_updated 拆下 WP Rocket 整合函式並保留
add_action( 'init', function () {
global $wp_filter;
if ( empty( $wp_filter['post_updated'] ) ) {
return;
}
foreach ( $wp_filter['post_updated']->callbacks as $priority => $callbacks ) {
foreach ( $callbacks as $cb ) {
$fn = $cb['function'];
if ( is_array( $fn ) && is_object( $fn[0] )
&& 0 === strpos( get_class( $fn[0] ), 'WP_Rocket\\' )
&& false !== stripos( get_class( $fn[0] ), 'TranslatePress' ) ) {
remove_action( 'post_updated', $fn, $priority );
$GLOBALS['rtdf_original'] = $fn;
}
}
}
}, 9999 );
// 2. 替代函式
add_action( 'post_updated', function ( $post_id, $after, $before ) {
if ( empty( $GLOBALS['rtdf_original'] ) || ! class_exists( 'TRP_Translate_Press' ) ) {
return;
}
$trp = TRP_Translate_Press::get_trp_instance();
$converter = $trp->get_component( 'url_converter' );
$settings = $trp->get_component( 'settings' )->get_settings();
$languages = array_diff( $settings['translation-languages'], [ $settings['default-language'] ] );
// 首頁 — 只刪除語言首頁的快取檔案,不刪除語言資料夾
if ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === (int) $post_id ) {
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$home = $converter->get_url_for_language( $lang, home_url( '/' ), '' );
$dir = WP_ROCKET_CACHE_PATH . wp_parse_url( $home, PHP_URL_HOST ) . wp_parse_url( $home, PHP_URL_PATH );
array_map( 'unlink', glob( $dir . 'index*.html*' ) ?: [] );
$urls[] = $home;
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
do_action( 'rocket_after_clean_terms', $urls ); // 告知預先載入(5️⃣)
return;
}
// 儲存已發佈文章 — 照樣執行 WP Rocket 原本的函式
if ( 'publish' === $after->post_status ) {
call_user_func( $GLOBALS['rtdf_original'], $post_id );
return;
}
// 已發佈 → 草稿·私密·回收桶 — 只處理下架前網址的翻譯版
if ( 'publish' === $before->post_status ) {
$old = get_permalink( $before );
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$urls[] = $converter->get_url_for_language( $lang, $old, '' );
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
rocket_clean_files( $urls );
}
// 其他情況(草稿·修訂版本·自動儲存)沒有翻譯版網址,因此不刪除
}, 10, 3 );第一部分會找出 WP Rocket 登記的整合函式,從 [post_updated] 拆下,並保留原本的函式。替代函式遇到首頁時只刪除語言首頁的快取檔案,遇到已發佈文章時照樣執行保留下來的原本函式,而已發佈文章下架時,會用下架前的網址找出翻譯版並刪除。
存成 PHP 檔案放在 [wp-content/mu-plugins/] 資料夾,不必啟用就會立即生效;刪除檔案後,WP Rocket 就會回到原本的運作方式。用運作方式相同的程式碼測試的結果,再次儲存草稿時語言資料夾的檔案數維持不變,再次儲存已發佈文章時只刪除該文章的原文與翻譯版資料夾。
在加入上方程式碼前後,於同一環境比較了四種情況下語言資料夾的檔案數。英文與日文資料夾各有 20 個語言首頁與四篇文章的電腦版·行動版快取檔案。
加入程式碼之前,儲存草稿、儲存首頁、取消發佈三種情況都讓兩個資料夾變成 0 個,只有儲存已發佈文章時才只刪除該文章的翻譯版快取。
加入程式碼之後,儲存草稿與編輯器自動儲存時 20 個檔案都還在。儲存首頁只刪除 4 個語言首頁快取檔案,儲存已發佈文章與取消發佈只刪除該文章的 4 個翻譯版檔案,剩下 16 個;下架文章的英文翻譯版網址回應 404。
| 情況 | 修正前 | 加入程式碼後 | 加入後刪除的快取 |
|---|---|---|---|
| 儲存草稿 | 20 → 0 | 20 → 20 | 無 |
| 儲存已發佈文章 | 只刪除該文章的翻譯版 | 20 → 16 | 該文章的翻譯版 4 個 |
| 儲存首頁 | 20 → 0 | 20 → 16 | 語言首頁快取 4 個 |
| 取消發佈 | 20 → 0 | 20 → 16 | 該文章的翻譯版 4 個 · 翻譯版網址 404 |
4️⃣ 預先載入沒有重建:殘留在佇列中的完成狀態
即使擋下了草稿儲存,在沒有 WP Rocket 函式刪除訊號的情況下刪掉的快取檔案,預先載入也不會重新補上。 WP Rocket 預先載入使用一張依網址記錄狀態的佇列表:待請求的網址是等待,已送出請求的是進行中,建立好快取的會變成完成,3 分鐘內沒完成則變成失敗。
已完成的網址會在三種情況下回到等待:快取壽命到期而刪除的檔案、WP Rocket 刪除時發出刪除訊號的網址,以及失敗後已超過 12 小時的網址。如果把 [rocket_clean_files] 的第三個參數傳成 false,或直接刪除檔案,就不會發出這個訊號,佇列中仍顯示完成,只有快取檔案消失。
接收刪除訊號後接著清除其他快取的程式碼,為了避免重複呼叫,會把第三個參數傳成 false。比對翻譯版快取時,英文翻譯版 65 個中有 3 個、日文翻譯版 65 個中有 7 個,在佇列中仍是完成,快取檔案卻不存在。
WP Rocket 支援團隊的貢獻者也在 issue #6130 的留言中承認,清除快取後表中的網址仍維持完成,導致狀態對不上。定期讓表格與快取檔案一致的提案(#2939)在 2020 年未實作就被關閉。
5️⃣ 重新登記預先載入:把自行刪除的網址告知佇列
自行刪除的網址,只要在刪除後立即用一行程式碼告知預先載入就能解決。預先載入收到 [rocket_after_clean_terms] 訊號後,會把收到的網址改回等待,表中沒有的網址則新增進去。
// 沒有刪除訊號就刪除之後 — 把刪除的網址陣列重新登記到預先載入佇列
rocket_clean_files( $urls, null, false ); // 第三個參數 false = 沒有刪除訊號
do_action( 'rocket_after_clean_terms', $urls ); // 預先載入把這些網址改回等待(pending)在 WP Rocket 內部,接收這個訊號的只有預先載入佇列函式與 SpinUpWP 主機整合,而預先載入函式只修改佇列資料列,不會刪除 WP Rocket 快取檔案,也不會再次發出刪除訊號,所以不會發生重複呼叫。只用 WP Rocket 函式清除快取、並讓第三個參數維持預設值的網站,已經會發出訊號,不需要這一行。
加入這一行後,清除一篇有四種語言翻譯版的文章快取,2 秒內四列佇列資料就變成等待。在每分鐘執行 cron 的伺服器上測試兩次,翻譯版的電腦版·行動版快取檔案 8 個在 101 秒與 153 秒後全部重新建立。可以從佇列表的狀態與快取資料夾的檔案確認。
# 該網址在佇列表中的狀態(表名前的 wp_ 是網站前綴)
wp db query <<'SQL'
SELECT url, status, modified FROM wp_wpr_rocket_cache WHERE url LIKE '%/en/sample-post-en'
SQLurl status modified
https://example.com/en/sample-post-en pending 2026-10-17 09:10:48# 約 1 分鐘後快取檔案是否重新產生
ls -l wp-content/cache/wp-rocket/example.com/en/sample-post-en/total 240
-rw-r--r-- 1 example nobody 95084 Oct 17 18:12 index-https.html
-rw-r--r-- 1 example nobody 22352 Oct 17 18:12 index-https.html_gzip
-rw-r--r-- 1 example nobody 95083 Oct 17 18:12 index-mobile-https.html
-rw-r--r-- 1 example nobody 22348 Oct 17 18:12 index-mobile-https.html_gzip刪除後狀態會立刻變成 [pending],預先載入送出請求、產生快取檔案後就會變成 [completed]。如果狀態是 [completed] 卻沒有快取檔案,表示那個網址是在沒有訊號的情況下被刪除的。
儲存首頁後,上方程式碼刪除語言首頁快取檔案並發出訊號,英文與日文首頁網址就重新進入佇列。語言首頁的電腦版·行動版快取最晚在第二次 cron 執行時重新建立。
測試兩次,從儲存到最後一個快取檔案分別花了 47 秒與 91 秒。
🔢 FAQ & 推薦內容





ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。










