This is an implementation of ngettext function from standard GNU gettext. But also it's standard behavior can be extended by some extra configuration for handling different number of plural forms than 2. By default it works as expected with 2 plural forms. So you can use it even when you don't have actual translations, just for having support for plural forms in your code.

Live demo

this demo works without transpile step, consider using babel-plugin-c-3po for production usage


import { ngettext, msgid } from 'c-3po'

function test(n) {
    return ngettext(msgid`${n} time clicked`, `${n} times clicked`, n)

This example works out of the box without no extra configuration for c-3po plugin.

You may noticed that the first argument for ngettext is tagged with msgid tag. This may seem a little bit weird, but this is the only way to find translations by msgid without code transpilation with babel plugin. User friendly checks on extract and resolve steps will not let you forgive about that. More details about why we are using msgid tag are here.

ngettext format


ngettext(msgid`${n} time clicked`, `${n} times clicked`, n)
ngettext(msgid`${1} time clicked`, `${1} times clicked`, 1)
ngettext(msgid`${this.count} time clicked`, `${this.count} times clicked`, this.count)


ngettext(msgid`${n} time clicked`, `${n} times clicked`) // plural number argument is missing
ngettext(`${n} time clicked`, `${n} times clicked`, n) // msgid tag for the first argument is missing
// only identifiers and member expressions are allowed inside translated templates.
ngettext(msgid`${fn()} time clicked`, `${fn()} times clicked`, fn())

.po extraction

We will extract this from the example above:

#: src/ngettextDemo.js:19
msgid "${ n } time clicked"
msgid_plural "${ n } times clicked"
msgstr[0] ""
msgstr[1] ""

Resolve translations

Assume that translator added translations to our previous extracted entry:

#: src/ngettextDemo.js:19
msgid "${ n } time clicked"
msgid_plural "${ n } times clicked"
msgstr[0] "${ n } time clicked [translated]"
msgstr[1] "${ n } times clicked [translated]"

The resulting code (after transpilation will look like):

"use strict";
function _tag_ngettext(n, args) { 
    return args[+(n != 1)];

function test() {
    return _tag_ngettext(n, [ n + " time clicked [translated]", n + " times clicked [translated]" ]));

There is a new extra function in our file - _tag_ngettext, but you don't need to worry much about it, because this function is autogenerated by c-3po and may change in future without requiring any changes to your code.

Plural forms that are different from default (English)

There are cases when your source code uses string literals from some different language than English. And here is the point when usage of ngettext becomes not so clear. For instance, you decided to write string literals in Ukrainian in your sources, because at the time of the first release it was a single language for your project. At some point, you decided to localize your strings and want to use ngettext for plurals. Ukrainian language has 3 plural forms, but standard ngettext has only 2 arguments for them, so which one of those 3 plural forms must be passed to ngettext function? c-3po suggests quite a nice solution for this problem.

You can configure the number of plural forms arguments for ngettext by defaultHeaders property in config or by headers in Ukrainian the locale .po file. But still c-3po will use defaultHeaders on resolve if translations was not found. Also if you are resolving your translation at runtime you can use setDefaultHeaders function from c-3po library API.

By default c-3po uses this headers:

'content-type': 'text/plain; charset=UTF-8'
'plural-forms': 'nplurals=2; plural=(n!=1);'

So, if you want to use ngettext with Ukrainian locale, you should change defaultHeaders setting to:

'content-type': 'text/plain; charset=UTF-8'
'plural-forms': 'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);',

Here is a link where you can search for an appropriate headers for some other locales -

So, you need to make sure to use correct defaultHeaders. Those headers must correspond to your default locale.

results matching ""

    No results matching ""