Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

messageformat

Package Overview
Dependencies
Maintainers
2
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

messageformat - npm Package Compare versions

Comparing version 3.0.0-beta.2 to 4.0.0-0

lib/data-model.d.ts

355

lib/messageformat.js

@@ -1,284 +0,81 @@

'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var safeIdentifier = require('safe-identifier');
var Compiler = _interopDefault(require('./compiler'));
var Cardinals = require('make-plural/cardinals');
var PluralCategories = require('make-plural/pluralCategories');
var Plurals = require('make-plural/plurals');
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function normalize(locale) {
if (typeof locale !== 'string' || locale.length < 2) throw new RangeError(`Invalid language tag: ${locale}`); // The only locale for which anything but the primary subtag matters is
// Portuguese as spoken in Portugal.
if (locale.startsWith('pt-PT')) return 'pt-PT';
const m = locale.match(/.+?(?=[-_])/);
return m ? m[0] : locale;
}
function getPlural(locale) {
if (typeof locale === 'function') {
const lc = normalize(locale.name);
return {
isDefault: false,
id: safeIdentifier.identifier(lc),
lc,
locale: locale.name,
getPlural: locale,
cardinals: locale.cardinals || [],
ordinals: locale.ordinals || []
};
}
const lc = normalize(locale);
if (lc in Plurals) {
return {
isDefault: true,
id: safeIdentifier.identifier(lc),
lc,
locale,
getCardinal: Cardinals[lc],
getPlural: Plurals[lc],
cardinals: PluralCategories[lc].cardinal,
ordinals: PluralCategories[lc].ordinal
};
}
return null;
}
function getAllPlurals(firstLocale) {
const keys = Object.keys(Plurals).filter(key => key !== firstLocale);
keys.unshift(firstLocale);
return keys.map(getPlural);
}
function hasPlural(locale) {
const lc = normalize(locale);
return lc in Plurals;
}
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _MessageFormat_localeMatcher, _MessageFormat_locales, _MessageFormat_message, _MessageFormat_runtime;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageFormat = void 0;
const message_value_1 = require("./message-value");
const message_1 = require("./parser/message");
const pattern_1 = require("./pattern");
const runtime_1 = require("./runtime");
/**
* Create a new message formatter.
*
* If `runtime` is unset, a default minimal set is used, consisting of `plural`
* for selection and `datetime` & `number` formatters based on the `Intl`
* equivalents.
*
* @beta
*/
class MessageFormat {
/**
* Used by the constructor when no `locale` argument is given.
*
* @memberof MessageFormat
* @default 'en'
*/
/**
* Escape special characaters by surrounding the characters `{` and `}` in the
* input string with 'quotes'. This will allow those characters to not be
* considered as MessageFormat control characters.
*
* @memberof MessageFormat
* @param {string} str - The input string
* @param {boolean} [octothorpe=false] - Also escape `#`
* @returns {string} The escaped string
*/
static escape(str, octothorpe) {
const esc = octothorpe ? /[#{}]/g : /[{}]/g;
return String(str).replace(esc, "'$&'");
}
/**
* Returns a subset of `locales` consisting of those for which MessageFormat
* has built-in plural category support.
*
* @memberof MessageFormat
* @param {(string|string[])} locales
* @returns {string[]}
*/
static supportedLocalesOf(locales) {
const la = Array.isArray(locales) ? locales : [locales];
return la.filter(hasPlural);
}
/**
* @typedef {Object} MessageFormat~Options - The shape of the options object
* that may be used as the second argument of the constructor.
* @property {boolean} [biDiSupport=false] - Add Unicode control characters to
* all input parts to preserve the integrity of the output when mixing LTR
* and RTL text
* @property {string} [currency='USD'] - The currency to use when formatting
* `{V, number, currency}`
* @property {Object} [customFormatters] - Map of custom formatting functions
* to include. See the {@tutorial guide} for more details.
* @property {boolean} [requireAllArguments=false] - Require all message
* arguments to be set with a defined value
* @property {('string'|'values')} [returnType='string'] - Return type of
* compiled functions; either a concatenated string or an array (possibly
* hierarchical) of values
* @property {boolean} [strictNumberSign=false] - Allow `#` only directly
* within a plural or selectordinal case, rather than in any inner select
* case as well.
*/
/**
* Create a new MessageFormat compiler
*
* ```
* import MessageFormat from 'messageformat'
* ```
*
* @class MessageFormat
* @classdesc MessageFormat-to-JavaScript compiler
* @param {string|Array} [locale]
* Define the locale or locales supported by this MessageFormat instance. If
* given multiple valid locales, the first will be the default. If `locale`
* is empty, it will fall back to `MessageFormat.defaultLocale`.
*
* String `locale` values will be matched to plural categorisation functions
* provided by the Unicode CLDR. If defining your own instead, use named
* functions instead, optionally providing them with the properties:
* `cardinals: string[]`, `ordinals: string[]`, `module: string` (to import
* the formatter as a runtime dependency, rather than inlining its source).
*
* If `locale` has the special value `'*'`, it will match *all* available
* locales. This may be useful if you want your messages to be completely
* determined by your data, but may provide surprising results if your input
* message object includes any 2-3 character keys that are not locale
* identifiers.
* @param {MessageFormat~Options} [options] - Compiler options
*/
constructor(locale, options) {
this.options = Object.assign({
biDiSupport: false,
currency: 'USD',
customFormatters: {},
returnType: 'string',
strictNumberSign: false
}, options);
if (locale === '*') {
this.plurals = getAllPlurals(MessageFormat.defaultLocale);
} else if (Array.isArray(locale)) {
this.plurals = locale.map(getPlural).filter(Boolean);
} else if (locale) {
const pl = getPlural(locale);
if (pl) this.plurals = [pl];
constructor(source, locales, options) {
var _a, _b;
_MessageFormat_localeMatcher.set(this, void 0);
_MessageFormat_locales.set(this, void 0);
_MessageFormat_message.set(this, void 0);
_MessageFormat_runtime.set(this, void 0);
__classPrivateFieldSet(this, _MessageFormat_localeMatcher, (_a = options === null || options === void 0 ? void 0 : options.localeMatcher) !== null && _a !== void 0 ? _a : 'best fit', "f");
__classPrivateFieldSet(this, _MessageFormat_locales, Array.isArray(locales)
? locales.slice()
: locales
? [locales]
: [], "f");
__classPrivateFieldSet(this, _MessageFormat_message, typeof source === 'string' ? (0, message_1.parseMessage)(source) : source, "f");
const rt = (_b = options === null || options === void 0 ? void 0 : options.runtime) !== null && _b !== void 0 ? _b : runtime_1.defaultRuntime;
__classPrivateFieldSet(this, _MessageFormat_runtime, Object.freeze(Object.assign({}, rt)), "f");
}
if (!this.plurals || this.plurals.length === 0) {
const pl = getPlural(MessageFormat.defaultLocale);
this.plurals = [pl];
resolveMessage(msgParams, onError) {
if (onError && __classPrivateFieldGet(this, _MessageFormat_message, "f").errors)
for (const pErr of __classPrivateFieldGet(this, _MessageFormat_message, "f").errors) {
const error = new Error(`Parse error: ${pErr.type} at ${pErr.start}`);
onError(Object.assign(error, pErr), undefined);
}
const ctx = this.createContext(msgParams, onError);
return new message_value_1.ResolvedMessage(ctx, __classPrivateFieldGet(this, _MessageFormat_message, "f"));
}
}
/**
* @typedef {Object} MessageFormat~ResolvedOptions
* @property {boolean} biDiSupport - Whether Unicode control characters be
* added to all input parts to preserve the integrity of the output when
* mixing LTR and RTL text
* @property {object} customFormatters - Map of custom formatting functions
* @property {string} locale - The default locale
* @property {object[]} plurals - All of the supported plurals
* @property {boolean} strictNumberSign - Is `#` only allowed directly within
* a plural or selectordinal case
*/
/**
* Returns a new object with properties reflecting the default locale,
* plurals, and other options computed during initialization.
*
* @memberof MessageFormat
* @instance
* @returns {MessageFormat~ResolvedOptions}
*/
resolvedOptions() {
return _objectSpread2({}, this.options, {
locale: this.plurals[0].locale,
plurals: this.plurals
});
}
/**
* Compile a message into a function
*
* Given a string `message` with ICU MessageFormat declarations, the result is
* a function taking a single Object parameter representing each of the
* input's defined variables, using the first valid locale.
*
* @memberof MessageFormat
* @instance
* @param {string} message - The input message to be compiled, in ICU MessageFormat
* @returns {function} - The compiled function
*
* @example
* const mf = new MessageFormat('en')
* const msg = mf.compile('A {TYPE} example.')
*
* msg({ TYPE: 'simple' }) // 'A simple example.'
*/
compile(message) {
const compiler = new Compiler(this.options);
const fnBody = 'return ' + compiler.compile(message, this.plurals[0]);
const nfArgs = [];
const fnArgs = [];
for (const [key, fmt] of Object.entries(compiler.runtime)) {
nfArgs.push(key);
fnArgs.push(fmt);
resolvedOptions() {
return {
localeMatcher: __classPrivateFieldGet(this, _MessageFormat_localeMatcher, "f"),
locales: __classPrivateFieldGet(this, _MessageFormat_locales, "f").slice(),
message: __classPrivateFieldGet(this, _MessageFormat_message, "f"),
runtime: __classPrivateFieldGet(this, _MessageFormat_runtime, "f")
};
}
const fn = new Function(...nfArgs, fnBody);
return fn.apply(null, fnArgs);
}
createContext(scope = {}, onError = () => {
// Ignore errors by default
}) {
const { declarations } = __classPrivateFieldGet(this, _MessageFormat_message, "f");
const ctx = {
onError,
resolve: elem => (0, pattern_1.resolvePatternElement)(ctx, elem),
declarations,
localeMatcher: __classPrivateFieldGet(this, _MessageFormat_localeMatcher, "f"),
locales: __classPrivateFieldGet(this, _MessageFormat_locales, "f"),
runtime: __classPrivateFieldGet(this, _MessageFormat_runtime, "f"),
// If declarations exist, scope may be modified during formatting
scope: declarations.length > 0 ? Object.assign({}, scope) : scope
};
return ctx;
}
}
MessageFormat.defaultLocale = 'en';
module.exports = MessageFormat;
exports.MessageFormat = MessageFormat;
_MessageFormat_localeMatcher = new WeakMap(), _MessageFormat_locales = new WeakMap(), _MessageFormat_message = new WeakMap(), _MessageFormat_runtime = new WeakMap();
{
"name": "messageformat",
"version": "3.0.0-beta.2",
"description": "PluralFormat and SelectFormat Message and i18n Tool - A JavaScript Implemenation of the ICU standards.",
"version": "4.0.0-0",
"description": "Intl.MessageFormat / Unicode MessageFormat 2 parser, runtime and polyfill",
"keywords": [
"messageformat",
"unicode",
"polyfill",
"internationalization",
"i18n",
"pluralformat",
"icu",
"gettext",
"choiceformat",
"selectformat",
"messageformat",
"internationalization"
"localization",
"l10n"
],
"author": "Alex Sexton <alexsexton@gmail.com>",
"contributors": [
"Eemeli Aro <eemeli@gmail.com>"
],
"license": "MIT",
"homepage": "https://messageformat.github.io/messageformat/v3/",
"license": "Apache-2.0",
"homepage": "http://messageformat.github.io/messageformat/",
"repository": {
"type": "git",
"url": "https://github.com/messageformat/messageformat.git",
"directory": "packages/messageformat"
"directory": "packages/mf2-messageformat"
},
"main": "lib/messageformat.js",
"browser": "./messageformat.js",
"main": "lib/index.js",
"files": [
"compile-module.js",
"index.d.ts",
"lib/",
"messageformat.js",
"messageformat.js.map"
"lib/"
],
"babel": {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "6.5.0"
}
}
]
],
"plugins": [
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
},
"eslintConfig": {
"env": {
"commonjs": true,
"es6": true
},
"overrides": [
{
"files": [
"src/*.js"
],
"parser": "babel-eslint"
},
{
"files": [
"rollup.config.js"
],
"env": {
"node": true
},
"parserOptions": {
"sourceType": "module"
}
},
{
"files": [
"webpack.config.js"
],
"env": {
"node": true
}
}
]
},
"dependencies": {
"make-plural": "^6.2.1",
"messageformat-date-skeleton": "^0.1.1",
"messageformat-number-skeleton": "^0.2.1",
"messageformat-parser": "^4.1.3",
"messageformat-runtime": "^3.0.0-beta.1",
"safe-identifier": "^0.4.1"
},
"scripts": {
"build": "rollup -c"
},
"gitHead": "dd10833d8d39155b005ea2ba6ec7a87f4daf0cd2"
"build": "tsc --project tsconfig.build.json",
"extract-api": "api-extractor run --local --verbose"
}
}

