This article maps the cache layers inside the WPEstate Translate plugin (wpestate-translate, text domain wpr-translate, 1.0.9) so you can reason about invalidation, extend it safely from a child theme or companion plugin, and keep a multi-language real estate website fast at scale.
Cache Layers Overview
| Layer | Scope | API | Where |
|---|---|---|---|
| Runtime string maps | Request static + object cache | static $cache and wp_cache_get() / wp_cache_set() in group wpr_translate_runtime_strings, keyed with wp_cache_get_last_changed() | includes/translation-runtime.php |
| Translation family cache | Single PHP request | $GLOBALS[‘wpr_translate_family_cache’] | includes/translation-relationships.php |
| Custom field preferences | Object cache (persistent with a drop-in) + option | wp_cache_get() / wp_cache_set() in group wpr_translate, option wpr_cf_preferences | includes/custom-fields-sync.php |
| Custom field rules | Single PHP request | $GLOBALS[‘wpr_translate_cf_rules_cache’] | includes/custom-field-rules.php |
| Generated MO directory | Option | wpr_translate_custom_directory | includes/translation-directories.php |
| Transients (per language) | Cross-request, expiring | set_transient() with names built by the theme’s wpestate_set_transient_name_multilang() | Theme lists; includes/elementor-widget-compat.php |
| Theme cache purge | Whole site | wpestate_delete_cache() | includes/admin/cache-purge.php (direct call) |
Runtime String Maps
The gettext filters run on every translated string. wpr_translate_runtime_should_use_database_translations() enables the database lookup on the front end whenever the current language is not the default, and in wp-admin only on the wpr-translate-strings page. To keep that cheap, the plugin loads every translation for one text domain and language in a single query and reuses the map:
// includes/translation-runtime.php
function wpr_translate_get_runtime_translation_map( $domain, $language_code ) {
static $cache = array();
$cache_key = strtolower( $domain ) . '|' . strtolower( $language_code ) . '|'
. wp_cache_get_last_changed( 'wpr_translate_runtime_strings' );
// 1. request static 2. wp_cache_get( $cache_key, 'wpr_translate_runtime_strings' )
// 3. one SELECT name, translation ... WHERE context IN (...) AND language_code = %s
wp_cache_set( $cache_key, $map, 'wpr_translate_runtime_strings' );
return $map; // name (str_<md5>) => translation
}
function wpr_translate_lookup_runtime_translation( $domain, $original, $language_code ) {
$map = wpr_translate_get_runtime_translation_map( $domain, $language_code );
return $map[ 'str_' . md5( $original ) ] ?? '';
}
Key design points:
- A miss is simply an absent key, so untranslated strings cost no extra queries.
- Invalidation is by generation: wpr_translate_runtime_strings_changed() writes a new last_changed value, so every old key is ignored. It runs after string saves, catalog writes and the Reset tool. If you write to {prefix}wpestate_translation_strings yourself, call it afterwards.
- With a persistent object cache the map survives across requests; without one it is per request.
Translation Family Cache
Permalinks, fields => ids result sets and front-end term lists read the same post or term family several times per request. wpr_translate_relationship_get_post_family() and wpr_translate_relationship_get_term_family() cache each family in $GLOBALS[‘wpr_translate_family_cache’] for the rest of the request. The cache is emptied by wpr_translate_relationship_flush_family_cache() on clean_post_cache, added/updated/deleted_post_meta, clean_term_cache and added/updated/deleted_term_meta. Code that changes relationship meta through the WordPress meta API is covered automatically.
Object Cache – Custom Field Preferences
The custom field configuration file is cached under the wpr_translate object cache group:
// includes/custom-fields-sync.php $option_name = 'wpr_cf_preferences'; $cache_key = 'wpr_cf_preferences'; $cache_group = 'wpr_translate'; wp_cache_set( $cache_key, $preferences, $cache_group ); $cached = wp_cache_get( $cache_key, $cache_group );
wpr_translate_prime_custom_fields_preferences_cache() runs from the plugin bootstrap. It compares the md5 of the config file with the cached payload, then with the stored option, and only re-reads the JSON (wpr_translate_sync_custom_fields_preferences()) when both are stale.
Extending: after writing to the same config, call wpr_translate_sync_custom_fields_preferences(), or invalidate with wp_cache_delete( ‘wpr_cf_preferences’, ‘wpr_translate’ ).
The rule set itself (wpr_translate_get_custom_field_rules()) is held in a request global; wpr_translate_clear_custom_field_rules_cache() drops it. On a fresh install the rules are read from the theme JSON and stored the first time they are requested.
Generated MO Directory
wpr_translate_get_custom_translations_directory() stores the chosen generated-files directory in the wpr_translate_custom_directory option. On each call it checks that the stored path still exists and is still one of the candidates (child theme, parent theme, plugin); if not, for example after a theme switch, it selects and stores a new one.
Per-Language Transients – Theme Lists
The WPResidence theme caches large lookup lists as transients. On a multilingual site each language has its own set: the theme helper wpestate_set_transient_name_multilang() appends the active language to the transient name.
The select-list base names:
wpestate_get_action_select_list wpestate_get_category_select_list wpestate_get_city_select_list wpestate_get_area_select_list wpestate_get_county_state_select_list wpestate_get_status_select_list wpestate_get_features_select_list
includes/admin/theme-transients.php provides wpr_translate_admin_clear_theme_search_transients( $context ), which deletes each base key and its per-language variants (language slugs and codes from wpr_translate_get_active_languages(), plus ICL_LANGUAGE_CODE when defined). In 1.0.9 nothing in the plugin calls it, so translated labels in these lists refresh only when the transients expire or the WPResidence cache is cleared.
Elementor taxonomy widgets prime their own transient through the same helper (includes/elementor-widget-compat.php):
$primed_transient_key = 'wpestate_elementor_tax_';
if ( function_exists( 'wpestate_set_transient_name_multilang' ) ) {
$primed_transient_key = (string) wpestate_set_transient_name_multilang( $primed_transient_key );
}
set_transient( $primed_transient_key, $primed_terms, 60 * 60 * 6 );
TTL is 6 hours. Invalidation is lazy.
Theme-Wide Cache Purge
The theme purge (wpestate_delete_cache() in wpresidence-core) deletes every _transient_wpestate* option. Administrators trigger it from the Clear WpResidence Cache admin-bar link.
WPEstate Translate runs the same purge on its own through wpr_translate_purge_wpestate_cache() in includes/admin/cache-purge.php, which calls wpestate_delete_cache() directly. It runs when a bulk automatic translation ends (AJAX wp_ajax_wpr_translate_purge_cache, manage_options + nonce wpr_translate_purge_cache), when a post translated with Auto translate is published, and after the last batch of Automatic Translate on strings. See Cache Purge & Reset Tools (Developer).
Query Filter & Cache Interaction
includes/query-filter.php applies the active language to front-end WP_Query via pre_get_posts (priority 9) and the_posts, using the _wpr_language post meta. Caching considerations:
- Cached query results must be language-scoped. Include the active language in the cache key: use wpestate_set_transient_name_multilang(), or get the code from wpr_translate_get_current_language().
- suppress_filters bypasses language filtering. Queries with ‘suppress_filters’ => true are left untouched. Never do this silently inside a cached helper.
- Some post types are never filtered. elementor_library and nav_menu_item are skipped; wpestate_booking, wpestate_invoice, wpestate_message and wpestate_search are treated as language-neutral.
Settings-Errors Transients
Admin screens use short-lived (30-second) settings_errors transients to carry notices across a redirect:
set_transient( 'settings_errors', get_settings_errors(), 30 );
These are unrelated to content caching and self-expire.
Extension Points & Recipes
- Language-dependent cached helper – key it with wpestate_set_transient_name_multilang( ‘your_base_key’ ). If the base name starts with wpestate, the theme purge clears it too.
- After writing string rows directly – call wpr_translate_runtime_strings_changed().
- After bulk relationship changes outside the meta API – call wpr_translate_relationship_flush_family_cache().
- Clearing the select-list transients – call wpr_translate_admin_clear_theme_search_transients( ‘wpresidence_admin’ ).
- Flushing the whole theme cache – call wpestate_delete_cache() from PHP, or use the admin-bar link.
Non-Latin Safety
Language codes go through sanitize_key(); original strings are hashed with md5() before being used as keys. Cyrillic, Arabic and CJK data cache the same way as Latin data.
Gotchas
- Static caches do not clear on option save. Code that updates a value mid-request and reads it back through a static cache can see the old value in the same request.
- Transient TTLs are not adaptive. The 6-hour TTL on Elementor primed terms means a renamed term can take up to 6 hours to show in some widgets. Clear explicitly after bulk term updates.
- The theme purge is whole-site. Use it after batch jobs, not on every save.
- Reactivation does not clear transients. Stale caches survive deactivate/activate.
Further Reading
- Database Schema – the data the caches front.
- Cache Purge & Reset Tools – the admin-side counterparts to this reference.
- WP_Query Language Filtering – how pre_get_posts interacts with cached results.
- Automatic Translation (OpenAI, Google, DeepL, Azure) – the bulk runs that call the purge endpoint.
For broader context on running a multi-language real estate website on WPResidence, see the product page.