For one of my projects, I needed a tool to list all used translations keys from a PHP project. Because I want to keep the translation database clean, I decided to create a Grunt plugin. There was one big problem: How do you create this plugin? So I started to read the documentation, some blog posts, etc. and noticed that the documentation was pretty confusing. Let me take you on a tour through my first plugin.
Where to start?
Create the plugin skeleton
The Grunt team has created a tool called grunt-init. It’s purpose is to quickly scaffold your project with some basic templates. The tool does not include any templates, so you have to download them manually. There is a tempalte for creating a grunt-plugin and another one to add a Gruntfile to your project. When you run the tool, a wizard is launched to personalize the template.
After running this command, all the template files are being copied to your folder. The structure is pretty straight forward. There is a tasks
folder which contains the Grunt tasks. Another folder is the test
folder. This is the place where you write some node tests for your plugin. Finally, there is also a Gruntfile which will help you with some basic tasks like testing the plugin.
Configure NPM
Once you got through the installation wizard, you should personalize the NPM file. My plugin is project specific, so I decided to make it a private repository. I also like using Lo-Dash for handling arrays, so I added this one as a dependency of the plugin. All these parameters can be configured in the package.json
file.
When the configuration is saved, the next step is to install the dependencies. This can be done by running the command:
Writing the task
Task structure
You can add one or more tasks to your plugin. By default there is one multi-task in the tasks folder. It looks like this:
Initialize dependencies
Grunt has some standard tools to handle file actions and logging. When you want to use custom libraries, you can include them through require.
Options
If you ever worked with Grunt before, you know that all commands are configurable. Some of the options will have a default value. In the plugin, you can add custom options as followed:
The options in the object are the default options and can be overwritten in the Gruntfile.
Files
Next to the options, there is a basic configuration parameter to specify source and destinations files. The configuration looks like:
As you can see, it is possible to add multiple src / dest collections. The source can consist out of multiple files and the names can contain regular expressions like wildcards. In the plugin, it is very easy to loop through the files:
Locating translation keys
To find the translation keys in a file, I needed to write some regular expressions to find the key. Luckily for me, I use a very basic translation method. In my project I have 2 different ways of retrieving translations: one for the controller and one for the view.
This may look a bit hard at first, but it is actually very easy. There are only 2 patterns:
- In the controller:
$translator->get('KEY_123');
- In the view:
_t->get('KEY_123');
After the regular expressions were written, I created a function that returns the translation keys:
Okay, the hard part of the plugin is written. Now the only thing left is adding this functionality to the part where we read the file. I want to execute all regular expressions patterns on every file. The resulting array should only contain unique entries. This can easily be done with the Lo-Dash forEach and union method:
Formatting the output
As you can see in the files part, at the moment we only output the result as newline-separated text. Because I want to clean up my database through PHP, I decided to make JSON the default format. To specify which format will be used I added one other function:
The translations parameter is the array of found translation keys and the format is configured in the options.format
variable.
Logging
At the end of the command, I find it useful to log the results of the task. In this case, I only print an indication of the amount of found items.
Testing
For now, I only talked about writing the plugin. Of course it is also recommended to write some test for your plugin. As far as I can see in other plugins, there are only functional tests. In the test directory there are 2 folders. The fixtures
folder is where you add the files where you want to find translation keys in. In the expected
folder you place the output file that you expect the plugin to generate.
Now the first thing that you need to do, is to configure your task in the Gruntfile. When you run the test command, your configured tasks will run first. So in the tasks you can configure to run the tasks with the fixtures file and save it in a tmp
folder. When you know which files are being read and written, it is easy to create your test:
Now the only command you need to run to test your code is:
Deployment
Once your plugin is ready, you want to place it in GIT. Because this is a private repository, I placed it on a private repository on BitBucket. Now the next problem arose: How can I load my custom plugin through NPM in another project? In the latest versions of NPM, it is possible to add packages that are not managed on NPM. The real problem is that it is a private repository. After some searching, I found out that the only good way the require the package is to use the SSH url of the package. This is what I placed in the package.json
file of the main project.
Note: Don’t forget to npm install
In the Gruntfile of the project, I configured the task to search for translations in all .php and .phtml files. The result of the tasks will be saved in the logs folder.
Finally my task was ready to use in my project: