Grunt & WordPress development III: Tasks for internationalisation

grunt

This is part 3 in a series looking at using Grunt in WordPress plug-in/theme development.

  1. Grunt & WordPress development
  2. Grunt & WordPress development II: Installing Grunt
  3. Grunt & WordPress development III: Tasks for internationalisation
  4. Grunt & WordPress development IV: Another task for internationalisation

Internationalisation Tasks

One aspect of WordPress plug-in development that involves a lot of mundane work is that of internalisation: ensuring WordPress’ localisation functions are used correctly, generating a .pot file, compiling submitted .po files to .mo files. The latter two you can do with Poedit – but this still involves manually opening the .po/.pot file. These tasks can be completely automated so let’s do that:

po2mo – Compiling to .po files to .mo

The po2mo plug-in automatically compiles given .po files and produces a .mo file of the same name.

To install:

npm install grunt-po2mo --save-dev

<em>Please not an earlier version of this article executed the above as a super user (`sudo npm`). As pointed out by Lacy in the comments, this necessary and can cause permission issues with the npm cache.</em>

The following set up looks in the languages directory for any .po files and compiles them, creating the corresponding .mo in the same directory:

po2mo: {
    files: {
        src: 'languages/*.po',
        expand: true,
    },
},

Finally load the task by adding grunt.loadNpmTasks('grunt-po2mo'); at the bottom of your Gruntfile.js, just after grunt.loadTasks('tasks');. Then whenever you add or change a .po file:

grunt po2mo

