WordPress: How to localize your JavaScript code?
Update (on 17th July 2017) – see the bottom of the page.
In WordPress, while writing theme or plugin, you should always keep in mind localization. Maybe your theme or plugin become so popular and someone would like to translate it into another language? It’s easy for me to think about localization, because I write my themes and plugins using english language and I usually add polish translation, because I’m Pole. So it isn’t very hard to achieve.
Localization in WordPress is very easy. Let’s see one example from my plugin, Google Analytics Head. Plugins requires some information in the file header, like plugin name, description, author etc. There is also a place for text domain, it looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php /* Plugin Name: Google Analytics Head Plugin URI: https://phylax.pl/plugins/google-analytics-head/ Description: This plugin adds tracking code for <strong>Google Analytics</strong> to your WordPress site. Unlike other plugins, code is added to the <i><head></i> section, so you can authorize your site in <strong>Google Webmaster Tools</strong>. Of course you can also move your tracking code into footer. Author: Lukasz Nowicki Author URI: http://lukasznowicki.info/ Version: 1.6.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: analytics-head Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZUE7KNWHW3CJ4 */ |
Of course it isn’t whole file, just a few lines from the top of the main file. I used analytics-head
as text domain as you may see. Then I need to load text domain in init
action. This is file inc/plugin.php
and appropriate fragment looks like this:
1 2 3 4 |
add_action( 'init', array( $this, 'init' ) ); function init() { load_plugin_textdomain( 'analytics-head', false, PLUGIN_BASE ); } |
Originally it is in the class, PLUGIN_BASE
is set in other place, but it isn’t important here. We know how to do that. Then we may use localized strings as simply as that:
1 |
echo '<a href="options-general.php?page=' . OPTION_SLUG . '">' . __( 'Settings', 'analytics-head' ) . '</a>' |
String “Settings” is ready to translate. It’s easy. But what should we do, when we want to use translatable strings in javascript? It isn’t much more harder, just a bit. Let’s write example plugin. This plugin will be very easy, just to show you the basics.
First of all, let’s write some frame code to see the plugin in Plugin menu. We may activate it as well. It won’t affect our WordPress since there are no code to do anything:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php /* Plugin Name: Localized JS Description: This is just an example on using localized strings in javascript files. Version: 0.1.0 Author: Łukasz Nowicki Author URI: http://lukasznowicki.info/ Text Domain: localizedjs */ namespace Phylax\WP\Plugin\LocalizedJS; if ( !is_admin() ) { return; } class Plugin { } $phylax_localizedjs = new Plugin(); |
Now, let’s add a page on the back-end (as you may see, this plugin will work only on back-end). We may use whatever we want, as this is just an example… Let’s add a page to “Media” menu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?php /* Plugin Name: Localized JS Description: This is just an example on using localized strings in javascript files. Version: 0.1.0 Author: Łukasz Nowicki Author URI: http://lukasznowicki.info/ Text Domain: localizedjs */ namespace Phylax\WP\Plugin\LocalizedJS; if ( !is_admin() ) { return; } class Plugin { function __construct() { add_action( 'admin_menu', [ $this, 'admin_menu' ] ); } function admin_menu() { add_media_page( __( 'LocalizedJS Example Page', 'localizedjs' ), __( 'LocalizedJS', 'localizedjs' ), 'edit_files', 'localizedjs', [ $this, 'admin_menu_view' ] ); } function admin_menu_view() { ?> <div class="wrap"> <h1><?php echo __( 'LocalizedJS', 'localizedjs' ); ?></h1> <div id="localizedjs"> <a href="#" id="localizedjs_click"><?php echo __( 'Draw the answer', 'localizedjs' ); ?></a> <div id="answer"></div> </div> </div> <?php } } $phylax_localizedjs = new Plugin(); |
By using this code, we will get new “LocalizedJS” sub-menu item in “Media” menu. Please note that I used “edit_files” capability for control user access. This capability is originally assigned to Administrator (and Superadministrator on Network sites) role. Please click this menu, you should get almost empty page with title and one link.
Now we need to add javascript file with our code, to handle link clicking. Of course we will control $hook
parameter to prevent loading our script on other pages, it’s only for us. We will also add JQuery dependency, because we are lazy and we like to use it 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<?php /* Plugin Name: Localized JS Description: This is just an example on using localized strings in javascript files. Version: 0.1.0 Author: Łukasz Nowicki Author URI: http://lukasznowicki.info/ Text Domain: localizedjs */ namespace Phylax\WP\Plugin\LocalizedJS; if ( !is_admin() ) { return; } class Plugin { function __construct() { add_action( 'admin_menu', [ $this, 'admin_menu' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); } function admin_menu() { add_media_page( __( 'LocalizedJS Example Page', 'localizedjs' ), __( 'LocalizedJS', 'localizedjs' ), 'edit_files', 'localizedjs', [ $this, 'admin_menu_view' ] ); } function admin_menu_view() { ?> <div class="wrap"> <h1><?php echo __( 'LocalizedJS', 'localizedjs' ); ?></h1> <div id="localizedjs"> <a href="#" id="localizedjs_click"><?php echo __( 'Draw the answer', 'localizedjs' ); ?></a> <div id="answer"></div> </div> </div> <?php } function admin_enqueue_scripts( $hook ) { if ( 'media_page_localizedjs' !== $hook ) { return; } wp_enqueue_script( 'localizedjs', plugins_url( 'localized.js', __FILE__ ), [ 'jquery' ] ); } } $phylax_localizedjs = new Plugin(); |
Our localized.js
file should be located in the same plugin folder, the same path as main file and now it looks like this:
1 2 3 4 5 |
jQuery( document ).ready( function( $ ) { alert('ready'); } ); |
Now, when we refresh our page, we should get standard message box with “ready” text. Now let’s handle our click. We need to edit localized.js
file this time:
1 2 3 4 5 6 7 8 9 |
jQuery( document ).ready( function( $ ) { $('#localizedjs_click').on( 'click', function( e ) { e.preventDefault(); $(this).blur(); alert( 'ready' ); } ); } ); |
Now we’ve got the same effect, but only when we click “Draw the answer” link. Please note, that we get rid of default link behavior (using preventDefault function) and we remove focus from our link (using blur function). So, what’s next? Well, we will use ajax to draw the answer. We will draw the number between 1 and 2. First, we need to handle our ajax call, let’s get back to localized-js.php
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?php /* Plugin Name: Localized JS Description: This is just an example on using localized strings in javascript files. Version: 0.1.0 Author: Łukasz Nowicki Author URI: http://lukasznowicki.info/ Text Domain: localizedjs */ namespace Phylax\WP\Plugin\LocalizedJS; if ( !is_admin() ) { return; } class Plugin { function __construct() { add_action( 'admin_menu', [ $this, 'admin_menu' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); add_action( 'wp_ajax_drawanswer', [ $this, 'drawanswer' ] ); } function admin_menu() { add_media_page( __( 'LocalizedJS Example Page', 'localizedjs' ), __( 'LocalizedJS', 'localizedjs' ), 'edit_files', 'localizedjs', [ $this, 'admin_menu_view' ] ); } function admin_menu_view() { ?> <div class="wrap"> <h1><?php echo __( 'LocalizedJS', 'localizedjs' ); ?></h1> <div id="localizedjs"> <a href="#" id="localizedjs_click"><?php echo __( 'Draw the answer', 'localizedjs' ); ?></a> <div id="answer"></div> </div> </div> <?php } function admin_enqueue_scripts( $hook ) { if ( 'media_page_localizedjs' !== $hook ) { return; } wp_enqueue_script( 'localizedjs', plugins_url( 'localized.js', __FILE__ ), [ 'jquery' ] ); } function drawanswer() { $answer = rand( 1, 2 ); echo json_encode( [ 'answer' => $answer, 'dt' => date( 'Y-m-d H:i:s' ), ] ); wp_die(); } } $phylax_localizedjs = new Plugin(); |
Now we may call our ajax, let’s go to ‘localized.js’ file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
jQuery( document ).ready( function( $ ) { $('#localizedjs_click').on( 'click', function( e ) { e.preventDefault(); $(this).blur(); ajaxdata = { 'action' : 'drawanswer', }; $.post( ajaxurl, ajaxdata, function( response ) { try { data = JSON.parse( response ); if ( data.answer == 1 ) { $('#answer').html( 'The answer is one. Generated on: ' + data.dt ); } else if ( data.answer == 2 ) { $('#answer').html( 'The answer is two. Generated on: ' + data.dt ); } else { $('#answer').html( 'Something went wrong' ); } } catch( e ) { alert( 'Something went wrong' ); } } ); } ); } ); |
So, this could be it! But unfortunately it isn’t. Why? Because our javascript code has strings and we can’t translate it… now. So, what to do? There is a special function: wp_localize_script
. And this is the main part of this article. At last! So, let’s get back to our php file once again. I will save you whole code, just admin_enqueue_scripts
method:
1 2 3 4 5 6 7 8 9 |
function admin_enqueue_scripts( $hook ) { if ( 'media_page_localizedjs' !== $hook ) { return; } wp_enqueue_script( 'localizedjs', plugins_url( 'localized.js', __FILE__ ), [ 'jquery' ] ); wp_localize_script( 'localizedjs', 'localizedjs_msg', [ 'error' => __( 'Something went wrong', 'localizedjs' ), 'answer_one' => __( 'The answer is one. Generated on: ', 'localizedjs' ), 'answer_two' => __( 'The answer is two. Generated on: ', 'localizedjs' ), ] ); } |
As you may see, now we can translate all messages in javascript file. Of course we need to make some changes on our javascript code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
jQuery( document ).ready( function( $ ) { $('#localizedjs_click').on( 'click', function( e ) { e.preventDefault(); $(this).blur(); ajaxdata = { 'action' : 'drawanswer', }; $.post( ajaxurl, ajaxdata, function( response ) { try { data = JSON.parse( response ); if ( data.answer == 1 ) { $('#answer').html( localizedjs_msg.answer_one + data.dt ); } else if ( data.answer == 2 ) { $('#answer').html( localizedjs_msg.answer_two + data.dt ); } else { $('#answer').html( localizedjs_msg.error ); } } catch( e ) { alert( localizedjs_msg.error ); } } ); } ); } ); |
And that’s it. Now we’ve got it all. Or… maybe not? Well, one last thing. Security. We should use two things to improve our security. First, we need to check user permission. Of course we will use edit_files
capability, because we use it in the admin menu creation. It’s simple:
1 2 3 4 5 6 7 8 9 |
function drawanswer() { if ( !current_user_can( 'edit_files' ) ) { wp_die(); } $answer = rand( 1, 2 ); echo json_encode( [ 'answer' => $answer, 'dt' => date( 'Y-m-d H:i:s' ), ] ); wp_die(); } |
But this may be not enough. We should also check WP nonce field… And to do that, we also have to use our localization technique! Finally, our files should looks like this:
1. localized-js.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?php /* Plugin Name: Localized JS Description: This is just an example on using localized strings in javascript files. Version: 0.1.0 Author: Łukasz Nowicki Author URI: http://lukasznowicki.info/ Text Domain: localizedjs */ namespace Phylax\WP\Plugin\LocalizedJS; if ( !is_admin() ) { return; } class Plugin { function __construct() { add_action( 'admin_menu', [ $this, 'admin_menu' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); add_action( 'wp_ajax_drawanswer', [ $this, 'drawanswer' ] ); } function admin_menu() { add_media_page( __( 'LocalizedJS Example Page', 'localizedjs' ), __( 'LocalizedJS', 'localizedjs' ), 'edit_files', 'localizedjs', [ $this, 'admin_menu_view' ] ); } function admin_menu_view() { ?> <div class="wrap"> <h1><?php echo __( 'LocalizedJS', 'localizedjs' ); ?></h1> <div id="localizedjs"> <a href="#" id="localizedjs_click"><?php echo __( 'Draw the answer', 'localizedjs' ); ?></a> <div id="answer"></div> </div> </div> <?php } function admin_enqueue_scripts( $hook ) { if ( 'media_page_localizedjs' !== $hook ) { return; } wp_enqueue_script( 'localizedjs', plugins_url( 'localized.js', __FILE__ ), [ 'jquery' ] ); wp_localize_script( 'localizedjs', 'localizedjs_msg', [ 'error' => __( 'Something went wrong', 'localizedjs' ), 'answer_one' => __( 'The answer is one. Generated on: ', 'localizedjs' ), 'answer_two' => __( 'The answer is two. Generated on: ', 'localizedjs' ), 'wpnonce' => wp_create_nonce( 'draw_the_answer' ), ] ); } function drawanswer() { if ( !current_user_can( 'edit_files' ) ) { wp_die(); } check_ajax_referer( 'draw_the_answer' ); $answer = rand( 1, 2 ); echo json_encode( [ 'answer' => $answer, 'dt' => date( 'Y-m-d H:i:s' ), ] ); wp_die(); } } $phylax_localizedjs = new Plugin(); |
2. localized.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
jQuery( document ).ready( function( $ ) { $('#localizedjs_click').on( 'click', function( e ) { e.preventDefault(); $(this).blur(); ajaxdata = { 'action' : 'drawanswer', '_wpnonce' : localizedjs_msg.wpnonce, }; $.post( ajaxurl, ajaxdata, function( response ) { try { data = JSON.parse( response ); if ( data.answer == 1 ) { $('#answer').html( localizedjs_msg.answer_one + data.dt ); } else if ( data.answer == 2 ) { $('#answer').html( localizedjs_msg.answer_two + data.dt ); } else { $('#answer').html( localizedjs_msg.error ); } } catch( e ) { alert( localizedjs_msg.error ); } } ); } ); } ); |
This should work.
Update, 17th July 2017
You may download this plugin on GitHub repository. There are a few more things – I added text domain loading and polish translation as an example, so you may switch your WordPress to polish (if you dare!) and see that it really works. If you want, feel free to add another translations by forking this repository. Have fun!