npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

xgettext-handlebars

v0.0.6

Published

Extract translatable strings from Handlebars templates

Downloads

4

Readme

xgettext-handlebars

Extract translatable strings from Handlebars templates.

Warning

This code is unstable and going through a major rewrite.

Usage

var xgettext = require('xgettext-handlebars');

xgettext('{{gettext "Hello World!"}}', {
  // optional options
});

Features

Basic extraction

{{gettext "Hello World!"}}

Plurals

{{ngettext "boat" "boats" numBoats}}

gettext documentation on plural forms

Contexts

Necessary to disambiguate things like homonyms:

{{pgettext "noun" "file"}}

gettext documentation on contexts

Domains

Useful for splitting your translation catalogs up:

{{dgettext "plugin_catalog" "Hello World!"}}

gettext documentation on domains

Extracted comments

Useful for providing instructions to your localizers. Comments are extracted from Handlebars subexpressions:

{{gettext "Hi %s" (gettext-comment "%s is a variable")}}

gettext documentation on extracted comments

Options

filename

Filename the Handlebars string came from. Used in references.

// no filename, so references are non-existent or not very helpful
// (if they only have column number)
xgettext('{{gettext "Hi"}}');

// this would generate a reference in your PO entry that's actually useful
xgettext('{{gettext "Hi"}}', {filename: foo.js});

identifiers

Set of identifiers to extract. Defaults to every function in the standard gettext API, plus a common shorthand version of each function:

{
  gettext: ['msgid'],
  _: ['msgid'],

  dgettext: ['domain', 'msgid'],
  d_: ['domain', 'msgid'],

  dcgettext: ['domain', 'msgid', 'category'],
  dc_: ['domain', 'msgid', 'category'],

  ngettext: ['msgid', 'msgid_plural', 'n'],
  n_: ['msgid', 'msgid_plural', 'n'],

  dngettext: ['domain', 'msgid', 'msgid_plural', 'n'],
  dn_: ['domain', 'msgid', 'msgid_plural', 'n'],

  dcngettext: ['domain', 'msgid', 'msgid_plural', 'n', 'category'],
  dcn_: ['domain', 'msgid', 'msgid_plural', 'n', 'category'],

  pgettext: ['msgctxt', 'msgid'],
  p_: ['msgctxt', 'msgid'],

  dpgettext: ['domain', 'msgctxt', 'msgid'],
  dp_: ['domain', 'msgctxt', 'msgid'],

  npgettext: ['msgctxt', 'msgid', 'msgid_plural', 'n'],
  np_: ['msgctxt', 'msgid', 'msgid_plural', 'n'],

  dnpgettext: ['domain', 'msgctxt', 'msgid', 'msgid_plural', 'n'],
  dnp_: ['domain', 'msgctxt', 'msgid', 'msgid_plural', 'n'],

  dcnpgettext: ['domain', 'msgctxt', 'msgid', 'msgid_plural', 'n', 'category'],
  dcnp_: ['domain', 'msgctxt', 'msgid', 'msgid_plural', 'n', 'category']
}

Each key in identifiers indicates a Handlebars helper to extract, while the value indicates the order of expected PO fields for that helper. For example, npgettext: ['msgctxt', 'msgid', 'msgid_plural'] indicates that the npgettext handlebars helper takes arguments of the form {{npgettext "context" "string" "plural"}}.

The default identifiers assumes you're using helpers that match the gettext API, but you can customize it to fit your needs. See the Examples section for more info.

While identifiers allows for a large amount of freedom, there are some limits. Since xgettext-handlebars can't do much without knowing a string's message ID, each extracted identifier must declare a msgid. For example, the following will throw an Error:

xgettext('{{gettext "Hi"}}', {
  identifiers: {
    // error, no msgid specified
    i18n: ['string']
  }
});

Similarly, if you want strings with the same msgid to be distinguished by their contexts, use msgctxt. Otherwise, contexts won't work. Likewise, domain parameters should be called domain.

