0.1.1 • Published 11 years ago

grunt-gettext v0.1.1

Weekly downloads
20
License
-
Repository
github
Last release
11 years ago

grunt-gettext

Gettext for JavaScript and Grunt.js

Introduction

Gettext is the name of a set of utilities developed by GNU to add multi-language support to applications.

The first step of the translation process is to extract translatable messages from your sources. To do so, you should mark all strings which you want to translate by wrapping them in a translator function (often called tr() or i18n()). The xgettext task provided by this plugin then scans all your source files and generates a POT file containing all the translatable strings. In addition to scanning plain JavaScript files, this plugin also supports scanning Handlebars templates.

The second step is converting the POT file into a PO file which is the file that will be actually translated by translators. During this step you can merge the POT file with a previously translated PO file resulting in a new PO file that has all known translations already filled in. This step can be performed using the msgmerge command-line utility. It is not provided by this plugin, but an example of calling the utility through the Grunt Shell plugin is given below as part of the real-world example.

The third step is taking the translated PO files and getting the translations to show up in your project. In order to facilitate this, this plugin provides the po2json task which converts the translations to a JSON map, optionally wrapped in a Require.js definition. To finish this step, you will have to make sure the JSON translations are loaded into your application and actually used by the tr() or i18n() function you use.

Happy translating!


To find more information about the original gettext utilities, visit the GNU project: http://www.gnu.org/software/gettext/

GNU also maintains a list of additional tools to work with PO files, including translation programs useful for translators: http://www.gnu.org/software/trans-coord/manual/web-trans/html_node/PO-Files.html

Getting Started

This plugin requires Grunt ~0.4.1

If you haven't used Grunt before, be sure to check out the Getting Started guide, as it explains how to create a Gruntfile as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:

npm install grunt-gettext --save-dev

Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:

grunt.loadNpmTasks('grunt-gettext');

The "xgettext" task

Overview

In your project's Gruntfile, add a section named xgettext to the data object passed into grunt.initConfig().

grunt.initConfig({
  xgettext: {
    options: {
      functionName: "tr",
      potFile: "messages.pot",
      //processMessage: function(message) { ... }
    },
    target: {
      files: {
        handlebars: [],
        javascript: []
      }
    }
  },
})

Options

options.functionName

Type: String Default value: "tr"

The function name that marks translatable messages.

options.potFile

Type: String Default value: "messages.pot"

The .pot file that is generated by this task (the output file). It will contain all extracted messages, regardless of the source in which they were found (be it Handlebars or JavaScript files).

Warning: This file is overwritten every time you run this task.

options.processMessage

Type: Function Default value: _.identity

Custom function that will process every extracted message before it's included in the POT file. This may come in handy, for example, if you want to simplify whitespace.

The function takes the message string as its sole argument and should return the processed message.

Files

files.handlebars

Handlebars files to scan for translatable messages.

Assuming the default functionName is used, translatable messages look like this:

{{tr "Some translatable message"}}

{{tr "You have %1 followers" numFollowers}}

In either case, the first string argument is extracted as a translatable message.

files.javascript

JavaScript files to scan for translatable texts.

Assuming the default functionName is used, translatable messages look like this:

tr("Some translatable messages")

tr("You have %1 follower", "You have %1 followers").arg(numFollowers)

In both cases, all string arguments inside the tr() function call are extracted as translatable messages.

Be aware that concatenating translatable strings with variables is inherently not possible. For example, this will NOT work:

tr("Some value: " + value)

The reason this fails is because the input to the tr() function will be different every time it is called, and therefore does not have a stable key for looking up the proper translation.

Note that concatenating multiple strings to create a single (multi-line) translatable string does work, provided all parts use the same type of quotes. Example:

tr("This is the first line " +
   "of a multiline translatable message")

The "po2json" task

Overview

In your project's Gruntfile, add a section named po2json to the data object passed into grunt.initConfig().

grunt.initConfig({
  xgettext: {
    options: {
      requireJs: false
    },
    your_target: {
      // target-specific file lists and/or options go here.
    },
  },
})

Options

options.requireJs

Type: Boolean Default value: false

If set to true, the JSON resource is wrapped in an anonymouse Require.js definition.

Usage Examples

Real-world example

var _ = grunt.util._;

var locales = ["de-DE", "en-GB", "en-US", "fr-FR", "it-IT", "ja-JP", "nl-NL"];

grunt.initConfig({
  xgettext: {
    target: {
      files: {
        handlebars: ["tmpl/**.handlebars"],
        javascript: ["js/**.js"]
      },
      options: {
        functionName: "i18n",
        potFile: "translations/messages.pot",
        processMessage: function(message) {
          return message.replace(/\s+/g, " "); // simplify whitespace
        }
      }
    }
  },

  po2json: {
    target: {
      files: _.object(
          _.map(locales, function(locale) { return "translations/" + locale + ".js"; }),
          _.map(locales, function(locale) { return "translations/" + locale + ".po"; })
        )
      },
      options: {
        requireJs: true
      }
    }
  },
  
  shell: {
    options: {
      failOnError: true
    },
    msgmerge: {
      command: _.map(locales, function(locale) {
        var po = "translations/" + locale + ".po";
        return "if [ -f \"" + po + "\" ]; then\n" +
               "    echo \"Updating " + po + "\"\n" +
               "    msgmerge " + po + " translations/messages.pot > .new.po.tmp\n" +
               "    exitCode=$?\n" +
               "    if [ $exitCode -ne 0 ]; then\n" +
               "        echo \"Msgmerge failed with exit code $?\"\n" +
               "        exit $exitCode\n" +
               "    fi\n" +
               "    mv .new.po.tmp " + po + "\n" +
               "fi\n";
      }).join("")
    }
  }
}

Contributing

In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using Grunt.

Release History

(Nothing yet)