Give your JavaScript the ability to speak many languages.

Overview

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 buliding a global marketplace.

View the documentation on Github.

View the annotated source.

Polylglot 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

Related projects

  • i18n-extract: Manage localization with static analysis. (E.g. key usage extraction)
Comments
  • Getting TypeError: getPolyfill is not a function after update

    Getting TypeError: getPolyfill is not a function after update

    Hello!

    After the latest update, I'm seeing an error in my code. It looks like the error is actually coming from array.prototype.foreach but seeing as you recently changed this project to use that code, I figured this is the place to report it. I've added a screenshot of the error below:

    image

    opened by zakmatik 21
  • Remove string trim polyfill

    Remove string trim polyfill

    This library is actually quite large (21.6kb https://bundlephobia.com/[email protected]). This is due to the String.prototype.trim polyfill, and it's dependencies (mainly es-abstract).

    String.prototype.trim has been supported by all major browsers for nearly 10 years https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim so it's probably not worth including in the library.

    By removing it, it should save just over 15kb of minified JS (according to bundlephobia).

    Dependencies before: https://bundlephobia.com/[email protected],[email protected],[email protected],[email protected]

    Dependencies after: https://bundlephobia.com/[email protected],[email protected],[email protected]

    wontfix 
    opened by adamnoakes 12
  • Handle no window object with Webpack

    Handle no window object with Webpack

    In environments like Webpack there is no this to refer to the global object. But you do have global, which is the Node global object.

    I suggest supporting that to ensure logging works. An other fix would be to check if root actually exists when logging.

    At least now it breaks with Webpack, which is too bad :-/

    opened by christianalfoni 11
  • Modernize the code base

    Modernize the code base

    Hey!

    I'm seeing you're using quite an old JS style and using modules that you really wouldn't need to be using (has and warning), for example). Whats your stance on modernizing the codebase of polyglot and reducing its dependencies? :)

    question 
    opened by NeoLegends 10
  • Improve the warning messages

    Improve the warning messages

    I believe the warning implementation. I see two issues with how it's done now.

    function warn(message) {
      if (typeof console !== 'undefined' && typeof console.warn === 'function') {
        console.warn('WARNING: ' + message);
      }
    }
    
    1. Using console.warn isn't providing the stack trace and make debugging hard.
    2. We end up with the dev checks in the production build. That some wasted bytes.

    To solve those two issues, what do you think about?

    1. https://github.com/r3dm/warning (use console.error internally)
    2. https://github.com/oliviertassinari/babel-plugin-transform-dev-warning (make dead code removal possible)
    opened by oliviertassinari 10
  • Interpolation smart_count option problem

    Interpolation smart_count option problem

    Hello! Great library! I had a problem translating such a line with Russian localization:

    polyglot.extend({
      'ra.action.bulk_actions': '1 выбрано |||| %{smart_count} выбрано'
    });
    
    polyglot.t("ra.action.bulk_actions", {smart_count: 5}) ==> "1 выбрано"
    

    Is this a bug or a feature? If a feature, please indicate the place in the documentation where this behavior is described. Thanks.

    "name": "node-polyglot", "version": "2.4.0",

    opened by wmwart 9
  • remove pluralize, add its functionalities to t

    remove pluralize, add its functionalities to t

    t now accepts, as a second parameter, a number as a shortcut to {smart_count: theNumber}. The feature of pluralize that reads an object's length is removed, as passing the explicit form obj.length looks better.

    opened by chenglou 9
  • Incorrect interpolation when using number prefix for Arabic content

    Incorrect interpolation when using number prefix for Arabic content

    There seems to be an issue regarding interpolation with Arabic string when template value start with a number.

    var Polyglot = require("node-polyglot")
    var polyglot = new Polyglot({interpolation: { prefix: '${', suffix: '}' }});
    
    polyglot.extend({
      "hello_name": "الرمز ${code} غير صحيح"});
    
    // works fine: CASE-A
    polyglot.t("hello_name", {code: "De30Niro"});
    
    // number goes at the beginning: CASE-B
    polyglot.t("hello_name", {code: "30DeNiro"});
    

    Results: CASE-A: image

    CASE-B: image

    opened by rohitcelestial 8
  • Pluralization not working in fa locale

    Pluralization not working in fa locale

    In fa locale (persian or farsi) pluralization is not working properly. For example I have following text for my i18n:

    کاربر |||| کاربران
    

    but in both plural and singular form I get کاربر which is the singular form.

    opened by hamidfzm 8
  • Error thrown with undefined option

    Error thrown with undefined option

    Let's take the following code

    polyglot.t('snackbar_error', { message })
    

    and the following key:

    snackbar_error: 'Erreur : %{message}',
    

    It's gonna output Erreur : foo when message = foo But when message = undefined it's throwing here:

    TypeError: String.prototype.replace called on null or undefined

    I think that it would be better not to crash and to display a warning in the console so we save time digging into it.

    bug 
    opened by oliviertassinari 8
  • Added 'unset' prototype method

    Added 'unset' prototype method

    to: @spikebrehm cc: @ljharb

    This PR adds an "unset" method to the polyglot prototype. This function allows one to selectively "unset" certain keys on a polyglot instance.

    To be honest, this method is mainly useful (or at least is only useful to us right now) as a method to facilitate testing.

    opened by lelandrichardson 8
  • New Release + Updated Types

    New Release + Updated Types

    Hey there 👋

    I've created the types for https://github.com/airbnb/polyglot.js/pull/171

    Have a look here: https://github.com/DefinitelyTyped/DefinitelyTyped/compare/master...Xiphe:DefinitelyTyped:node-polyglot-custom-replace

    Let me know when you plan to release this, so that I can open the PR

    opened by Xiphe 1
  • Add main code for Serbian language

    Add main code for Serbian language

    Language tag, According to IETF Language tags, can be composed of primary language tag, script tag and region tag (among some other, rarely used tags).

    Main code for Serbian, by ISO 639-1, is sr.

    Serbian and Bosnian have two scripts, latin and cyrillic, so I added those also, as in ISO 15924.

    I also added, for both languages, region tag (sr-RS and bs-BA), as in ISO 3166-1 alpha 2 although I am not sure those are needed, but Croatian have that so why not ;)

    Serbian, Croatian, Bosnian and Montenegrin are basically same languages but, officially, they are different, so I separated them here.

    semver-minor: new stuff 
    opened by ceracera 1
  • Custom pluralRules not working

    Custom pluralRules not working

    Hi, is the documentation about pluralRules still accurate? I'm having some trouble defining my own.. it doesn't seem to work. Here's my definition, I copy pasted if from the Readme and modify it:

    const polyglot = new Polyglot({
      locale: 'foo',
      phrases: {
        messages: 'You have no new messages.||||You have %{smart_count} new message.||||You have %{smart_count} new messages.',
      },
      pluralRules: {
        pluralTypes: {
          noneLike: function (n) {
            if (n === 0) return 0;
            if (n === 1) return 1;
            return 2;
          },
        },
        pluralTypeToLanguages: {
          noneLike: ['foo']
        }
      }
    });
    

    It should print:

    You have no new messages.
    You have 1 new message.
    You have 30 new messages.
    

    But instead it's:

    You have 0 new message.
    You have no new messages.
    You have 30 new message.
    

    I wrote a CodePen: https://codepen.io/josemigallas/pen/poJxYNX

    opened by josemigallas 1
  • Missing docs for creating custom interpolation

    Missing docs for creating custom interpolation

    Hey,

    I've been diving into the code source of Polyglot lately and have actually found that we can create a custom interpolation syntax by passing an interpolation property to the Polyglot constructor.

    I noticed there are no documentation on how to create our own custom interpolation syntax on your docs and wanted to let you know about it.

    docs 
    opened by thomaslombart 3
human friendly i18n for javascript (node.js + browser)

BabelFish - human friendly i18n for JS Internationalisation with easy syntax for node.js and browser. Classic solutions use multiple phrases for plura

Nodeca 246 Nov 20, 2022
:orange_book: simple approach for javascript localization

ttag ⚠️ This project was previously named c-3po. Some of the talks, presentations, and documentation may reference it with both names. Modern javascri

ttag 307 Jan 2, 2023
lightweight jQuery plugin for providing internationalization to javascript from ‘.properties’ files

jQuery.i18n.properties About jQuery.i18n.properties is a lightweight jQuery plugin for providing internationalization to javascript from ‘.properties’

null 408 Dec 25, 2022
A JavaScript Internationalization Framework

FBT is an internationalization framework for JavaScript designed to be not just powerful and flexible, but also simple and intuitive. It helps with th

Facebook 3.8k Jan 8, 2023
🌍📖 A readable, automated, and optimized (5 kb) internationalization for JavaScript

Linguijs ?? ?? A readable, automated, and optimized (5 kb) internationalization for JavaScript Documentation · Documentation 2.x · Quickstart · Exampl

Lingui 3.5k Jan 2, 2023
ICU MessageFormat for Javascript - i18n Plural and Gender Capable Messages

messageformat The experience and subtlety of your program's text can be important. Messageformat is a mechanism for handling both pluralization and ge