(You can see a live example of this task, and the others listed below, here.

pot – Create a .pot template file

For users to be able to translate your plug-in you’ll need to create a .po template file ( a .pot file). The pot plug-in does exactly that.

You just need to provide it with:

  • The files to search in,
  • The keywords to search for (and indicate which arguments are translatable strings, and which are context specifiers)
  • A text domain (used only for naming the the .pot file)
  • The directory where you wish to output the .pot file.

To install:

npm install grunt-pot --save-dev

Then

pot: {
      options:{
          text_domain: 'my-plugin', //Your text domain. Produces my-text-domain.pot
          dest: 'languages/', //directory to place the pot file
          keywords: [ //WordPress localisation functions
            '__:1',
            '_e:1',
            '_x:1,2c',
            'esc_html__:1',
            'esc_html_e:1',
            'esc_html_x:1,2c',
            'esc_attr__:1', 
            'esc_attr_e:1', 
            'esc_attr_x:1,2c', 
            '_ex:1,2c',
            '_n:1,2', 
            '_nx:1,2,4c',
            '_n_noop:1,2',
            '_nx_noop:1,2,3c'
           ],
      },
      files:{
          src:  [ '**/*.php' ], //Parse all php files
          expand: true,
      }
},

Finally load the task by adding grunt.loadNpmTasks('grunt-pot'); to the bottom. Then to generate your .pot file:

grunt pot

checktextdomain – Verify localisation functions have been used correctly

Having generated a .pot file, gathered translations for your plug-in and then compiled them – it would be entirely wasted if you haven’t used the WordPress localisations functions properly. In particular, if you had failed to specify the correct domain, your efforts would have been wasted.

When coding it’s easy to forget to specify a text domain, or to mistype it. Or perhaps you’ve been using a variable for the domain, and now want to switch to a literal string.

The checktextdomain – not only checks if you’ve used the correct textdomain in the localisation function it can also correct it for you.

Simply provide it with:

  • Files to look in,
  • Keywords to look for (important: you must provide a domain argument specifier)
  • A text-domain to check against
  • Whether you want mistakes corrected (it will not add missing domains… yet).

The plug-in will then

  • Warn you if some keywords have been used without a text domain
  • Warn you if some keywords have been used with an incorrect text domain (optionally correct it for you)
  • Warn you if some keywords have been used with a variable text domain (optionally correct it for you)

There are various options for this plug-in to enable you to check (and correct) the things you want to. You can see all the available options for this Grunt plug-in on its Github page.

To install:

npm install grunt-checktextdomain --save-dev

You’ll notice that the keywords option is very similar to grunt-pot. There is an important distinction. For this plug-in to work you must extend the keyword specifier and indicate where the domain should be.

E.g. 2d indicates that the domain should be passed as the second argument of the localisation function

checktextdomain: {
   options:{
      text_domain: 'my-plugin',
      correct_domain: true, //Will correct missing/variable domains
      keywords: [ //WordPress localisation functions
            '__:1,2d',
            '_e:1,2d',
            '_x:1,2c,3d',
            'esc_html__:1,2d',
            'esc_html_e:1,2d',
            'esc_html_x:1,2c,3d',
            'esc_attr__:1,2d', 
            'esc_attr_e:1,2d', 
            'esc_attr_x:1,2c,3d', 
            '_ex:1,2c,3d',
            '_n:1,2,4d', 
            '_nx:1,2,4c,5d',
            '_n_noop:1,2,3d',
            '_nx_noop:1,2,3c,4d'
      ],
   },
   files: {
       src:  [ '**/*.php', ], //All php files
       expand: true,
   },
},

Finally load the task by adding grunt.loadNpmTasks('grunt-checktextdomain'); to the bottom. Then to check your files:

grunt checktextdomain

I’m planning on improving this further to warn you of missing contexts which using functions that expect one.

Final remarks

Remembering to add grunt.loadNpmTasks(...); at the bottom of your Gruntfile.js, just after grunt.loadTasks('tasks'); is easily forgotten. But there’s a way around this which I’ll discuss in my next post.

Just before publishing Brady Vercher announced his Grunt plug-in, which allows you to utilize the internationalisation tools that WordPress uses. There’s a bit more set-up involved, but a notable advantage over grunt-pot is that it recognises theme template headers as translatable.

Grunt & WordPress development II: Installing Grunt

grunt

This is part 2 in a series looking at using Grunt in WordPress plug-in/theme development.

  1. Grunt & WordPress development
  2. Grunt & WordPress development II: Installing Grunt
  3. Grunt & WordPress development III: Tasks for internationalisation
  4. Grunt & WordPress development IV: Another task for internationalisation

1. Installing Node

Grunt runs on top of Node.js (version 0.8.0 or later). To check if you have Node installed (and what version), run

node --version

in your command line. If you don’t have it installed you’ll get an error message like “command not found”. If you have it installed, and an appropriate version, then you’re dandy and can skip to the next section.

Otherwise you can download node for your particular operating system here: http://nodejs.org/download/. Windows & Macs have their own installer, but if you can also install from source.

Installing Node for Linux

For Debian-based Linux distros (Debian, Ubuntu, Mint etc), first install the dependencies (you’ll probably have these)

  sudo apt-get install g++ curl libssl-dev apache2-utils

Install Git (again, you’ll probably have this)

  sudo apt-get install git-core

Clone the Node repository and run

  git clone git://github.com/joyent/node.git
  cd node
  ./configure
  make
  sudo make install

Finally to double check all went well:

  man node;

2. Installing NPM

NPM is the package manager for Node, and is used by grunt to install and manage plug-ins. This should come with Node, so you don’t have to worry about installing this.

3. Installing Grunt

First we want to install the grunt command line interface, and we want it to be globally available:

 npm install -g grunt-cli

This will put the grunt command in your system path, allowing it to be run from any directory.

This hasn’t installed Grunt (the task runner). In fact, you install grunt locally in each project you work on, allowing you to have multiple versions of Grunt on the same machine at once.

Grunt will be installed, alongside all the other plug-ins you list. Before they can be installed we need to prepare the package.json file:

package.json

Your package.json file should live in the root of your project. It provides details of your project & lists any dependencies for development (including Grunt!). You can then install these using NPM.

Here’s a minimal example which would install grunt, jshint and uglify

{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.2",
    "grunt-contrib-jshint": "~0.6.3",
    "grunt-contrib-uglify": "~0.2.2"
  }
}

(Note that if any of those plug-ins require another plug-in to function, that will also be installed).

So to finally to install Grunt and the other listed plug-ins:

sudo npm install

All going well, Grunt is now installed.

Once you’ve got a package.json set-up you can easily install additional plug-ins with

 sudo npm install [module] --save-dev

which will also automatically add the specified module to your package.json as a “devDependencies”.

4. Using Grunt

Once Grunt is installed we can prepare our first task. Your grunt tasks are declared and configured by your Gruntfile.js. This includes:

  • The “wrapper” function
  • Project and task configurations
  • Loading your Grunt plug-ins
  • Declaring default task(s) and custom tasks

