Git Product home page Git Product logo

intl-messageformat's Introduction

This repo was migrated to the monorepo

Intl MessageFormat

Formats ICU Message strings with number, date, plural, and select placeholders to create localized messages.

npm Version Build Status Dependency Status

gzip size

Sauce Test Status

Overview

Goals

This package aims to provide a way for you to manage and format your JavaScript app's string messages into localized strings for people using your app. You can use this package in the browser and on the server via Node.js.

This implementation is based on the Strawman proposal, but there are a few places this implementation diverges.

Note: This IntlMessageFormat API may change to stay in sync with ECMA-402, but this package will follow semver.

How It Works

Messages are provided into the constructor as a String message, or a pre-parsed AST object.

var msg = new IntlMessageFormat(message, locales, [formats]);

The string message is parsed, then stored internally in a compiled form that is optimized for the format() method to produce the formatted string for displaying to the user.

var output = msg.format(values);

Common Usage Example

A very common example is formatting messages that have numbers with plural labels. With this package you can make sure that the string is properly formatted for a person's locale, e.g.:

var MESSAGES = {
    'en-US': {
        NUM_PHOTOS: 'You have {numPhotos, plural, ' +
            '=0 {no photos.}' +
            '=1 {one photo.}' +
            'other {# photos.}}'
    },

    'es-MX': {
        NUM_PHOTOS: 'Usted {numPhotos, plural, ' +
            '=0 {no tiene fotos.}' +
            '=1 {tiene una foto.}' +
            'other {tiene # fotos.}}'
    }
};

var output;

var enNumPhotos = new IntlMessageFormat(MESSAGES['en-US'].NUM_PHOTOS, 'en-US');
output = enNumPhotos.format({numPhotos: 1000});
console.log(output); // => "You have 1,000 photos."

var esNumPhotos = new IntlMessageFormat(MESSAGES['es-MX'].NUM_PHOTOS, 'es-MX');
output = esNumPhotos.format({numPhotos: 1000});
console.log(output); // => "Usted tiene 1,000 fotos."

Message Syntax

The message syntax that this package uses is not proprietary, in fact it's a common standard message syntax that works across programming languages and one that professional translators are familiar with. This package uses the ICU Message syntax and works for all CLDR languages which have pluralization rules defined.

Features

  • Uses industry standards: ICU Message syntax and CLDR locale data.

  • Supports plural, select, and selectordinal message arguments.

  • Formats numbers and dates/times in messages using Intl.NumberFormat and Intl.DateTimeFormat, respectively.

  • Optimized for repeated calls to an IntlMessageFormat instance's format() method.

  • Supports defining custom format styles/options.

  • Supports escape sequences for message syntax chars, e.g.: "\\{foo\\}" will output: "{foo}" in the formatted output instead of interpreting it as a foo argument.

Usage

Modern Intl Dependency

This package assumes that the Intl global object exists in the runtime. Intl is present in all modern browsers (IE11+) and Node (with full ICU). The Intl methods we rely on are:

  1. Intl.NumberFormat for number formatting (can be polyfilled using Intl.js)
  2. Intl.DateTimeFormat for date time formatting (can be polyfilled using Intl.js)
  3. Intl.PluralRules for plural/ordinal formatting (can be polyfilled using intl-pluralrules)

Loading Intl MessageFormat in a browser

<script src="intl-messageformat/intl-messageformat.min.js"></script>

Loading Intl MessageFormat in Node.js

Simply require() this package:

var IntlMessageFormat = require('intl-messageformat');

NOTE: Your Node has to include full ICU

Public API

IntlMessageFormat Constructor

To create a message to format, use the IntlMessageFormat constructor. The constructor takes three parameters:

  • message - {String | AST} - String message (or pre-parsed AST) that serves as formatting pattern.

  • locales - {String | String[]} - A string with a BCP 47 language tag, or an array of such strings. If you do not provide a locale, the default locale will be used. When an array of locales is provided, each item and its ancestor locales are checked and the first one with registered locale data is returned. See: Locale Resolution for more details.

  • [formats] - {Object} - Optional object with user defined options for format styles.

var msg = new IntlMessageFormat('My name is {name}.', 'en-US');

Locale Resolution

IntlMessageFormat uses Intl.NumberFormat.supportedLocalesOf() to determine which locale data to use based on the locales value passed to the constructor. The result of this resolution process can be determined by call the resolvedOptions() prototype method.

resolvedOptions() Method

This method returns an object with the options values that were resolved during instance creation. It currently only contains a locale property; here's an example:

