Fix WordPress Menu Locations Not Saving Polylang Conflict

Summary

A recent WordPress site could not persist Menu Display Location or Manage Locations settings. Every save reverted to “no location selected”. The issue was traced to the Polylang multilingual plugin, which interferes with the core menu‑location handling when certain filters are active.

Root Cause

  • Polylang registers a filter on wp_get_nav_menu_items and wp_nav_menu.
  • The filter assumes a single language context and unintentionally overwrites the theme_location meta saved by WordPress.
  • When Polylang’s pll_the_languages function is called on the admin page (via its admin UI assets), it forces a language switch before WordPress writes the menu‑location option, causing the saved value to be discarded.
  • The bug only appears when Polylang’s “Sync menus” option is enabled, because it tries to synchronize menu locations across languages and resets the location on each request.

Why This Happens in Real Systems

  • Plugins often hook into core actions/filters without fully checking the execution context (admin vs. front‑end, AJAX vs. normal POST).
  • Multilingual plugins need to manipulate menus to provide language‑specific navigation, which leads to complex state handling.
  • When a plugin modifies $_POST or option values after WordPress has validated them, the original data is lost, resulting in “cannot save” symptoms.

Real-World Impact

  • Site editors cannot assign navigation menus, breaking site navigation for visitors.
  • Theme customizers that rely on menu locations show empty placeholders, leading to broken UI previews.
  • Customer support tickets increase dramatically after a multilingual plugin upgrade, as non‑technical users cannot understand why their menus disappear.
  • SEO suffers if navigation links are missing, potentially reducing crawlability.

Example or Code (if necessary and relevant)

// Polylang hook that caused the issue (simplified)
add_filter( 'wp_get_nav_menu_items', function( $items, $menu, $args ) {
    if ( is_admin() && ! wp_doing_ajax() ) {
        // Incorrectly resets the location for the current language
        $args->theme_location = '';
    }
    return $items;
}, 10, 3 );

How Senior Engineers Fix It

  • Identify the conflicting filter using the Debug Bar or by temporarily disabling plugins.

  • Patch the plugin: add a guard clause to skip the menu‑location rewrite when $_POST['action'] is save-menu or when is_admin() and ! defined('DOING_AJAX').

  • Upgrade Polylang: the maintainers released a fix that checks is_network_admin() and respects wp_is_post_autosave() before altering menu data.

  • Add a mu‑plugin that restores the saved location after Polylang runs:

    add_action( 'after_setup_theme', function() {
      if ( is_admin() && isset( $_POST['menu-location'] ) ) {
          update_option( 'theme_mods_' . get_option( 'stylesheet' ), wp_unslash( $_POST['menu-location'] ) );
      }
    }, 20 );
  • Disable “Sync menus” in Polylang settings if multilingual menu sync is not required.

  • Write automated regression tests that simulate saving a menu with a selected location under different language contexts.

Why Juniors Miss It

  • They focus on the UI error (“cannot save”) without checking plugin interaction logs.
  • Lack of experience with WordPress hooks leads them to assume the core is at fault.
  • Missing knowledge of multilingual plugins and the side‑effects of their filters.
  • Often skip systematic deactivation of plugins or the use of WP_DEBUG_LOG, so the subtle overwrite goes unnoticed.

Leave a Comment