Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

node-polyglot

airbnb813.8kBSD-2-Clause2.6.0TypeScript support: definitely-typed

Give your JavaScript the ability to speak many languages.

i18n, internationalization, internationalisation, translation, interpolation, translate, polyglot

readme

Polyglot.js

Build Status

Join the chat at https://gitter.im/airbnb/polyglot.js

Polyglot.js is a tiny I18n helper library written in JavaScript, made to work both in the browser and in CommonJS environments (Node). It provides a simple solution for interpolation and pluralization, based off of Airbnb’s experience adding I18n functionality to its Backbone.js and Node apps.

I18n is incredibly important for us at Airbnb, as we have listings in 192 countries, and we translate our site into 30-odd different languages. We’re also hiring talented engineers to help us scale up to meet the challenges of building a global marketplace.

View the documentation on Github.

View the annotated source.

Polyglot is agnostic to your translation backend. It doesn’t perform any translation; it simply gives you a way to manage translated phrases from your client- or server-side JavaScript application.

Installation

install with npm:

$ npm install node-polyglot

Running the tests

Clone the repo, run npm install, and npm test.

Usage

Instantiation

First, create an instance of the Polyglot class, which you will use for translation.

var polyglot = new Polyglot();

Polyglot is class-based so you can maintain different sets of phrases at the same time, possibly in different locales. This is very useful for example when serving requests with Express, because each request may have a different locale, and you don’t want concurrent requests to clobber each other’s phrases.

See Options Overview for information about the options object you can choose to pass to new Polyglot.

Translation

Tell Polyglot what to say by simply giving it a phrases object, where the key is the canonical name of the phrase and the value is the already-translated string.

polyglot.extend({
  "hello": "Hello"
});

polyglot.t("hello");
=> "Hello"

You can also pass a mapping at instantiation, using the key phrases:

var polyglot = new Polyglot({phrases: {"hello": "Hello"}});

Polyglot doesn’t do the translation for you. It’s up to you to give it the proper phrases for the user’s locale.

A common pattern is to gather a hash of phrases in your backend, and output them in a <script> tag at the bottom of the document. For example, in Rails:

app/controllers/home_controller.rb

def index
  @phrases = {
    "home.login" => I18n.t("home.login"),
    "home.signup" => I18n.t("home.signup"),
    ...
  }
end

app/views/home/index.html.erb

<script>
  var polyglot = new Polyglot({phrases: <%= raw @phrases.to_json %>});
</script>

And now you can utilize i.e. polyglot.t("home.login") in your JavaScript application or Handlebars templates.

Interpolation

Polyglot.t() also provides interpolation. Pass an object with key-value pairs of interpolation arguments as the second parameter.

polyglot.extend({
  "hello_name": "Hola, %{name}."
});

polyglot.t("hello_name", {name: "DeNiro"});
=> "Hola, DeNiro."

Polyglot also supports nested phrase objects.

polyglot.extend({
  "nav": {
    "hello": "Hello",
    "hello_name": "Hello, %{name}",
    "sidebar": {
      "welcome": "Welcome"
    }
  }
});

polyglot.t("nav.sidebar.welcome");
=> "Welcome"

The substitution variable syntax is customizable.

var polyglot = new Polyglot({
  phrases: {
    "hello_name": "Hola {{name}}"
  },
  interpolation: {prefix: '{{', suffix: '}}'}
});

polyglot.t("hello_name", {name: "DeNiro"});
=> "Hola, DeNiro."

Pluralization

For pluralization to work properly, you need to tell Polyglot what the current locale is. You can use polyglot.locale("fr") to set the locale to, for example, French. This method is also a getter:

polyglot.locale()
=> "fr"

You can also pass this in during instantiation.

var polyglot = new Polyglot({locale: "fr"});

Currently, the only thing that Polyglot uses this locale setting for is pluralization.

Polyglot provides a very basic pattern for providing pluralization based on a single string that contains all plural forms for a given phrase. Because various languages have different nominal forms for zero, one, and multiple, and because the noun can be before or after the count, we have to be overly explicit about the possible phrases.

To get a pluralized phrase, still use polyglot.t() but use a specially-formatted phrase string that separates the plural forms by the delimiter ||||, or four vertical pipe characters.

For pluralizing "car" in English, Polyglot assumes you have a phrase of the form:

polyglot.extend({
  "num_cars": "%{smart_count} car |||| %{smart_count} cars",
});

Please keep in mind that smart_count is required. No other option name is taken into account to transform pluralization strings.

In English (and German, Spanish, Italian, and a few others) there are only two plural forms: singular and not-singular.

Some languages get a bit more complicated. In Czech, there are three separate forms: 1, 2 through 4, and 5 and up. Russian is even more involved.

var polyglot = new Polyglot({locale: "cs"}); // Czech
polyglot.extend({
  "num_foxes": "Mám %{smart_count} lišku |||| Mám %{smart_count} lišky |||| Mám %{smart_count} lišek"
})

polyglot.t() will choose the appropriate phrase based on the provided smart_count option, whose value is a number.

polyglot.t("num_cars", {smart_count: 0});
=> "0 cars"

polyglot.t("num_cars", {smart_count: 1});
=> "1 car"

polyglot.t("num_cars", {smart_count: 2});
=> "2 cars"

As a shortcut, you can also pass a number to the second parameter:

polyglot.t("num_cars", 2);
=> "2 cars"

Custom Pluralization Rules

Polyglot provides some default pluralization rules for some locales. You can specify a different set of rules through the pluralRules constructor param.

var polyglot = new Polyglot({
  pluralRules: {
    pluralTypes: {
      germanLike: function (n) {
        // is 1
        if (n === 1) {
          return 0;
        }
        // everything else
        return 1;
      },
      frenchLike: function (n) {
        // is 0 or 1
        if (n <= 1) {
          return 0;
        }
        // everything else
        return 1;
      }
    },
    pluralTypeToLanguages: {
      germanLike: ['de', 'en', 'xh', 'zu'],
      frenchLike: ['fr', 'hy']
    }
  }
});

This can be useful to support locales that polyglot does not support by default or to change the rule definitions.

Public Instance Methods

Polyglot.prototype.t(key, interpolationOptions)

The most-used method. Provide a key, and t() will return the phrase.

polyglot.t("hello");
=> "Hello"

The phrase value is provided first by a call to polyglot.extend() or polyglot.replace().

Pass in an object as the second argument to perform interpolation.

polyglot.t("hello_name", {name: "Spike"});
=> "Hello, Spike"

Pass a number as the second argument as a shortcut to smart_count:

// same as: polyglot.t("car", {smart_count: 2});
polyglot.t("car", 2);
=> "2 cars"

If you like, you can provide a default value in case the phrase is missing. Use the special option key "_" to specify a default.

polyglot.t("i_like_to_write_in_language", {
  _: "I like to write in %{language}.",
  language: "JavaScript"
});
=> "I like to write in JavaScript."

Polyglot.prototype.extend(phrases)

Use extend to tell Polyglot how to translate a given key.

polyglot.extend({
  "hello": "Hello",
  "hello_name": "Hello, %{name}"
});

The key can be any string. Feel free to call extend multiple times; it will override any phrases with the same key, but leave existing phrases untouched.

Polyglot.prototype.unset(keyOrObject)

Use unset to selectively remove keys from a polyglot instance. unset accepts one argument: either a single string key, or an object whose keys are string keys, and whose values are ignored unless they are nested objects (in the same format).

Example:

polyglot.unset('some_key');
polyglot.unset({
  hello: 'Hello',
  hello_name: 'Hello, %{name}',
  foo: {
    bar: 'This phrase’s key is "foo.bar"'
  }
});

Polyglot.prototype.locale([localeToSet])

Get or set the locale (also can be set using the constructor option, which is used only for pluralization. If a truthy value is provided, it will set the locale. Afterwards, it will return it.

Polyglot.prototype.clear()

Clears all phrases. Useful for special cases, such as freeing up memory if you have lots of phrases but no longer need to perform any translation. Also used internally by replace.

Polyglot.prototype.replace(phrases)

Completely replace the existing phrases with a new set of phrases. Normally, just use extend to add more phrases, but under certain circumstances, you may want to make sure no old phrases are lying around.

Polyglot.prototype.has(key)

Returns true if the key does exist in the provided phrases, otherwise it will return false.

Public Static Methods

transformPhrase(phrase[, substitutions[, locale]])

Takes a phrase string and transforms it by choosing the correct plural form and interpolating it. This method is used internally by t. The correct plural form is selected if substitutions.smart_count is set. You can pass in a number instead of an Object as substitutions as a shortcut for smart_count. You should pass in a third argument, the locale, to specify the correct plural type. It defaults to 'en' which has 2 plural forms.

Options Overview

new Polyglot accepts a number of options:

  • phrases: a key/value map of translated phrases. See Translation.
  • locale: a string describing the locale (language and region) of the translation, to apply pluralization rules. see Pluralization
  • allowMissing: a boolean to control whether missing keys in a t call are allowed. If false, by default, a missing key is returned and a warning is issued.
  • onMissingKey: if allowMissing is true, and this option is a function, then it will be called instead of the default functionality. Arguments passed to it are key, options, and locale. The return of this function will be used as a translation fallback when polyglot.t('missing.key') is called (hint: return the key).
  • interpolation: an object to change the substitution syntax for interpolation by setting the prefix and suffix fields.
  • pluralRules: an object of pluralTypes and pluralTypeToLanguages to control pluralization logic.

History

  • i18n-extract: Manage localization with static analysis. (E.g. key usage extraction)

changelog

v2.6.0: July 11, 2024

  • [New] add Ukrainian (#184)
  • [Refactor] remove array.prototype.foreach
  • [Refactor] remove string.prototype.trim
  • [Refactor] use hasown instead of has
  • [meta] add missing engines.node
  • [Deps] update array.prototype.foreach, object.entries, string.prototype.trim
  • [Deps] update aud, chai, docco, eslint-plugin-import, string.prototype.matchall
  • [eslint] disable yoda rule entirely
  • [readme] Fixed typos (buliding, Polylglot) (#181)

v2.5.0: January 23, 2023

  • [New] Add replace option for custom replace implementation (#171)
  • [New] Add Romanian and Macedonian (#176)
  • [Deps] update array.prototype.foreach, object.entries, string.prototype.trim (#172)
  • [Tests] Migrate tests to GitHub Actions (#169)
  • [Tests] Add passing tests (#168)

v2.4.2: August 16, 2021

  • [Fix] Handle null and undefined gracefully in extend and unset (#161)

v2.4.1: August 16, 2021

  • [Fix] French starts plural at 2 (#154)
  • [Refactor] Replace for-each with object.entries and array.prototype.foreach (#127)
  • [Performance] Add plural type name memoization (#158)
  • [Deps] Update string.prototype.trim (#127)
  • [Dev Deps] update chai, safe-publish-latest, eslint, eslint-plugin-import (#127)

v2.4.0: September 10, 2019

  • [New] add ability to configure pluralization rules (#138)

v2.3.1: June 20, 2019

  • [Fix] fix plurals for Russian with n > 100 (#119)
  • [Performance] Remove unnecessary dollar signs replacement (#132)
  • [Docs] fix typo in the Czech example (#123)
  • [Deps] update warning
  • [Dev Deps] update chai, eslint, eslint-config-airbnb-base, eslint-plugin-import, safe-publish-latest, uglify-js
  • [Tests] on node v12, v11, v10

v2.3.0: July 2, 2018

  • [New] add ability to change interpolation regex by specifying prefix and suffix (#106, #64)
  • [New] support for Serbian (Latin & Cyrillic), Bosnian (Latin & Cyrillic), Czech (#113)
  • [Fix] Moved lt to it's own group (#101)
  • [Fix] Moved tr from chinese to german group (#100)
  • [Fix] Move persian(fa) language to german group of pluralization (#86)
  • [Fix] Remove long-since-updated build files
  • [Fix] fix russian pluralization; add tests (#115)
  • [Fix] croatian is not russian (#114)
  • [Clarity] add more specific locales, even though language codes will match them (#115)
  • [Docs] document constructor options (#84)
  • [Docs] document all instance and static methods (#83)
  • [Docs] fix spelling of "delimiter" (#91)
  • [Docs] onMissingKey can (rather, should) return a value (#95)
  • [Docs] fix instructions to only recommend use with npm (#96)
  • [Docs] Added documentation for method has (#104)
  • [Docs] add example for languages with multiple plurals (#108)
  • [Docs] remove outdated sentence (#112, #110)
  • [Deps] update for-each, has, warning
  • [Dev Deps] update chai, eslint, eslint-config-airbnb-base, eslint-plugin-import, mocha; remove should
  • [Tests] up to node v10; use nvm install-latest-npm to ensure new npm doesn’t break old node; improve matrix

v2.2.2: January 5, 2017

  • [Fix] revert unintentional breaking change of missing substitutions being untouched
  • [Dev Deps] update eslint, eslint-config-airbnb-base, mocha, should; add safe-publish-latest

v2.2.1: November 18, 2016

  • [Fix] restore behavior of explicit null/undefined not touching the substitution

v2.2.0: November 14, 2016

  • [New] add onMissingKey constructor option - this can call .transformPhrase, or return false, or undefined, or throw - whatever you like (#34, #77)
  • [Dev Deps] update eslint

v2.1.3: January 5, 2017

  • [Fix] revert unintentional breaking change of missing substitutions being untouched

v2.1.2: November 18, 2016

  • [Fix] restore behavior of explicit null/undefined not touching the substitution

v2.1.1: November 13, 2016

  • [Fix] ensure that missing or null substitutions don’t crash (#79)

v2.1.0: November 11, 2016

  • [New] Merge choosePluralForm & interpolate into one exported function: transformPhrase (#75)
  • [New] Allow locales to have regions (#70)
  • [New] Support Arabic pluralization (#71)
  • [New] Added Lithuanian locale to russian group of pluralization (#68)
  • [Deps] update object.assign, warning
  • [Dev Deps] pin uglify-js because it does not follow semver
  • [Dev Deps] update eslint-config-airbnb-base, eslint, eslint-plugin-import, mocha, should, uglify-js
  • [Performance] instead of creating a new regex for each token, use the function form of replace (#19)
  • [Refactor] use warning package for warnings
  • [Robustness] Use has package to properly check “has own property”
  • [Robustness] use string.prototype.trim rather than a homegrown trim polyfill
  • [Tests] only test on latest node v7, v6, v5, v4, and iojs; improve test matrix
  • [Tests] add linting

v2.0.0: April 6, 2016

  • [Breaking] Remove non-node support + use object.assign/for-each.
  • [New] Add Slovak language to the Czech pluralization group
  • [Fix] fixwarn messages in node, where this is not the global object
  • [Tests] up to node v5.10, v4.4
  • [Tests] decaffeinate tests

v1.0.0: November 29, 2015

  • [Tests] up to node v5.1
  • [Tests] fix npm upgrades on older nodes
  • [Dev Deps] update uglify-js, docco, should, mocha, and fix test pollution

v0.4.5: November 29, 2015

  • [Fix] Ensure that dollar signs are properly escaped in substitutions (#43)
  • [Docs] use SPDX-compliant license string (#44)

v0.4.4: October 26, 2015

  • [New] Add unset method (#43)
  • [Tests] test on travis-ci

v0.4.3: June 26, 2015

  • Add .has(key) method (thanks @scarfacedeb).
  • Add UMD wrapper for AMD support (thanks @TomOne).

v0.4.2: March 13, 2015

  • Allow blank translations.

v0.4.1: July 14, 2014

  • Added support for warn option for custom error handler (thanks @terinjokes).
  • Added some more plural forms (thanks @jgill333).

v0.4.0: May 22, 2014

  • Added support for nested phrase objects to extend() and in the phrases option in the constructor.

v0.3.0: August 6, 2013

  • Breaking change: Removed pluralize() method; instead, just use the t() method, passing in a smart_count option.
  • Breaking change: Removed the ability to use Array, Backbone.Collection, etc. instances for the smart_count option; instead, must pass a Number.
  • Allow passing Number as second argument to t(), which gets converted to the options object {smart_count: <my number>}.

v0.2.1: May 2, 2013

  • Added allowMissing option to let the phrase key be the default translation (thanks @ziad-saab).

v0.2.0: Dec 20, 2012

  • Breaking change: Moved from Singleton pattern to class-based. Now you create an instance of the Polyglot class rather than using class methods directly on it. The reason is to allow maintaining multiple sets of phrases, which is something we ran into at Airbnb with a highly-concurrent Express app.
  • Breaking change: Removed the built-in Handlebars helpers, because Handlebars is a singleton, and it's messy to create a single helper function that can be bound to different Polyglot instances. Instead, it's super easy to create your own, based on your requirements.