@@ -1,76 +0,51 @@

<div class="main-title">
<img align="right" width="100" height="100" src="https://messageformat.github.io/messageformat/logo/messageformat.svg">
<a class="badge" href="http://travis-ci.org/messageformat/messageformat"><img src="https://secure.travis-ci.org/messageformat/messageformat.svg" alt="Build Status"></a>
<h1>messageformat</h1>
</div>
# A Polyfill for Intl.MessageFormat
The experience and subtlety of your program's text can be important. Messageformat is a mechanism for handling both **pluralization** and **gender** in your applications. It can also lead to much better translations, as it's designed to support [all the languages](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html) included in the [Unicode CLDR](http://cldr.unicode.org/).
This library provides a runtime for the [ECMA-402 Intl.MessageFormat proposal],
which is built on top of the developing [Unicode MessageFormat 2.0 specification], "MF2".
The ICU has an [official guide](http://userguide.icu-project.org/formatparse/messages) for the format. Messageformat supports and extends all parts of the [standard](http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html), with the exception of the deprecated ChoiceFormat.
[ecma-402 intl.messageformat proposal]: https://github.com/dminor/proposal-intl-messageformat/
[unicode messageformat 2.0 specification]: https://github.com/unicode-org/message-format-wg
There is a good slide-deck on [Plural and Gender in Translated Messages](https://docs.google.com/presentation/d/1ZyN8-0VXmod5hbHveq-M1AeQ61Ga3BmVuahZjbmbBxo/pub?start=false&loop=false&delayms=3000#slide=id.g1bc43a82_2_14) by Markus Scherer and Mark Davis. But, again, remember that many of these problems apply even if you're only outputting english.
> **NOTE**: This means that the v4 release of the `messageformat` package has
> an entirely different API compared to its earlier major releases,
> which were built on top of ICU MessageFormat, aka "MF1".
> For that,
> please see [`@messageformat/core`](https://www.npmjs.com/package/@messageformat/core) instead.
## What problems does it solve?
## Usage
Using messageformat, you can separate your code from your text formatting, while enabling much more humane expressions. In other words, you won't need to see this anymore in your output:
> There are 1 results.<br>
> There are 2 result(s).<br>
> Number of results: 3.
On a more fundamental level, messageformat and its associated tools can help you build an effective workflow for UI texts and translations, keeping message sources in human-friendly formats, compiling them into JavaScript during your build phase, and making them easy to use from your application code.
## What does it look like?
With this message:
```js
const msgSrc = `{GENDER, select,
male {He}
female {She}
other {They}
} found {RES, plural,
=0 {no results}
one {1 result}
other {# results}
}.`;
```sh
npm install --save-exact messageformat@next
```
You'll get these results:
```js
const MessageFormat = require('messageformat');
const mf = new MessageFormat('en');
const msg = mf.compile(msgSrc);
msg({ GENDER: 'male', RES: 1 }); // 'He found 1 result.'
msg({ GENDER: 'female', RES: 1 }); // 'She found 1 result.'
msg({ GENDER: 'male', RES: 0 }); // 'He found no results.'
msg({ RES: 2 }); // 'They found 2 results.'
import { MessageFormat } from 'messageformat';
Intl.MessageFormat = MessageFormat;
```
## Getting Started
In addition to supporting MF2 syntax,
compilers and formatting function runtimes are also provided for
ICU MessageFormat and Fluent messages:
```
npm install --save-dev messageformat@next
npm install --save messageformat-runtime
```
- [@messageformat/icu-messageformat-1](https://www.npmjs.com/package/@messageformat/icu-messageformat-1)
- [@messageformat/fluent](https://www.npmjs.com/package/@messageformat/fluent)
This includes the MessageFormat compiler and a runtime accessor class that provides a slightly nicer API for working with larger numbers of messages. Our [Format Guide] will help with the ICU MessageFormat Syntax, and the [Usage Guide] provides some options for integrating messageformat to be a part of your workflow around UI texts and translations.
## API
[format guide]: https://messageformat.github.io/messageformat/v3/page-guide
[usage guide]: https://messageformat.github.io/messageformat/v3/page-build
The API provided by this Intl.MessageFormat polyfill is current as of
[2022-07-13](https://github.com/tc39/proposal-intl-messageformat/blob/72eefa5/README.md).
The static `MessageFormat.parseResource()` method is not yet provided,
as the message resource syntax is still under development.
---
```js
const locale = 'en-US';
const msg = '{Today is {$today :datetime dateStyle=medium}}';
[Messageformat](https://messageformat.github.io/) is an OpenJS Foundation project, and we follow its [Code of Conduct](https://github.com/openjs-foundation/cross-project-council/blob/master/CODE_OF_CONDUCT.md).
const mf = new Intl.MessageFormat(msg, locale);
<a href="https://openjsf.org">
<img width=200 alt="OpenJS Foundation" src="https://messageformat.github.io/messageformat/logo/openjsf.svg" />
</a>
mf.resolveMessage({ today: new Date('2022-02-02') }).toString();
// 'Today is Feb 2, 2022'
```
Browser testing provided by:
<a href="https://www.browserstack.com/open-source">
<img width=200 alt="BrowserStack" src="https://messageformat.github.io/messageformat/logo/browserstack.svg" />
</a>
For more information on additional types and functions provided by this package,
see the [API documentation site](https://messageformat.github.io/messageformat/api/).

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc