WP Residence Help WP Residence Help

  • WpEstate
  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API
Home / 30. WPResidence Translate Plugin / Meta Sync Across Language Variants — Developer Guide

Meta Sync Across Language Variants — Developer Guide

5 views 0

This article covers the meta synchronization pipeline in thewpresidence-translate plugin (text domain wpr-translate): how rules are loaded, how they are normalized, which hooks drive the synchronization, and what safeguards prevent infinite loops. It is the companion to the user-facing Custom Field Rules article and feeds into the wider multi-language real estate website architecture.

Files Involved

File Role
includes/meta-sync.php Runtime synchronization on save_post and meta change hooks.
includes/custom-field-rules.php Rule loader, override storage, reload orchestration, state reporting.
includes/custom-field-rules-storage.php Normalization, grouping, and legacy rule conversion.
includes/custom-fields-sync.php JSON config path resolution, hash tracking, preferences cache.
includes/admin/views/settings-custom-fields.php Admin UI view rendering the Custom Field Rules page.

Behavior Keywords

Four keywords are accepted throughout the subsystem. They are normalized in wpr_translate_normalize_meta_field_behavior_value():

copy        // propagate on every save_post of any sibling
copy-once   // propagate only when a translation is first created
translate   // never propagated; language-specific value expected
ignore      // never touched

The legacy alias copy_once is rewritten to copy-once. Unknown values are dropped.

Rule Source of Truth

Defaults come from a JSON file in the active theme:

get_stylesheet_directory() . '/wpr/custom-fields-config.json'
// falls back to
get_template_directory()   . '/wpr/custom-fields-config.json'

The resolver is wpr_translate_get_custom_fields_config_path() in custom-fields-sync.php. The JSON is read by wpr_translate_read_custom_field_rules_file() in custom-field-rules.php, which accepts either a top-level array or an object with a custom_fields key. Each rule can carry:

{
  "id":          "property_price",
  "label":       "Price",
  "description": "Property asking price",
  "action":      "copy",
  "post":        "estate_property"
}

The newer value key is accepted as an alias for action.

Normalization & Grouping

wpr_translate_normalize_custom_field_rules() sanitizes every entry, generates a unique uid per post type via wpr_translate_build_unique_rule_id(), and defaults the action to copy when missing. wpr_translate_group_custom_field_rules_by_post() groups rules by post type and pushes estate_property to the top of the result array so the admin UI renders Property fields first.

Options Used

Option Contents
wpr_cf_rules_defaults Normalized default rules + file hash, path, and last-loaded timestamp.
wpr_cf_rule_overrides Admin-changed behaviors keyed by rule uid.
wpr_cf_file_state Status/error payload for the JSON loader surfaced on the admin page.
wpr_cf_preferences Secondary cache maintained by custom-fields-sync.php (hash + raw rules).

Overrides are merged on top of defaults in wpr_translate_get_custom_field_rules(). Each rule in the merged set carries a source of file or override, and the original file_action is preserved for UI display.

Screenshot: The Custom Field Rules status panel showing rules file path, current hash, last-loaded hash, status and last reloaded timestamp

Runtime Hooks

Two callbacks drive synchronization, both in meta-sync.php:

Hook Callback Purpose
save_post wpr_translate_handle_save_post_meta_sync() Full-sweep sync of every copy meta key when any post in the translation set is saved.
updated_postmeta / added_postmeta / deleted_postmeta wpr_translate_handle_post_meta_change_sync() Single-key sync for code paths that write meta directly without calling wp_update_post().

Both callbacks short-circuit on autosaves, revisions, auto-draft posts, and non-existent post types. They also respect the opt-out filter:

apply_filters( 'wpr_translate_should_sync_post_meta', true, $post_id, $post_type, $post );

Sibling Resolution

Translation siblings are resolved by wpr_translate_resolve_meta_sync_translation_post_ids() using a two-tier strategy:

  1. Meta-first: reads wpr_translated_original_post_id from the current post to find the canonical anchor, then runs a get_posts() query with suppress_filters = true for every post pointing at the same anchor.
  2. TRID fallback: if meta linkage yielded only the current post, the resolver calls wpr_translation_get_translation_group() with the element type from wpr_translation_get_element_type( $post_type ).

The suppress_filters flag is critical — the sibling lookup must not be language-filtered by query-filter.php, or it would never see posts in other languages.

Full-Sweep Sync: wpr_sync_meta_fields()

Called from the save_post entry point. Pseudocode:

$behaviors = wpr_translate_get_meta_field_behaviors();
$copy_keys = keys where behavior === 'copy';
$siblings  = wpr_translate_resolve_meta_sync_translation_post_ids( $post_id, $post_type );

foreach ( $copy_keys as $meta_key ) {
    $source_values = get_post_meta( $source_post_id, $meta_key, false );
    foreach ( $siblings as $target ) {
        if ( serialized($source) !== serialized($target_current) ) {
            wpr_translate_meta_sync_enter_internal_write();
            delete_post_meta( $target, $meta_key );
            foreach ( $source_values as $v ) add_post_meta( $target, $meta_key, $v );
            wpr_translate_meta_sync_exit_internal_write();
        }
    }
}

Values are compared through maybe_serialize( maybe_unserialize( $v ) ) to avoid redundant rewrites when the shape changes but the semantic value does not.