null 1.6k Dec 23, 2022
ICU MessageFormat for Javascript - i18n Plural and Gender Capable Messages

messageformat The experience and subtlety of your program's text can be important. Messageformat is a mechanism for handling both pluralization and ge

null 1.6k Dec 23, 2022
Gettext Style i18n for Modern JavaScript Apps

Jed Gettext Style i18n for Modern JavaScript Apps For more info

null 879 Dec 14, 2022
⚡️The Fullstack React Framework — built on Next.js

The Fullstack React Framework "Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails Read the Documentation “Zero-API” data layer lets y

⚡️Blitz 12.5k Jan 4, 2023
Hide your true voice from people and speak like a robot.

Hide your true voice from people and speak like a robot. Turn your speech into text and then turn the text into speech with tts engines.

null 3 Jun 3, 2022
Colour me! is a simple Discord bot running on Cloudflare Workers that allows server admins to give users the ability to change the colour of their own assigned colour roles!

Colour me! Colour me! is a simple Discord bot that allows server admins to give users the ability to change the colour of their own assigned colour ro

Erisa A 15 Dec 24, 2022
Use better-sqlite3 to give obsidian the ability to manipulate sqlite3 databases

OBSIDIAN-SQLITE3 Use better-sqlite3 to give obisidian the ability to manipulate sqlite3 databases. Intention Currently the linkage between obsidian an

cloud 12 Nov 28, 2022
Generator of interlinear glossing for many mark-up languages. Conlanging.

Gloss My Gloss A generator for interlinear glosses This is an input/output generator for aligning linguistic interlinear gloss, for the purposes of co

Neonnaut 6 Nov 16, 2022
Vite plugin to client bundle i18next locales composited from one to many json/yaml files from one to many libraries. Zero config HMR support included.

vite-plugin-i18next-loader yarn add -D vite-plugin-i18next-loader Vite plugin to client bundle i18next locales composited from one to many json/yaml f

AlienFast 4 Nov 30, 2022
LiveTabs is a Javascript library that allows you to create and manage tabs on the fly. This library gives the ability to your application to act like browser tabs, making dynamic tabs.

LiveTabs Table of content Description Goals Technologies Setup Description LiveTabs is a Javascript library that allows you to create and manage tabs

Hossein Khalili 3 May 3, 2022
This is a boilerplate for creating your own languages for various use cases. You can even create your own programming language from scratch!

Bootstrap compiler This is a bootstrap compiler that can be used to compile to compiler written in the target language. You can write a compiler in th

Kaan 3 Nov 14, 2022
DolphinDB JavaScript API is a JavaScript library that encapsulates the ability to operate the DolphinDB database, such as: connecting to the database, executing scripts, calling functions, uploading variables, etc.

DolphinDB JavaScript API English | 中文 Overview DolphinDB JavaScript API is a JavaScript library that encapsulates the ability to operate the DolphinDB

DolphinDB 6 Dec 12, 2022
Obsidian jTab adds the ability to show guitar chords and tabs directly in your notes.

Obsidian jTab Guide Obsidian jTab adds the ability to show guitar chords and tabs directly in your notes. It uses the jTab library to render the chord

David 24 Dec 25, 2022
Simple Cropper is a jQuery plugin which gives you ability to add cropping functionality to your web application

Simple Cropper is a jQuery plugin which gives you ability to add cropping functionality to your web application. It uses html5 canvas to create cropped images and css3, so it only works on latest browsers.

null 1 Feb 15, 2022