WP Residence Help WP Residence Help

  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API
Home / WPEstate Translate Plugin / Menu Translation & Language-Specific Menu Locations — Developer Guide

Menu Translation & Language-Specific Menu Locations — Developer Guide

192 views 0

This article documents how WPEstate Translate 1.0.9 attaches languages to WordPress nav menus, stores per-language theme-location assignments and synchronizes menu structure. It is the implementation-level companion to the user article. Product context is on our multi-language real estate website page.

CONTENT

  • File Map
  • Menu Language Metadata
  • Admin Integration
  • Language-Aware Location Map
  • Storage API
  • Front-End Fallback
  • Default Language and Language List Changes
  • Current Language Detection
  • Menu Translation Records (retired)
  • Language Switcher Injection
  • Menu Synchronization
  • Extension Points
  • Gotchas
  • Further Reading

File Map

File Role
includes/nav-menu-assignments-storage.php Theme-scoped storage envelope, normalizer, read/write of the assignment map.
includes/nav-menu-assignments-read.php Side-effect-free resolution of one location + language slot.
includes/nav-menu-assignments-commands.php Validated, atomic save commands and the core nav_menu_locations projection.
includes/nav-menu-assignments-language.php Changing a menu’s language and moving the assignments that reference it.
includes/nav-menu-assignments-default-language.php Reactions to default language changes and removed languages.
includes/nav-menu-assignments-all-themes.php Atomic writes across every stored theme map.
includes/nav-menu-locations.php Public wrappers (kept for compatibility) and the core hooks.
includes/nav-menu-locations-cleanup.php Removes deleted menus from stored assignments.
includes/nav-menu-locations-frontend.php Front-end fallback and language switcher injection into menus.
includes/nav-menu-language-request.php Requested language for front-end menu reads.
includes/nav-menu-translation-sync.php Retired trid helpers, kept callable.
includes/admin/nav-menus.php, nav-menus-helpers.php, nav-menus-language.php, nav-menus-language-save.php, nav-menus-assignment-notices.php Admin toolbar, language filter, per-menu language save and admin notices on the nav-menus screen.
includes/admin/menu.php + includes/admin/views/menu-synchronization.php Menu Synchronization page handler and view.

Menu Language Metadata

A nav menu’s language is term meta wpr_menu_language on the nav_menu term (key from wpr_translation_get_nav_menu_language_meta_key()). wpr_translation_get_nav_menu_language_code( $menu_id ) returns the stored code, otherwise the current default language. A menu with no meta therefore follows the default language.

wpr_translation_store_nav_menu_language( $menu_id, $menu_data ) runs on wp_create_nav_menu and wp_update_nav_menu (priority 10). It reads $_POST['wpr_menu_language'] (nonce update-nav_menu), requires edit_theme_options and calls wpr_translate_menu_assignments_change_menu_language(). When the posted code equals the default language and the menu has no meta yet, nothing is written, so the menu keeps following the default.

Admin Integration

wpr_translation_setup_nav_menu_integration() is hooked on load-nav-menus.php and registers:

add_action( 'admin_notices', 'wpr_translation_render_nav_menu_toolbar' );
add_action( 'admin_notices', 'wpr_translate_menu_assignments_render_notices', 5 );
add_action( 'admin_enqueue_scripts', 'wpr_translation_enqueue_nav_menu_toolbar_assets' );
add_filter( 'wp_get_nav_menus', 'wpr_translation_filter_nav_menus_by_language', 10, 2 );

The wp_get_nav_menus filter hides menus of other languages while a language chip (wpr_language query arg) is active. Failed assignment writes are queued as admin notices instead of failing silently.

Language-Aware Location Map

Assignments are stored in the option wpr_translate_nav_menu_locations (wpr_translation_get_nav_menu_locations_option_name()) as a versioned envelope scoped by theme stylesheet:

array(
    'version' => 1,
    'themes'  => array(
        'wpresidence-child' => array(
            'primary' => array( 'en' => 12, 'fr' => 34 ),
            'footer'  => array( 'en' => 13, 'fr' => 35 ),
        ),
    ),
)

Only the active theme’s map (get_stylesheet()) is read and written; maps of other themes are preserved. Location keys go through sanitize_key(), language codes through strtolower( sanitize_key() ), menu IDs through absint(). A site without the envelope reads core nav_menu_locations as default language assignments.

Storage API

Function Role
wpr_translation_get_stored_nav_menu_locations() Wrapper for wpr_translate_menu_assignments_read_map(): the active theme’s normalized map.
wpr_translation_update_nav_menu_locations_storage( $locations ) Wrapper for wpr_translate_menu_assignments_save_map(): validates the complete map, writes it atomically and returns true or WP_Error.
wpr_translate_menu_assignments_save_slot( $location, $language, $menu_id ) Saves or clears ($menu_id = 0) one slot.
wpr_translate_menu_assignments_resolve_slot( $location, $language, $frontend ) Returns the menu ID for a slot; on the front end falls back to the default language slot. Returns WP_Error for a missing menu or a language mismatch.
wpr_translation_capture_nav_menu_locations( $locations ) On pre_set_theme_mod_nav_menu_locations.
wpr_translation_store_submitted_nav_menu_locations( $menu_id, $menu_data ) On wp_create_nav_menu / wp_update_nav_menu (priority 20).
wpr_translation_filter_theme_mod_nav_menu_locations( $value ) On theme_mod_nav_menu_locations, so core reads the menu for the current language.
wpr_translation_cleanup_nav_menu_locations_on_delete( $menu_id ) On wp_delete_nav_menu; removes the menu from every stored theme map.

A save is validated before it is written. Rows carried over unchanged from the previous map are accepted even if stale, so old data never blocks an unrelated write.

Front-End Fallback

wpr_translation_filter_nav_menu_locations_option() is registered on 'option_' . wpr_translation_get_nav_menu_locations_option_name(). It unwraps the envelope to the active theme’s map and, on non-admin requests, copies the default language menu ID into any location that has no entry for the requested language.

The front-end get_terms language filter skips the nav_menu taxonomy, so wp_get_nav_menus() returns all menus on secondary languages (Studio / Elementor navigation widgets rely on this).

Default Language and Language List Changes

The default language and the language list are both saved in WPEstate Translate > Languages (option wpr_translate_languages). Two hooks react when that option changes:

Hook on update_option_wpr_translate_languages Behaviour
wpr_translate_menu_assignments_handle_default_language_change (10) A metadata-free menu assigned to the old default is pinned to the old default (wpr_menu_language written), so it stays in its language and the new default keeps its own menu.
wpr_translate_menu_assignments_prune_removed_languages (20) Drops assignment rows whose language was removed from the Languages Manager.

Current Language Detection

  • wpr_translation_get_requested_nav_menu_language_code() – front-end active language.
  • wpr_translation_get_current_nav_menu_filter() – admin wpr_language query var.
  • wpr_translation_adjust_selected_nav_menu() – on nav_menu_selected_id, keeps the selected menu in the active language.
  • wpr_translation_enforce_nav_menu_language_context() – on load-nav-menus.php (priority 5), prevents cross-language selection drift.

Menu Translation Records (retired)

Nav menus no longer write rows to {$wpdb->prefix}wpestate_translation_translations. Menu language is owned by the wpr_menu_language meta and the assignment map. The helpers remain callable for compatibility:

Function 1.0.9 behaviour
wpr_translation_get_nav_menu_translation_trid( $menu_id ) Still reads a legacy tax_nav_menu trid if one exists.
wpr_translation_upsert_nav_menu_translation(), wpr_translation_ensure_nav_menu_translation_record() Return false; no write.
wpr_translation_sync_nav_menu_translations_from_locations( $locations ) Returns the normalized map; no family membership is created.