var msg = new IntlMessageFormat('', 'en-us');
console.log(msg.resolvedOptions().locale); // => "en-US"

Notice how the specified locale was the all lower-case value: "en-us", but it was resolved and normalized to: "en-US".

format(values) Method

Once the message is created, formatting the message is done by calling the format() method on the instance and passing a collection of values:

var output = msg.format({name: "Eric"});
console.log(output); // => "My name is Eric."

Note: A value must be supplied for every argument in the message pattern the instance was constructed with.

User Defined Formats

Define custom format styles is useful you need supply a set of options to the underlying formatter; e.g., outputting a number in USD:

var msg = new IntlMessageFormat('The price is: {price, number, USD}', 'en-US', {
    number: {
        USD: {
            style   : 'currency',
            currency: 'USD'
        }
    }
});

var output = msg.format({price: 100});
console.log(output); // => "The price is: $100.00"

In this example, we're defining a USD number format style which is passed to the underlying Intl.NumberFormat instance as its options.

Examples

Plural Label

This example shows how to use the ICU Message syntax to define a message that has a plural label; e.g., "You have 10 photos":

You have {numPhotos, plural,
    =0 {no photos.}
    =1 {one photo.}
    other {# photos.}
}
var MESSAGES = {
    photos: '...', // String from code block above.
    ...
};

var msg = new IntlMessageFormat(MESSAGES.photos, 'en-US');

console.log(msg.format({numPhotos: 0}));    // => "You have no photos."
console.log(msg.format({numPhotos: 1}));    // => "You have one photo."
console.log(msg.format({numPhotos: 1000})); // => "You have 1,000 photos."

Note: how when numPhotos was 1000, the number is formatted with the correct thousands separator.

License

This software is free to use under the Yahoo! Inc. BSD license. See the LICENSE file for license text and copyright information.

intl-messageformat's People

Contributors

apipkin avatar apykhtin avatar aredridel avatar atlanteh avatar caridy avatar charlespwd avatar cwtuan avatar drewfish avatar ericf avatar fhemberger avatar jasonmit avatar juandopazo avatar kevintcoughlin avatar langpavel avatar linhuiw avatar longlho avatar okuryu avatar pawsong avatar rajington avatar worldmaker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

intl-messageformat's Issues

Add Release notes

This project has had some very major changes for 1.0, they all need to be reflected in the Release notes.

Document behavior when locales parameter is an array

Hi,

This is quite confusing to me and it does not seem to be well explained in the documentation, as all the exemples only always have a single locale.

I had to check the sources to understand.

What I understand is that it take the first locale in the array for which the language match a registered "CLDR data" (I guess it means fr.js / en.js)

So basically it seems that if you are sure you have appropriately loaded the "CLDR data" for a given locale, you do not have to care about the array.

I think it should be clearer because it is quite confusing. It would be far less confusing if the documentation was using "locale" wording instead of "locales", with a note on using an array instead of a single locale string.

See formatjs/formatjs#81

Leading whitespace ignored

Example:

Booked {numAttendees}{numMaxAttendees, plural,
      =0 {}
      other { / #}
   }

Results in

Booked 2/ 3

When it should have been

Booked 2 / 3

Is this correct behavior? If it is, that would suck.

bower warnings

Hello,

on install, bower complains :

bower intl-messageformat#*  not-cached git://github.com/yahoo/intl-messageformat.git#*
bower intl-messageformat#*     resolve git://github.com/yahoo/intl-messageformat.git#*
bower intl-messageformat#*    download https://github.com/yahoo/intl-messageformat/archive/v1.1.0.tar.gz
bower intl-messageformat#*     extract archive.tar.gz
bower intl-messageformat#*     invalid-meta intl-messageformat is missing "main" entry in bower.json
bower intl-messageformat#*     invalid-meta intl-messageformat is missing "ignore" entry in bower.json
bower intl-messageformat#*         resolved git://github.com/yahoo/intl-messageformat.git#1.1.0

Determine how the default locale is set

There's a context difference between the server and client, where the server might have all locales loaded, but the client will only have one.

We should align with the Intl spec on this, and also make sure the Intl.js polyfill works similar.

Update Test cases using other Languages

here is another tests in other languages:

lang: en-US
text: {NAME} went to {CITY}.
values: NAME=> Tony, CITY=> Paris, gender=>male
expected output: Tony went to Paris.

lang: fr-FR
text: {NAME} est {GENDER, select, female {allée} other {allé}} à {CITY}.

1st test
values: NAME=> Tony, CITY=> Paris, gender=>male
expected output: Tony est allé à Paris.

2nd test
values: NAME=> Jenny, CITY=> Paris, gender=>female
expected output: Jenny est allée à Paris.

support for nested/hierarchal properties in replacement variables

There are times would it be handy to resolve a nested/hierarchal property or key path.

new IntlMessageFormat('{a.key.path}').format({a:{key:{path:'value'}}});

But we get an error that periods are not allowed. However, dashes are allowed

> new IntlMessageFormat('{parent-child}').format({"parent-child":'child'});
'child'

I recognize this would lead to potential ambiguities, but they could be handled via policy or configuration if this is generally desirable feature.
We are looking at replacing some our existing i18n functions that do support this, but this would require us to do some special pre-processing to flatten our data structures for replacement.

I'm just curious if this is feasible to allow this. I can probably do the PR to allow it, if it's decided it's a good idea and the rules for resolving the paths are clear. Obviously there are potential issues.

var data = {
  "parent": {
    "child": "I am a parent.child value"
  },
  "parent.child": "I am also a parent.child value, depending on implementation",
  "parent-child": "This is allowed today, as dashes are supported, but periods are not"
}

Just curious what the thoughts are about this.

shasum check failed for /var/folders/sc/cxnrqnjn251727h2qgx7wj300000gp/T/npm-42513-c6749b0c/registry.npmjs.org/intl-messageformat/-/intl-messageformat-1.1.0.tgz

running npm install with ember-intl 1.3.2 in my package.json gave me this error

npm ERR! shasum check failed for /var/folders/sc/cxnrqnjn251727h2qgx7wj300000gp/T/npm-42513-c6749b0c/registry.npmjs.org/intl-messageformat/-/intl-messageformat-1.1.0.tgz
npm ERR! Expected: f46a168c1bd5e1838ecbddda809a0754727f4422
npm ERR! Actual:   14a066d1b22025c6b600b47a09b6f7de930d9fa0
npm ERR! From:     https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-1.1.0.tgz
npm config get registry
https://registry.npmjs.org/

A test uses `'1'` as a plural group.

The "complex object formatter with offset" test has a plural match for '1' (tests/index.js line 151). However, the spec says that (a) the format is '=1' and that (b) that use is discouraged.

No bower install for intl-messageformat

Going through the documentation for the Intl quickstart (https://github.com/yahoo/js-intl-docs/blob/master/quickstart-browser.md )
It says to install via bower:

bower install handlebars-helper-intl which is supposed to pull in handlebars-helper-intl, intl-messageformat, and handlebars. However it only pulls in handlebars-helper-intl, and I think this is in part due to there not being a bower install for intl-messageformat

When I run this manually:

bower install intl-messageformat

I get:

bower ENOTFOUND Package intl-messageformat not found

Can't use "console.log(error.stack)" on SyntaxError

The SyntaxError does not have the stack property, but this property is often used in log statements inside libraries that we don't always have control on, or that we did develop assuming normal error properties.

It would be nice to support these attributes to make it easier to deal with react intl errors without having to rewrite our log statements.

Remove Mojito CLA

I think it's better to not have a CONTRIBUTING.md file, than to have one that talks about the Mojito CLA, which is very confusing. There will soon be a Yahoo CLA, and until that time I think it's better to remove this.

Internet Explorer 8 and Browserify

Simply using require('intl-messageformat'); in a bundle that is browserified results in JavaScript errors in IE8. The errors occur as a result of the keyword default being used as a property for the modules that use esnext.

I'm probably going to workaround this by including IntlMessageFormat on window through the bower package, but I haven't quite figured this out with intl-format-cache.

I'm curious if this is a known issue or if there is some way to bundle this package with browserify and have it work in IE8.

Using escaped \ causes syntax error

I am unable to use this string ' i am \ back slash' on a message. Getting this error

SyntaxError{message: 'Expected "\\#", "\\u", "\\{", "\\}", "{", [^{}\\\0-\x1F \t\n\r], end of input or whitespace but "\\" found.', expected: [Object{type: ..., value: ..., description: ...}, Object{type: ..., value: ..., description: ...}, Object{type: ..., value: ..., description: ...}, Object{type: ..., value: ..., description: ...}, Object{type: ..., value: ..., description: ...}, Object{type: ..., value: ..., description: ...}, Object{type: ..., description: ...}, Object{type: ..., description: ...}], found: '\', offset: 77, line: 1, column: 78, name: 'SyntaxError'}

My use case is to render a message which has back slash in it. How can I do this ?

How about ordinals?

It would be great if intl-messageformat supported ordinal numbers:

1st, 2nd, 3rd, 4th, 10th, 11th, 12th, 13th, 52nd, 53rd, etc...

Here is a really simple version of the logic:

    function ordinal_suffix(num) {
        var s = ["th", "st", "nd", "rd"],
            v = num % 100;
        return num + (s[(v - 20) % 10] || s[v] || s[0]);
    }

I am unsure if ordinal works differently in other languages besides english

Move lib/message.js to ./index.js?

[[TRANSFER: yahoo/intl-messageformat-polyfill

@ericf "What's the reason for having a lib/message.js file instead of simply an index.js at the repo's root?"

@caridy "agreed, if that's the only source file in this repo, it is better to have it as index.js."

@drewfish "+1"

@apipkin "I was under the impression that all "do something" files in npm packages are stored under lib/. Is it correct to say this is only the case if there is more than one file? I'm happy to move it, just want to store the reasoning in my "logic bank" for later use."

refactor cldr data collector

Locales

This pkg only consume root locales. This means we don't care (maybe we should jejejeje), about fr-FR, or en-BR, we only care about en and fr as the top level locales. The routine to analyze all locales in CLDR and pick up the root locales is here:
https://github.com/yahoo/intl-messageformat/blob/master/tasks/build-data.js#L7-L16

Plural rules extraction

This pkg uses cldr.extractPluralRuleFunction() to collect "a function" that contains the pluralization and gender rules:
https://github.com/yahoo/intl-messageformat/blob/master/tasks/build-data.js#L98

This is where things gets funky, because we will like to collect data instead and creating the logic around the data rather than a function that we don't know how it works. The content of that function is something along these lines:

// require('cldr').extractPluralRuleFunction('en') returns:
function anonymous(n) {
  var i=Math.floor(Math.abs(n)),
        v=n.toString().replace(/^[^.]*\\.?/,"").length;
  if (typeof n==="string") n=parseInt(n,10);
  if (i===1&&v===0) return"one";
  return"other"
}

Format array of values to sentence

(this might not be the right place to post this, feel free to close & point me elsewhere)

I've a list of values which should be displayed as a sentence, eg: ['one', 'two', 'three'] should output one, two and three.

I'm not sure if this is possible with the ICU message format, or whether it's a higher level problem out of scope of this project?

I don't see an example of this kind of format in the ICU docs or elsewhere, but it seems like it would be a fairly common problem.

provide api to augment the formatters global structure

today, formatters can be defined only at the instance level, but for there should be a way to define it at the app level as well, which is equivalent to augmenting the MessageFormat class. I will propose a method similar to __addLocaleData() for that, probably __addFormatter().

question for message format

just to confirm if you have something like this: My Name is {name}.

will that work ? or should be ${name} ?

React, ES6 Classes, Webpack

From what I can tell this stack isn't currently supported in the React-Intl integrations as they leverage mixins. So I'm considering using this library directly. I would like to precompile the message format strings into the AST ahead of time which is why I was hoping for a webpack loader, though it appears this doesn't exist yet, should I build a webpack loader? Or am I just missing something?

Thanks!

Why MIT instead of Yahoo BSD?

[[TRANSFER: yahoo/intl-messageformat-polyfill]]

@ericf "I'm curious why we needed to make this MIT licensed instead of the Yahoo BSD?"

@drewfish "I chose that because I thought that was the standard. I'm fine with either and happy to relicense."

Infinity support ?

I'd like to use message format with Infinity. Something like that:

message = 'You have {storageAmount, plural, one {a gigabyte of} other {# gigabytes of} infinity {an unlimited}} data storage available'

It seems possible according to the spec:
Choosing Plural Category Names
8 - If there are more categories needed for the language, describe what those categories need to cover in the bug report.

How to convert AST object back into string pattern?

If you have an AST object representing an ICU message pattern, how can you convert that AST object to its string representation as a pattern?

I don't mean formatting the string. What I'd like to know is how to produce the pattern 'The price is: {price, number, USD}' from an AST object rather than the formatted message 'The price is: $1.00'.

One test is syntactically incorrect

There is no issue with the code, but the test "should format a message with ru-RU locale" is syntactically incorrect in case of "2 companies".

Translation messages manager tool

Dear community,

I'm trying to find some web service like localeapp.com, webtranslateit.com, lingohub.com, transifex.com, oneskyapp.com, crowdin.net, phraseapp.com or whatever which supports export to ICU/CLDR format. For sure some of you should use something like this, but on the ones I mentioned above I wasn't able to find such kind of export. How you collaborate on translations?

P.S. Sorry for an offtopic, I know it's not related to library itself, but I don't know where to ask that kind of questions - I wasn't able to find an IRC or Slack channel, so that's why I'm asking it there.

Is there something like xgettext to extract original phrases from a source code?

With gettext (ngettext) it is very easy to do I18N.

  1. I just run xgettext, it collects all the phrases in a source code and saves them to PO file
  2. I send the PO file to a translater who uses "poedit" to add translations.
  3. I convert translated PO to JSON and use JSON in my app.
  4. If any of the original English phrases were changed, I just run "xgettext" once more. It updates existing PO file marking changed phrases and adding new ones. Then I repeat from step "2".

intl-messageformat is a good alternative to gettext and ngettext. But how to extract English phrases from source code? Do you have something similar to xgettext?

different formats rendering on server and browser

Using:

<FormattedDate
  day="numeric"
  month="short"
  value={new Date().setTime(bc.date)}
  year="numeric"
/>

I am getting a warning in react saying the server is rendering the formatted date differently than what is rendered on the client. I am running the server and client on the same machine (currently). How can I ensure the date is rendered the same?

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) 35.0.0.0:$1933.0.1">Feb 28, 2016</span><
 (server) 35.0.0.0:$1933.0.1">28 Feb. 2016</span><

Working with non-stringy data

Forgive my ignorance... I'm applying this library with React and I really don't see a way to have non-stringy content in a message, but perhaps I've overlooked something (other than splitting a message into multiple pieces, which isn't very manageable/flexible).

Consider perhaps being able to do something like, using $$ to keep the example simple (<b></b> is an XML-like primitive exposed by JSX):

$$('Hello {countryName}!', {
  countryName: <b>{$$('countryForLanguage')}</b>
});
=> ['Hello ', <b>USA</b>, '!']

Or perhaps something more flexible:

$$('Hello {countryFunc, call, {USA}}!', {
  countryFunc: name => <b>{name}</b>
});
=> ['Hello ', <b>USA</b>, '!']
$$('Hello {countryFunc, call, {USA}, {UK}}!', {
  countryFunc: (name1, name2) => <b>{name1} & {name2}</b>
});
=> ['Hello ', <b>USA & UK</b>, '!']

How should we handle custom formats

Custom formats are a way to provide stored format options to be easily used within a message format object.

Given the message string: {amount, currency, toEuros}

The custom format would be: toEuros: { style: "currency", currency: "EUR" }

However, the way PR #47 is set up, formats would need to be prefixed with the type.

This means, the format would need to be number_toEuros or we could have them nested as {number: { toEuros: { style: 'currency', currency: 'EUR' } } }

I am more in favor of nested formats under the type. If there are a number of formats, prefixing with "number_", "date_" or "time_" could become disorganized.

Problems with Safari / iOS

Hello there

I'm building a application using React + Flux + Browserify and on Chrome/Firefox works with no problems, but when I was trying to run it on Safari I got this error and my application did not render.

ReferenceError: Can't find variable: Intl

To fix it I had to install the intl module from npm and require in my code manually.

After fix that problem another one started to be thrown on console:

ReferenceError: No locale data has been provided for this object yet.

I'm stuck on this one, if some one know something about it I'll appreciate

es5.js.map does not exist, but is reported within the source code

the package as downloaded from npm references a non-existent source map file. This causes an error in the chrome console whenever the browser developer tools are opened while the intl-messageformat library is included on the page.

$ npm install intl-messageformat
$ find . -iname "es5.js*"
./node_modules/intl-messageformat/lib/es5.js
./node_modules/intl-messageformat/src/es5.js
$  grep -F es5.js.map -R .
./node_modules/intl-messageformat/lib/es5.js://# sourceMappingURL=es5.js.map

I also noticed the same issue affects intl-format-cache and intl-relativeformat modules, presumably because they all use a similar build process.

I tried cloning this project and building it myself using Grunt and found that an es5.js.map file is generated at one point but is left in a temp directory and not included when the package for npm is created. I don't know if the solution is to include the map file in the npm package, or to remove the line beginning with //#sourceMappingURL=

$ pwd
~/src/intl-messageformat/ 
$ grunt
[. . .]
$ grep -F es5.js.map -R .
./lib/es5.js://# sourceMappingURL=es5.js.map
./tmp/src/es5.js://# sourceMappingURL=es5.js.map
$ find . -iname es5.js.map
./tmp/src/es5.js.map

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.