WP Residence Help WP Residence Help

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

Menu Translation & Language-Specific Menu Locations — Developer Guide

69 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.

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.

 

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.

WPEstate / 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

Help Categories

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

Join Us On

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