How to intercept already localized scripts

If a plugin uses some script (prominent example: jQuery UI Datepicker), but you’re not happy with how the script renders the output, then there’re two possibilities:

1. Unregister the script > Add your own version

So first you’d need to check the handle, then find the priority and the hook (wp_enqueue_scripts, login_enqueue_scripts, etc.) … you know the drill.

Read More

2. Change the jQuery plugin parameters

Normally – if the plugin isn’t crap – it pushes through the parameters from PHP to JS using

wp_localize_script( $handle, $object_name, array( 
    // data
) );

Now this is a smart way of adding your data to a JS script, but … it’s not filterable by default. Neither WP_Scripts nor WP_Dependencies offers any filter users can later utilize

Question: How can we filter the arguments/parameters that are moved from PHP to Javascript using wp_localize_script?

Related posts

4 comments

  1. wp_localize_script() calls the method localize() on the global variable $wp_scripts. We can set this variable to an instance of a child class of WP_Scripts:

    class Filterable_Scripts extends WP_Scripts
    {
        function localize( $handle, $object_name, $l10n )
        {
            $l10n = apply_filters( 'script_l10n', $l10n, $handle, $object_name );
            return parent::localize($handle, $object_name, $l10n);
        }
    }
    
    add_action( 'wp_loaded', function() {
        $GLOBALS['wp_scripts'] = new Filterable_Scripts;
    });
    

    The theme customizer doesn’t use that, it creates a separate instance of WP_Scripts (see wp-admin/customize.php). It might be possible to replace that too:

    add_action( 'customize_controls_init', function() {
        $GLOBALS['wp_scripts'] = new Filterable_Scripts;
        $GLOBALS['wp_scripts']->registered = $GLOBALS['registered'];
    });
    

    None of this has been tested, just an idea.

  2. @toscho great implementation. Tested and true. Here is a slightly modified version, which also passes the $handle and $object_name so you can filter only when needed.

    class Filterable_Scripts extends WP_Scripts
    {
        function localize( $handle, $object_name, $l10n )
        {
            $l10n = apply_filters( 'script_l10n', $l10n, $handle, $object_name );
            return parent::localize($handle, $object_name, $l10n);
        }
    }
    
    add_action( 'init', function() {
        $GLOBALS['wp_scripts'] = new Filterable_Scripts;
    });
    
    add_filter('script_l10n', 'se108362_example_filter', 10 , 3);
    
    // Example
    function se108362_example_filter($l10n, $handle, $object_name ) {
        if('js-handle' == $handle && 'jsVariable' == $object_name) {
           return 'Something Else';
        }
        return $l10n;
    }
    
  3. The accepted answer is great! But I ran into a problem that Advanced Custom Fields stopped working in the backend due to a javascript error. After digging for a few hours I came to the conclusion that the Filterable_Scripts object was missing the javascript files registered by the ACF plugin. I don’t know exactly why it did this, but I’ve found a proper solution to this if you run into the same problem.

    The $GLOBALS['wp_scripts'] fortunately still contained the proper scripts. So i did the following in the add_action:

    add_action( 'wp_loaded', function() {
        $fscripts = new Filterable_Scripts();
    
        $missing_scripts = array_diff_key( $GLOBALS['wp_scripts']->registered, $fscripts->registered);
        foreach($missing_scripts as $mscript){
            $fscripts->registered[$mscript->handle] = $mscript;
        }
    
        $GLOBALS['wp_scripts'] = $fscripts;
    });
    

    Because the object contains an array of all registered scripts and the handles are also the array keys, I could use array_diff_key to determine which scripts were missing from the extended object and re-add them. I did this and not just

    $fscripts->registered = $GLOBALS['wp_scripts']->registered;

    because I didn’t want to overwrite any changes made by the extended object.

Comments are closed.