Language Switcher Injection

wpr_translate_inject_language_switcher_into_menu() on wp_nav_menu_items reads wpr_translate_settings['menu_language_switcher'][ $location ] (before or after, set in WPEstate Translate > Settings) and adds the markup from wpr_translate_get_language_switcher_menu_markup( $location ). Menus printed without theme_location are matched to a location by menu term ID. See the Language Switcher Widget developer reference.

Menu Synchronization

wpr_translate_admin_handle_menu_sync_submission() runs on admin_init when wpr_menu_sync_action=sync is posted (capability manage_options, nonce wpr_menu_sync) and calls wpr_translate_admin_sync_menu_structures( $source_menu_id, $target_menu_id, $fallback_url ). It redirects to page=wpr-translate-menu-sync&settings-updated=true so success and error notices show.

  1. The target menu must have wpr_menu_language meta, otherwise wpr_menu_sync_language error.
  2. Every item payload is built first; if none can be built the target menu is left untouched (wpr_menu_sync_no_items).
  3. Only then are target items deleted and rebuilt, keeping the parent/child structure.
Source item type Result in the target menu
post_type Linked to the translated post (wpr_translate_relationship_resolve_post()) with its title; if no translation, a custom link to the fallback URL (or #).
taxonomy Linked to the translated term with a language-localized URL (wpr_translate_relationship_resolve_term()); if no translation, a custom link to the fallback URL.
custom Same URL.

Titles of custom and taxonomy items are looked up in the string catalog under the active theme’s text domain (wpr_translate_admin_get_theme_domain( wp_get_theme() )).

Extension Points

  • Writes – use wpr_translation_update_nav_menu_locations_storage() or wpr_translate_menu_assignments_save_slot() and check for WP_Error. Do not update_option() the envelope directly.
  • Reads – use wpr_translate_menu_assignments_resolve_slot() for one slot with fallback.
  • Overrides – most public helpers are wrapped in function_exists() guards and can be shadowed from a child theme include loaded earlier.

Gotchas

  • option_{name} is a read filter. It returns the active theme’s map, not the raw envelope.
  • Menu IDs are term IDs; the language is term meta, not post meta.
  • A menu with no wpr_menu_language follows the default language. Menu Synchronization still requires the target menu to have explicit meta.
  • Switching themes gives an empty map for the new stylesheet; the previous theme’s assignments are kept.
  • Language codes are always lowercased via strtolower( sanitize_key() ).

Further Reading

  • Translation Linking (trid system) – how posts and terms are linked.
  • Database Schema – the plugin tables.
  • Managing Languages – the language registry the menu map keys on.

See also the main multi-language real estate website page for the product-level view.

WPEstate 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
MLSImportWordPress IDX Plugin for MLS ListingsImport MLS listings into your WordPress website, customize your property pages, and keep your inventory updated.Start free 30-day trial →

Help Categories

  • 51 Getting Started
  • 72 Installation & Setup
  • 243 Installation FAQ
  • 18Agent, Agency & Developers
  • 5Blog Posts & Blog Lists
  • 39Elementor Shortcodes Built-In
  • 56FAQ
  • 15Footer
  • 37Header
  • 2IDX & MLSImport
  • 23Maps & Location Settings
  • 21Multi-Language - Third Party Plugins
  • 7Other Third party Plugins
  • 21Pages
  • 4Payments & Monetization
  • 20Property Lists, Categories & Archive
  • 37Property Pages & Layouts
  • 32Search & Filtering
  • 162Technical how to | Custom Code
  • 8Technical: Actions and filters
  • 7Technical: Child Theme
  • 86Theme Options & Global Settings
  • 6Translations & Languages
  • 16WPBakery Shortcodes
  • 50WPEstate Translate Plugin
  • 51WPResidence / WPEstate CRM
  • 50WPResidence 5.0 Documentation
  • 9WPResidence Elementor Studio

Join Us On

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