Finally, you should name your plural message ID msgid_plural. Otherwise, xgettext-handlebars can't warn you about mismatched plural definitions like the following:

{{ngettext "goose" "goose"}}
{{ngettext "goose" "geese"}}
{{! error, should we use "goose" or "geese"? }}

defaultDomain

Optional string indicating the default domain. Defaults to messages.

commentIdentifiers

Optional array of strings indicating names of subexpressions to extract comments from. Defaults to ['gettext-comment'].

Return value

The xgettext function returns an object representing the set of extracted messages.

{
  "messages": { // messages are grouped under domain, which defaults to "messages"
    msgid1: {
      msgid: "msgid1", // useful when you have context
      extractedComments: [
        // Extracted comments are different from translator comments.
        // Extracted comments come from the developers who put them in the
        // code. Translator comments are added to PO files by translators.
        "TRANSLATORS: please listen to me"
      ],
      references: [ // list of all the places this msgid is used
        {
          // whatever you passed in as the `filename` option
          filename: undefined

          // see https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API
          start: {
            line: 1,
            column: 0
          },
          end: {
            line: 2,
            column: 15
          }
        }
        // there can be more references if this msigd appears multiple times
      ]
    },

    // plural messages have a msgid_plural field
    boat: {
      msgid: "boat",
      msgid_plural: "boats",
      references: [/* ...you get the idea...*/],
      extractedComments: []
    },

    // Messages with context are stored as keys prefixed by their context
    // and a separator. This is the same technique Jed uses.
    "context\u0004msgid1": {
      msgid: "msgid1",
      msgctxt: "context",
      references: [/* ... */]
    }
  },
  "another domain": {
    // if you define multiple domains in your strings, which you can do by
    // using the dgettext family of functions, you'll get multiple domains in your ouput
  }
}

Examples

Changing field order

Changing the field order is useful if your helpers' signatures don't match the gettext API. For example, if your Handlebars helper ngettext expects the plural form to be passed first, initialize xgettext-handlebars like so:

xgettext('{{gettext "boats" "boat"}}', {
  identifiers: {
    ngettext: ['msgid_plural', 'msgid']
    // declare other identifiers as needed
  }
});

Custom identifiers

You can define totally new identifiers to extract, for example:

xgettext('{{i18n "Hi"}}', {
  identifiers: {
    i18n: ['msgid']
  }
});

Not that identifiers overrides the built-in default identifiers. If you want to add a new identifier to the existing set use xgettext.DEFAULT_IDENTIFIERS:

xgettext('{{i18n "Hi"}}', {
  identifiers: Object.assign({i18n: ['msgid']}, xgettext.DEFAULT_IDENTIFIERS)
});

Custom fields

You don't have to limit yourself to extracting gettext fields. For example, let's say you mark the gender of your strings (a field gettext doesn't otherwise support):

xgettext('{{i18n-gender "You" "female"}}', {
  identifiers: {
    'i18n-gender': ['msgid', 'gender']
  }
});

The return value will include the gender field in its output for this message:

{
  msgid: 'You',
  gender: 'female'
  // other fields not shown
}

Ignoring arguments

xgettext-handlebars ignores any extra arguments it encounters. Take this example, using the default config for gettext (['msgid']):

{{! ignored will be ignored, only "message" will be extracted}}
{{gettext "message" ignored}}

Skipping arguments

Ignoring non-trailing arguments is also possible. Let's say you want to extract something of the form

{{i18n variable "Hi %s"}}

The first parameter to i18n is a variable and shouldn't be extracted, while the second field, the msgid, is what we actually care about. We can use xgettext-handlebar's support for custom fields to ignore fields we don't want:

xgettext('{{i18n variable "Hi %s"}}', {
  identifiers: {
    i18n: ['parameter', 'msgid']
  }
});

Note that the parameter will still technically be extracted, it's just in a field nothing uses. Here's the extracted message for the above example:

{
  msgid: 'Hi %s',
  parameter: 'variable'
}

License

MIT