If you’re getting lost at this point, don’t worry, a simple example will help. Don’t worry about the details too much – I’ll be covering that later in the series.

 module.exports = function(grunt) { //The wrapper function

 // Project configuration & task configuration
 grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),

      //The uglify task and its configurations
      uglify: {
           options: {
                banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
           },
           build: {
             files: [{
                expand: true,     // Enable dynamic expansion.
                src: ['js/*.js', '!js/*.min.js'], // Actual pattern(s) to match.
                ext: '.min.js',   // Dest filepaths will have this extension.
             }]
           }
      },

      //The jshint task and its configurations
     jshint: {
          all: [ 'js/*.js', '!js/*.min.js' ]
     },

  });

  //Loading the plug-ins
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');


  // Default task(s), executed when you run 'grunt'
  grunt.registerTask('default', ['uglify']);


  //Creating a custom task
  grunt.registerTask('test', [ 'jshint' ] );

};

In that example our ‘test’ task triggers the jshint task, but it could trigger additional tasks such as unit testing tasks too (if we had that installed).

You’re done and ready to go:

grunt

will execute your default task, and

grunt test

will execute your ‘test’ task(s).

Real world example?

Feel free to checkout https://github.com/stephenharris/Event-Organiser-Posterboard/ for a real-world example of Grunt used in development.

What’s next?

In the next few posts I’ll be going through my particular set-up for WordPress plug-in (& theme) development: the plug-ins I use, their configurations and the tasks I have set-up.

Grunt & WordPress development

grunt

This is the first post of a five part series looking at WordPress plug-in/theme development with Grunt.

  1. Grunt & WordPress development
  2. Grunt & WordPress development II: Installing Grunt
  3. Grunt & WordPress development III: Tasks for internationalisation
  4. Grunt & WordPress development IV: Another task for internationalisation

Back in August the WordPress core team announced they were going to use Grunt in WordPress’ development. This in my view is a major stride forward for WordPress (more so than the much celebrated ‘features as plug-ins’ – which itself marked an improvement to WordPress’ development cycle).

This series of articles however, will be focussed on using Grunt for WordPress plug-ins and themes (which are fundamentally the same thing), and the tasks I use in development. In this first post I’ll discuss the what and the whys:

What is Grunt?

Grunt is a javascript based task runner from Ben Alman. It performs repetitive task such as compression, unit testing, linting, concatenation, preprocessing etc. Almost any task in the development, building or deployment of your WordPress plug-in which can be automated can be performed by Grunt – freeing you from those tedious, and potentially human-error-prone routines.

(Once Grunt is installed) there are two files which set up Grunt for use in your project:

  • package.json – which details your project (in this case: a WordPress plug-in) and it’s dependencies (in this context, Grunt and any Grunt plug-ins you want to use).
  • Gruntfile.js – listing the tasks you wish to perform and their configuration

Those tasks can be executed simply by running

 grunt [task name] 

in your command line. You’ll probably have multiple tasks that you’d want to run one after than other (e.g. one task to copy/generate files from your development environment to a build directory, and another to upload that directory to an Amazon S3 server). Instead of calling each manually Grunt allows you to create tasks which simply call a collection of other tasks. For example

 grunt test

might be configured to trigger unit-test and linting tasks.

Why Grunt?

The idea of automating deployments, unit testing, compressing images, scripts & stylesheets and other tasks you may wish to perform in your plug-in’s development, build and release cycle is certainly not unique to Grunt. Before I switched to Grunt I had a home-grown Makefile to perform a lot of my routine tasks.

Grunt however, brings this all under one roof: giving a familiar command line procedure to execute task(s). Importantly it allows (Grunt) plug-ins, and their end-users to add structure to their tasks. By this I mean tasks being able to call other tasks, being able to initiate an entire list of tasks, being able to configure all your tasks in only one file and easy of portability. In fact, if you download a development repository for a plug-in which includes the package.json and Gruntfile.js files, in one command you can install all the Grunt plug-ins it requires for use in testing, building and deploying that plug-in. (This assumes you have Node.js installed, which I’ll cover in part two).

Grunt doesn’t offer much new – but it does offer a much better solution.

It’s also popular – and popularity is key for any library, platform or tool to succeed and to grow. Popularity brings greater number of developers, they drive the growth of plug-ins & features and the increased functionality drives popularity. (Perhaps not dissimilar to WordPress’ own growth). Grunt’s ecosystem is already very substantial: there’s phpunit for PHP unit testing, jshint for Javascript linting, there is uglifyjs for compressing javascript files and imagemin to optimise images.

The point is: it has a large, and growing ecosystem. In the majority cases, for any task you might want to perform, there will exist a grunt plug-in to perform it.

And if there isn’t? Grunt is incredibly well documented, open-source and easy to dive into. If you find a gap in its armoury, the chances are it’s easy enough to fill.

What’s next?

If you weren’t already sold on Grunt, hopefully that will do it. The next post will be on installing Grunt and executing your first task.