Re-Entry Guards

  • Per-request static guard keyed by post_type:post_id inside wpr_sync_meta_fields(). Prevents a sibling save from recursing back to the source.
  • Internal-write depth counter via wpr_translate_meta_sync_enter_internal_write() / exit_internal_write(). The single-key handler checks wpr_translate_meta_sync_is_internal_write() and bails out when the change event was triggered by our own write.
  • Availability check via wpr_translation_is_translation_post_available() — trashed or unpublished translations are skipped.

Screenshot: Code editor showing the save_post hook registration and the enter/exit internal_write wrapper pattern

Extension Points

  • apply_filters( 'wpr_translate_should_sync_post_meta', $should, $post_id, $post_type, $post ) — veto sync per post or post type.
  • apply_filters( 'wpr_translate_meta_field_behaviors', $map ) — final chance to mutate the behavior map (add/remove keys) before sync runs.
  • apply_filters( 'wpr_translate_custom_field_rules_file', $path ) — override the JSON defaults file path, e.g. for a plugin shipping its own defaults.

Admin Page Wiring

The view at includes/admin/views/settings-custom-fields.php receives $data['custom_field_rules'] (the merged map) and $data['custom_field_state'] (reload status). The reload button posts back with wpr_cf_action=reload and nonce action wpr_cf_reload; the handler calls wpr_translate_reload_custom_field_rules_from_file() which resets wpr_cf_rule_overrides, persists new defaults, and updates wpr_cf_file_state.

AJAX dropdown changes are persisted via wpr_translate_save_custom_field_overrides(), which sanitizes keys through sanitize_text_field() (UTF-8 safe) and validates actions against the copy|translate|copy-once|ignore allowlist.

Legacy Import

wpr_translate_convert_legacy_custom_fields_to_rules() in custom-field-rules-storage.php accepts the WPResidence wp_estate_custom_fields option format (indexed arrays with [0]=name, [1]=label, [2]=type, [4]=choices) and emits normalized rules. This is merged in on reload so older installations keep expected defaults for their theme-defined custom fields.

Related Reading

  • Translation Linking (trid system) — background on how sibling posts are glued together.
  • WP_Query Language Filtering — explains why sibling queries must use suppress_filters.
  • Database Schema — the translation table consulted by the TRID fallback path.

For product context, visit the multi-language real estate website landing page.

30. WPResidence Translate Plugin

Related Articles

  • String Scanner — Developer Guide
  • The String Scanner
  • Gettext Pipeline & MO Files — Developer Guide
  • Gettext & MO Files — Making Translations Appear on the Front End

WP Residence Documentation

  • 01. Getting Started
    • How to Get Support
    • Get your buyer license code.
    • Use SSL / https
    • Server / Theme Requirements
  • 02. Installation & Setup
  • 03. Installation FAQ
  • 06. Search & Filtering
    • Advanced Search Display Settings
    • Advanced Search Form
    • Geolocation Search for Half Map
    • Save Search Theme Options
    • Advanced Search Colors
  • 09. Agent, Agency & Developers
  • 08. Property Pages & Layouts
  • 07. Property Lists, Categories & Archive
  • 13. WPResidence Elementor Studio
  • 10. Blog Posts & Blog List
  • 11. Shortcodes
    • Contact Form
    • Featured Agency/Developer
    • Membership Packages
    • Testimonials
    • Google Map with Property Marker
    • Listings per Agent, Agency or Developer
    • Display Categories
    • Agent List
    • Recent Items Slider
    • Recent items
    • List Properties or Articles by ID
    • Featured Agent
    • Featured Article
    • Featured Property
    • Login & Register Form
    • Icon Content Box Shortcode
  • 12. Widgets
  • 04. Theme Options & Global Settings
    • General Settings
    • User Types Settings
    • Appearance
    • Logos & Favicon
    • Header
    • Footer Style and Colors
    • Price & Currency
    • Property Custom Fields
    • Features & Amenities
    • Listing Labels
    • Theme Slider
    • Permalinks
    • Splash Page
    • Social & Contact
    • Map Settings
    • Pin Management
    • How read from file works
    • General Design Settings
    • Custom Colors Settings
    • Header Design & Colors
    • Mobile Menu Colors
    • User Dashboard Colors
    • Print PDF Design
    • Property, Agent, Blog Lists Design Settings
    • Sidebar Widget Design
    • Font management
    • How to add custom CSS
    • Custom Property Card Unit – Beta version
    • Email Management
    • Import & Export theme options
    • reCaptcha settings
    • YELP API Integration
    • iHomefinder Optima Express IDX
    • MEMBERSHIP & PAYMENT Settings
    • Property Submission Page
    • PayPal Setup
    • Stripe Setup
    • Wire Transfer Payment Method
  • 20. Translations & Languages
  • 26. FAQ
  • 10. Pages
  • 11. Header
  • 12. Footer
  • 05. Maps & Location Settings
  • 18. Payments & Monetization
  • Plugins
    • 19. Included Plugins
    • 22. Third Party Plugins – IDX Compatibility
    • 21. Third-Party Plugins – Multi-Language
    • 23. Third party Plugins – Other
  • Technical
    • 24. Technical how to | Custom Code Required
    • 25. Technical: Child Theme

Join Us On

Powered by WP Estate - All Rights Reserved
  • WpEstate
  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API