WP Residence Help WP Residence Help

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

Menu Translation & Language-Specific Menu Locations — Developer Guide

2 views 0

This article documents how WPResidence Translate attaches languages to WordPress nav menus, stores per-language theme-location assignments, tracks menu translation groups in the database, 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.

File Map

File Role
includes/nav-menu-locations.php Storage + sanitizer for the language-aware menu location map.
includes/nav-menu-locations-cleanup.php Removes deleted menus from the stored assignments.
includes/nav-menu-locations-frontend.php Front-end fallback logic and language switcher injection into menus.
includes/nav-menu-translation-sync.php Reads/writes translation records for nav menus in the translations table.
includes/admin/nav-menus.php Admin toolbar & language filter on the nav-menus screen.
includes/admin/nav-menus-helpers.php Language overview, meta key, and filter helpers shared by admin code.
includes/admin/nav-menus-language.php Per-menu language resolution and selection adjustment.
includes/admin/views/menu-synchronization.php Admin view for the Menu Synchronization page.

Menu Language Metadata

The language of a nav menu is stored as term meta on the nav_menu taxonomy term. The meta key is returned by wpr_translation_get_nav_menu_language_meta_key(). Effective resolution:

function wpr_translation_get_nav_menu_language_code( $menu_id ) {
    // Stored language in term meta, otherwise the plugin default.
}

wpr_translation_store_nav_menu_language( $menu_id ) writes the language code on menu create/update. Deletion is handled by the cleanup layer.

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_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 uses the current filter (from the wpr_language query arg) to hide menus belonging to other languages when a language chip is active on the admin screen. Capability gate: edit_theme_options.

Screenshot: Nav menus admin screen with the WPR language toolbar and an active language filter

Language-Aware Location Map

Per-language location assignments are stored in a dedicated option, not in theme_mods_*. Option name:

wpr_translate_nav_menu_locations

Accessed via wpr_translation_get_nav_menu_locations_option_name(). Shape:

array(
    'primary'  => array( 'en' => 12, 'fr' => 34 ),
    'footer'   => array( 'en' => 13, 'fr' => 35 ),
)

Normalization is done by wpr_translation_normalize_nav_menu_locations_map() — every location key goes through sanitize_key(), every language code through strtolower( sanitize_key() ), and every menu ID through absint(). Entries with invalid parts are dropped.

Storage API

Function Role
wpr_translation_get_stored_nav_menu_locations() Reads the option and returns a normalized map.
wpr_translation_update_nav_menu_locations_storage( $locations ) Writes the normalized map back to the option.
wpr_translation_capture_nav_menu_locations( $locations ) Captures assignments coming out of the nav menu save flow.
wpr_translation_store_submitted_nav_menu_locations( $menu_id, $menu_data ) Stores submitted per-menu location assignments.
wpr_translation_filter_theme_mod_nav_menu_locations( $value ) Intercepts theme_mod_nav_menu_locations so WordPress core reads the language-appropriate menu.
wpr_translation_cleanup_nav_menu_locations_on_delete( $menu_id ) Hook target on menu deletion that strips the menu from every language entry.

Front-End Fallback

wpr_translation_filter_nav_menu_locations_option() is registered on the dynamic filter:

add_filter(
    'option_' . wpr_translation_get_nav_menu_locations_option_name(),
    'wpr_translation_filter_nav_menu_locations_option'
);

On non-admin requests, for each location that has a default-language assignment but no entry for the requested language, the default-language menu ID is mirrored into the active language slot. This guarantees the frontend always resolves to a menu even when a translator has not assigned one per language.

Current Language Detection

  • wpr_translation_get_requested_nav_menu_language_code() — front-end detection of the active language.
  • wpr_translation_get_current_nav_menu_filter() — admin-side reading of the wpr_language query var.
  • wpr_translation_adjust_selected_nav_menu() — keeps the selected menu consistent with the active language filter on the admin screen.
  • wpr_translation_enforce_nav_menu_language_context() — prevents cross-language menu selection drift.

Menu Translation Records

Nav menus also participate in the main translations table using element type tax_nav_menu. Helpers in nav-menu-translation-sync.php:

wpr_translation_get_nav_menu_translation_trid( $menu_id )
wpr_translation_upsert_nav_menu_translation( $menu_id, $language_code, $trid, $source_language, $is_original )
wpr_translation_ensure_nav_menu_translation_record( $menu_id, $language_code )
wpr_translation_sync_nav_menu_translations_from_locations( $locations )

Table: {$wpdb->prefix}wpestate_translation_translations. Relevant columns: element_id, element_type = 'tax_nav_menu', trid, language_code, source_language_code, post_status, translator_id, original.

Records are upserted: existing rows are updated via $wpdb->update(); missing rows are inserted. source_language_code is set to null for originals and for rows whose source language is empty.

Screenshot: Database row from wp_wpestate_translation_translations showing an element_type of tax_nav_menu

Language Switcher Injection

wpr_translate_get_language_switcher_menu_markup( $location ) generates the HTML for injecting the language switcher directly into a menu slot, provided wpr_translate_language_switcher_widget_display() is available. Returns an empty string if the switcher function is missing.

Menu Synchronization

The admin view lives in includes/admin/views/menu-synchronization.php. It receives a $data array with:

  • default_menus, other_menus — menu dropdown options per language.
  • default_menu_items, other_menu_items — flattened menu item lists with title, url, depth.
  • selected_default_menu, selected_other_menu.
  • fallback_link — used when a translated target is missing.
  • page_slug — admin page slug, defaults to wpr-translate-menu-sync.

The sync workflow copies structure, re-resolves post and term targets to their translated equivalents (via the translation group helpers), and keeps custom links verbatim. Missing translations preserve the source title instead of leaving a blank.

Cleanup on Deletion

wpr_translation_cleanup_nav_menu_locations_on_delete( $menu_id ) is hooked on menu deletion. It loads the option, removes every occurrence of the deleted ID from the language sub-array, drops empty location entries, and writes the map back only when it changed.

Extension Points

  • Per-location defaults — extend wpr_translation_filter_nav_menu_locations_option() via a custom filter to implement custom fallback rules per location.
  • Extra element types — if you add custom menu-like structures, reuse the tax_nav_menu element-type pattern to reuse the translations table.
  • Sync customization — override the synchronization routine by shadowing its helper functions under function_exists() guards in a child theme include.

Gotchas

  • option_{name} is a read filter. Writes go through the storage helpers directly — do not rely on the option filter to persist fallbacks.
  • Menu IDs are term IDs. Term meta (not post meta) stores the language code.
  • The cleanup hook is vital. Without it, deleted menus leave dangling IDs that render as empty locations.
  • Translation records for nav menus use element_type = 'tax_nav_menu' — querying by post type alone will miss them.
  • Language codes are always lowercased via strtolower( sanitize_key() ). Comparisons in your own code should match.

Further Reading

  • Translation Linking (trid system) — how the trid column works.
  • Database Schema — full schema of the six plugin tables.
  • Managing Languages — the language registry that the menu map keys on.

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

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