# Formatjs > id: basic-internationalization-principles --- # FormatJS Documentation # Source: https://raw.githubusercontent.com/formatjs/formatjs/main/website/docs/core-concepts/basic-internationalization-principles.md # Path: website/docs/core-concepts/basic-internationalization-principles.md --- id: basic-internationalization-principles title: Basic Internationalization Principles --- ## What Is Internationalization and Why Does It Matter? Internationalized software supports the languages and cultural customs of people throughout the world. The Web reaches all parts of the world. Internationalized web apps provide a great user experience for people everywhere. Localized software adapts to a specific language and culture by translating text into the user's language and formatting data in accordance with the user's expectations. An app is typically localized for a small set of locales. The [ECMA-402 JavaScript internationalization specification](https://github.com/tc39/ecma402) has an excellent overview. ## Locales: Language and Region A "locale" refers to the lingual and cultural expectations for a region. It is represented using a "locale code" defined in [UTS LDML](https://www.unicode.org/reports/tr35/tr35.html#Identifiers). This code is comprised of several parts separated by hyphens (-). The first part is a short string representing the language. The second, optional, part is a short string representing the region. Additionally, various extensions and variants can be specified. For guidance on choosing the right locale components, see the [W3C article on choosing language tags](https://www.w3.org/International/questions/qa-choosing-language-tags). Typically, web apps are localized to just the language or language-region combination. Examples of such locale codes are: - `en` for English - `en-US` for English as spoken in the United States - `en-GB` for English as spoken in the United Kingdom - `es-AR` for Spanish as spoken in Argentina - `ar-001` for Arabic as spoken throughout the world - `ar-AE` for Arabic as spoken in United Arab Emirates Most internationalized apps only support a small list of locales. ## Translating Strings You likely have some text in your application that is in a natural language such as English or Japanese. In order to support other locales, you will need to translate these strings. FormatJS provides a mechanism to let you write the core "software" of your application without special code for different translations. The considerations for each locale are encapsulated in your translated strings and our libraries. ```tsx const messages = { en: { GREETING: 'Hello {name}', }, fr: { GREETING: 'Bonjour {name}', }, } ``` We use the [ICU Message syntax](https://unicode-org.github.io/icu/userguide/format_parse/messages) which is also used in [Java](http://docs.oracle.com/javase/7/docs/api/java/text/MessageFormat.html), C, PHP and various other platforms. ## Bundling Translated Strings It is common to organize your translations primarily by locale, because you only need the translations for the user's current locale. Our template and component library integrations are designed to work with the translations for a single locale. If your app is complex, you can further subdivide your translations, such as by page or section of the site. ## Structure of Code The actual formatting and presentation of data and translated strings typically takes these steps: 1. Determine the user's locale, as described in Runtime Environments guide. 2. Setup one of FormatJS's integrations with the following data: - the user's current locale - translated strings for that locale - optionally, any custom formats 3. Call the template engine, passing the data that needs formatting. --- # FormatJS Documentation # Source: https://raw.githubusercontent.com/formatjs/formatjs/main/website/docs/getting-started/installation.md # Path: website/docs/getting-started/installation.md --- id: installation title: Installation --- formatjs is a set of libraries that help you setup internationalization in any project whether it's React or not. ## Installation import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem' ```sh npm i -S react react-intl ``` ```sh yarn add react react-intl ``` ## Minimal Application After following the step above, you should be able to get a minimal application like this running: ```tsx import {createIntl, createIntlCache} from '@formatjs/intl' // Translated messages in French with matching IDs to what you declared const messagesInFrench = { myMessage: "Aujourd'hui, nous sommes le {ts, date, ::yyyyMMdd}", } // This is optional but highly recommended // since it prevents memory leak const cache = createIntlCache() // Create the `intl` object const intl = createIntl( { // Locale of the application locale: 'fr', // Locale of the fallback defaultMessage defaultLocale: 'en', messages: messagesInFrench, }, cache ) // Aujourd'hui, nous sommes le 23/07/2020 console.log( intl.formatMessage( { // Matching ID as above id: 'myMessage', // Default Message in English defaultMessage: 'Today is {ts, date, ::yyyyMMdd}', }, {ts: Date.now()} ) ) // 19,00 € console.log(intl.formatNumber(19, {style: 'currency', currency: 'EUR'})) ``` ```tsx import * as React from 'react' import {IntlProvider, FormattedMessage, FormattedNumber} from 'react-intl' // Translated messages in French with matching IDs to what you declared const messagesInFrench = { myMessage: "Aujourd'hui, nous sommes le {ts, date, ::yyyyMMdd}", } export default function App() { return (


) } ``` Output ```html

Aujourd'hui, nous sommes le 23/07/2020
19,00 €

```
```tsx import VueIntl from 'vue-intl' import {createApp} from 'vue' const app = createApp(App) app.use(VueIntl, { locale: 'fr', defaultLocale: 'en', messages: { myMessage: "Aujourd'hui, nous sommes le {ts, date, ::yyyyMMdd}", }, }) ``` ```vue ``` Output ```html

Aujourd'hui, nous sommes le 23/07/2020
19,00 €

```
## Adding our babel-plugin/TypeScript Transformer for compilation Our tooling supports `babel`, `ts-loader`, `ts-jest`, `rollup-plugin-typescript2` & `ts-patch` for message compilation: ### Babel If you're using `babel`, add `babel-plugin-formatjs` to your dependencies: ```sh npm i -D babel-plugin-formatjs ``` ```sh yarn add -D babel-plugin-formatjs ``` and add it to your `babel.config.js` or `.babelrc`: ```json { "plugins": [ [ "formatjs", { "idInterpolationPattern": "[sha512:contenthash:base64:6]", "ast": true } ] ] } ``` ### `ts-loader` ```sh npm i -D @formatjs/ts-transformer ``` ```sh yarn add -D @formatjs/ts-transformer ``` ```tsx import {transform} from '@formatjs/ts-transformer' module.exports = { ...otherConfigs, module: { rules: [ { test: /\.tsx?$/, use: [ { loader: 'ts-loader', options: { getCustomTransformers() { return { before: [ transform({ overrideIdFn: '[sha512:contenthash:base64:6]', }), ], } }, }, }, ], }, ], }, } ``` ### `ts-jest` in `jest.config.js` ```sh npm i -D @formatjs/ts-transformer ``` ```sh yarn add -D @formatjs/ts-transformer ``` Take a look at [`ts-jest` guide](https://kulshekhar.github.io/ts-jest/docs/getting-started/options/astTransformers) on how to incorporate custom AST Transformers. ### `ts-patch` ```sh npm i -D @formatjs/ts-transformer ``` ```sh yarn add -D @formatjs/ts-transformer ``` ```json { "compilerOptions": { "plugins": [ { "transform": "@formatjs/ts-transformer", "import": "transform", "type": "config", "overrideIdFn": "[sha512:contenthash:base64:6]", "ast": true } ] } } ``` ### `rollup-plugin-typescript2` ```sh npm i -D @formatjs/ts-transformer ``` ```sh yarn add -D @formatjs/ts-transformer ``` ```ts // rollup.config.js import typescript from 'rollup-plugin-typescript2' import {transform} from '@formatjs/ts-transformer' export default { input: './main.ts', plugins: [ typescript({ transformers: () => ({ before: [ transform({ overrideIdFn: '[sha512:contenthash:base64:6]', ast: true, }), ], }), }), ], } ``` --- # FormatJS Documentation # Source: https://raw.githubusercontent.com/formatjs/formatjs/main/website/docs/getting-started/message-declaration.md # Path: website/docs/getting-started/message-declaration.md --- id: message-declaration title: Message Declaration --- While you can declare your messages using only `id`s, we highly recommend declaring `defaultMessage`s inline along with their usages because of the following reasons: 1. Messages colocated with their usages become self-managed, as their usages change/removed, so are the messages. 2. Messages are highly contextual. We've seen a lot of cases where developers assume a certain grammar when they write their messages. Buttons/Call-To-Actions and labels are also translated differently. 3. Text styling is also dependent on the message itself. Things like truncation, capitalization... certainly affect the messages themselves. 4. Better integrations with toolchains. Most toolchains cannot verify cross-file references to validate syntax/usage. At a high level, formatjs messages use [ICU Syntax](../core-concepts/icu-syntax.mdx) with a couple of enhancements common in other message format such as [Fluent](https://github.com/projectfluent/fluent.js/). This section focuses on the actual supported ways of calling `formatjs` APIs so messages can be extracted. ## Using imperative API `intl.formatMessage` ```tsx // Method must be exactly `intl.formatMessage` intl.formatMessage( { description: 'A message', // Description should be a string literal defaultMessage: 'My name is {name}', // Message should be a string literal }, { name: userName, } // Values should be an object literal, but not necessarily every value inside ) ``` ## Using React API `` ```tsx import {FormattedMessage} from 'react-intl' ; ``` ## Using Vue API & template methods such as `$formatMessage` ```vue ``` ## Pre-declaring using `defineMessage` for later consumption (less recommended) ```tsx import {defineMessage} from 'react-intl' const message = defineMessage({ description: 'A message', // Description should be a string literal defaultMessage: 'My name is {name}', // Message should be a string literal }) intl.formatMessage(message, {name: 'John'}) // My name is John // My name is John ``` :::caution We rely on AST to extract messages from the codebase, so make sure you call `intl.formatMessage()`, use our builtin React components, use our Vue methods or configure `--additionalFunctionNames`/`--additionalComponentNames` in our [CLI](../tooling/cli.md) properly. ::: :::caution You can declare a message without immediately formatting it with `defineMessage` and our extractor would still be able to extract it. However, our [enforce-placeholders](../tooling/linter.md#enforce-placeholders) linter rule won't be able to analyze it. ::: --- # FormatJS Documentation # Source: https://raw.githubusercontent.com/formatjs/formatjs/main/website/docs/getting-started/message-extraction.md # Path: website/docs/getting-started/message-extraction.md --- id: message-extraction title: Message Extraction --- Now that you've declared some messages, it's time to extract them. ## Installation import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem' ```sh npm i -D @formatjs/cli ``` ```sh yarn add -D @formatjs/cli ``` ## Extraction Add the following command to your `package.json` `scripts`: ```json { "scripts": { "extract": "formatjs extract" } } ``` and execute with `npm`: ```sh npm run extract -- 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' ``` ```sh yarn extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' ``` :::caution ID Interpolation Pattern Make sure this pattern matches `idInterpolationPattern` when you use `babel-plugin-formatjs` or `@formatjs/ts-transformer` in [Bundling with formatjs](https://formatjs.github.io/docs/guides/bundler-plugins) or you'll get a `MISSING_TRANSLATION` error. ::: Given a file that has the following messages: ```tsx import * as React from 'react' import {FormattedMessage, useIntl, injectIntl} from 'react-intl' class PasswordChangeWithIntl extends React.Component { render() { const {intl} = this.props return (
  • ) } } const PasswordChange = injectIntl(PasswordChangeWithIntl) export function List(props) { const intl = useIntl() return (
    ) } ```
    ```vue ```
    running the above command will create a file called `lang/en.json`: ```json { "hak27d": { "defaultMessage": "Control Panel", "description": "title of control panel section" }, "haqsd": { "defaultMessage": "Delete user {name}", "description": "delete button" }, "19hjs": { "defaultMessage": "New Password", "description": "placeholder text" }, "explicit-id": { "defaultMessage": "Confirm Password", "description": "placeholder text" } } ``` :::info Message ID During extraction, we'll preserve explicit declared IDs and insert a hash as an ID for messages without. We recommend against explicit IDs since it can cause collision. ::: ## Automatic ID Generation Since manual IDs are discouraged, we've provided a `babel` plugin and a `TypeScript` AST transformer that will automatically insert message IDs in your transpiled code. For more details please visit [Bundling with formatjs](https://formatjs.github.io/docs/guides/bundler-plugins). ## Translation Management System (TMS) Integration The default format generated from `@formatjs/cli` might not work with the specific TMS/vendor you're working with. You can specify a custom formatter with `--format ` that allows you to convert that format into something tailored to your TMS. For example: If your vendor accepts the format like ```json { "[id]": { "string": "[message]", "comment": "[description]" } } ``` you can run ```sh npm run extract -- "src/**/*.{ts,tsx,vue}" --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format formatter.js ``` ```sh yarn extract "src/**/*.{ts,tsx,vue}" --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format formatter.js ``` where `formatter.js` is: ```js export function format(msgs) { const results = {} for (const [id, msg] of Object.entries(msgs)) { results[id] = { string: msg.defaultMessage, comment: msg.description, } } return results } ``` We also provide several [builtin formatters](../tooling/cli.md#builtin-formatters) to integrate with 3rd party TMSes so feel free to create PRs to add more. | TMS | `--format` | | ------------------------------------------------------------------------------------------------------------- | ----------- | | [BabelEdit](https://www.codeandweb.com/babeledit/format-js) | `simple` | | [Crowdin Chrome JSON](https://support.crowdin.com/file-formats/chrome-json/) | `crowdin` | | [Lingohub](https://lingohub.com/developers/resource-files/json-localization/) | `simple` | | [Localize's Simple JSON](https://developers.localizejs.com/docs/simple-json-import-export) | `simple` | | [Localizely](https://localizely.com/flat-json-file/?tab=react-intl) | `simple` | | [locize](https://docs.locize.com/integration/supported-formats#json-nested) | `simple` | | [Lokalise Structured JSON](https://docs.lokalise.com/en/articles/3229161-structured-json) | `lokalise` | | [Phrase Strings](https://support.phrase.com/hc/en-us/articles/6111390065948--JSON-React-Intl-Simple-Strings-) | `simple` | | [POEditor Key-Value JSON](https://poeditor.com/localization/files/key-value-json) | `simple` | | [SimpleLocalize JSON](https://simplelocalize.io/docs/file-formats/simplelocalize-json/) | `simple` | | [Smartling ICU JSON](https://help.smartling.com/hc/en-us/articles/360008000733-JSON) | `smartling` | | [Transifex's Structured JSON](https://docs.transifex.com/formats/json/structured-json) | `transifex` | --- # FormatJS Documentation # Source: https://raw.githubusercontent.com/formatjs/formatjs/main/website/docs/guides/runtime-requirements.md # Path: website/docs/guides/runtime-requirements.md --- id: runtime-requirements title: Runtime Requirements --- ## Browser :::info Browser Support We support **IE11** & **2 most recent versions of Edge, Chrome, Firefox & Safari**. If you need older browser support, take a look at [polyfill-library](https://github.com/Financial-Times/polyfill-library) that also uses `formatjs` but pre-bundle other polyfills that we use. ::: React Intl relies on these `Intl` APIs: - [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat): Available on IE11+ - [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat): Available on IE11+ - [Intl.PluralRules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/PluralRules): This can be polyfilled using [this package](polyfills/intl-pluralrules.md). - (Optional) [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RelativeTimeFormat): Required if you use [`formatRelativeTime`](react-intl/api.md#formatrelativetime) or [`FormattedRelativeTime`](react-intl/components.md#formattedrelativetime). This can be polyfilled using [this package](polyfills/intl-relativetimeformat.md). - (Optional) [Intl.DisplayNames](https://tc39.es/proposal-intl-displaynames/): Required if you use [`formatDisplayName`](react-intl/api.md#formatdisplayname) or [`FormattedDisplayName`](react-intl/components.md#formatteddisplayname). This can be polyfilled using [this package](polyfills/intl-displaynames.md). We officially support IE11 along with 2 most recent versions of Edge, Chrome & Firefox. ## Node.js ### full-icu Starting with Node.js 13.0.0 full-icu is supported by default. If using React Intl in an earlier version of Node.js, your `node` binary has to either: - Get compiled with `full-icu` using these [instructions](https://nodejs.org/api/intl.html) **OR** - Uses [`full-icu` npm package](https://www.npmjs.com/package/full-icu) If your `node` version is missing any of the `Intl` APIs above, you'd have to polyfill them accordingly. ## React Native If you're using `react-intl` in React Native, make sure your runtime has built-in `Intl` support (similar to [JSC International variant](https://github.com/react-native-community/jsc-android-buildscripts#international-variant)). See these issues for more details: - https://github.com/formatjs/formatjs/issues/1356 - https://github.com/formatjs/formatjs/issues/992 ### React Native on iOS If you cannot use the Intl variant of JSC (e.g on iOS), follow the instructions in [polyfills](../polyfills.md) to polyfill the following APIs (in this order): 1. [Intl.getCanonicalLocales](../polyfills/intl-getcanonicallocales.md) 1. [Intl.Locale](../polyfills/intl-locale.md) 1. [Intl.PluralRules](../polyfills/intl-pluralrules.md) 1. [Intl.NumberFormat](../polyfills/intl-numberformat.md) 1. [Intl.DateTimeFormat](../polyfills/intl-datetimeformat.md) --- # FormatJS Documentation # Source: https://raw.githubusercontent.com/formatjs/formatjs/main/website/docs/intl-messageformat.md # Path: website/docs/intl-messageformat.md --- id: intl-messageformat title: Intl MessageFormat --- Formats ICU Message strings with number, date, plural, and select placeholders to create localized messages. [![npm Version](https://img.shields.io/npm/v/intl-messageformat.svg?style=flat-square)](https://www.npmjs.org/package/intl-messageformat) ![`intl-messageformat` minzipped size](https://badgen.net/badgesize/normal/https://unpkg.com/intl-messageformat/dist/umd/intl-messageformat.min.js?label=intl-messageformat+minzipped+size) ## 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](http://wiki.ecmascript.org/doku.php?id=globalization:messageformatting), but there are a few places this implementation diverges. :::info Future Changes This `IntlMessageFormat` API may change to stay in sync with ECMA-402, but this package will follow [semver](http://semver.org/). ::: ### How It Works Messages are provided into the constructor as a `String` message, or a [pre-parsed AST](./icu-messageformat-parser.md) object. ```tsx const msg = new IntlMessageFormat(message, locales, [formats], [opts]) ``` 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. ```tsx const 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.: ```tsx live new IntlMessageFormat( `{numPhotos, plural, =0 {You have no photos.} =1 {You have one photo.} other {You have # photos.} }`, 'en-US' ).format({numPhotos: 1000}) ``` ```tsx live new IntlMessageFormat( `{numPhotos, plural, =0 {Usted no tiene fotos.} =1 {Usted tiene una foto.} other {Usted tiene # fotos.} }`, 'es-ES' ).format({numPhotos: 1000}) ``` ### 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](https://unicode-org.github.io/icu/userguide/format_parse/messages)** and works for all [CLDR languages](http://cldr.unicode.org/) which have pluralization rules defined. ### Features - Uses industry standards: [ICU Message syntax](https://unicode-org.github.io/icu/userguide/format_parse/messages) and [CLDR locale data](http://cldr.unicode.org/). - Supports **plural**, **select**, and **selectordinal** message arguments. - Formats numbers and dates/times in messages using [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) and [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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 [@formatjs/intl-numberformat](./polyfills/intl-numberformat.md)) 2. `Intl.DateTimeFormat` for date time formatting (can be polyfilled using [@formatjs/intl-datetimeformat](./polyfills/intl-datetimeformat.md)) 3. `Intl.PluralRules` for plural/ordinal formatting (can be polyfilled using [@formatjs/intl-pluralrules](./polyfills/intl-pluralrules.md)) ### Loading Intl MessageFormat in a browser ```html ``` ### Loading Intl MessageFormat in Node.js Either do: ```tsx import IntlMessageFormat from 'intl-messageformat' ``` **NOTE: Your Node has to include [full ICU](https://nodejs.org/api/intl.html)** ## 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](#locale-resolution) for more details.** - **`formats?: object`** - Optional object with user defined options for format styles. - **`opts?: { formatters?: Formatters, ignoreTag?: boolean }`** - Optional options. - `formatters`: Map containing memoized formatters for performance. - `ignoreTag`: Whether to treat HTML/XML tags as string literal instead of parsing them as tag token. When this is `false` we only allow simple tags without any attributes ```tsx const 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: ```tsx live new IntlMessageFormat('', 'en-us').resolvedOptions().locale ``` 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`: ```tsx live new IntlMessageFormat('My name is {name}.', 'en-US').format({name: 'Eric'}) ``` :::danger placeholders A value **must** be supplied for every argument in the message pattern the instance was constructed with. ::: #### Rich Text support ```tsx live new IntlMessageFormat('hello world', 'en').format({ b: chunks => {chunks}, }) ``` We support embedded XML tag in the message, e.g `this is a strong tag`. This is not meant to be a full-fledged method to embed HTML, but rather to tag specific text chunk so translation can be more contextual. Therefore, the following restrictions apply: 1. Any attributes on the HTML tag are also ignored. 2. Self-closing tags are treated as string literal and not supported, please use regular ICU placeholder like `{placeholder}`. 3. All tags specified must have corresponding values and will throw error if it's missing, e.g: ```tsx live function () { try { return new IntlMessageFormat('a strong').format() } catch (e) { return String(e) } } ``` 4. XML/HTML tags are escaped using apostrophe just like other ICU constructs. In order to escape you can do things like: ```tsx live new IntlMessageFormat("I '<'3 cats").format() ``` ```tsx live new IntlMessageFormat("raw 'HTML'").format() ``` ```tsx live new IntlMessageFormat("raw 'HTML' with ''{placeholder}''").format( {placeholder: 'some word'} ) ``` 5. Embedded valid HTML tag is a bit of a grey area right now since we're not supporting the full HTML/XHTML/XML spec. ### `getAst` Method Return the underlying AST for the compiled message. ### Date/Time/Number Skeleton We support ICU Number skeleton and a subset of Date/Time Skeleton for further customization of formats. #### Number Skeleton Example: ```tsx live new IntlMessageFormat( 'The price is: {price, number, ::currency/EUR}', 'en-GB' ).format({price: 100}) ``` A full set of options and syntax can be found [here](https://unicode-org.github.io/icu/userguide/format_parse/numbers/skeletons.html) #### Date/Time Skeleton ICU provides a [wide array of pattern](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) to customize date time format. However, not all of them are available via ECMA402's Intl API. Therefore, our parser only support the following patterns | Symbol | Meaning | Notes | | ------ | ----------------------------- | ------------------------- | | G | Era designator | | y | year | | M | month in year | | L | stand-alone month in year | | d | day in month | | E | day of week | | e | local day of week | `e..eee` is not supported | | c | stand-alone local day of week | `c..ccc` is not supported | | a | AM/PM marker | | h | Hour [1-12] | | H | Hour [0-23] | | K | Hour [0-11] | | k | Hour [1-24] | | m | Minute | | s | Second | | z | Time Zone | Example: ```tsx live new IntlMessageFormat('Today is: {now, date, ::yyyyMMdd}', 'en-GB').format({ now: new Date(), }) ``` ## Advanced Usage ### Passing in AST You can pass in pre-parsed AST to IntlMessageFormat like this: ```tsx new IntlMessageFormat('hello').format() // prints out hello // is equivalent to import IntlMessageFormat from 'intl-messageformat' import {parse} from '@formatjs/icu-messageformat-parser' new IntlMessageFormat(parse('hello')).format() // prints out hello ``` This helps performance for cases like SSR or preload/precompilation-supported platforms since `AST` can be cached. If your messages are all in ASTs, you can alias `@formatjs/icu-messageformat-parser` to `{default: undefined}` to save some bytes during bundling. ### Formatters For complex messages, initializing `Intl.*` constructors can be expensive. Therefore, we allow user to pass in `formatters` to provide memoized instances of these `Intl` objects. This opts combines with [passing in AST](#passing-in-ast) and `fast-memoize` can speed things up by 30x per the benchmark down below. For example: ```ts import IntlMessageFormat from 'intl-messageformat' import {memoize} from '@formatjs/fast-memoize' const formatters = { getNumberFormat: memoize( (locale, opts) => new Intl.NumberFormat(locale, opts) ), getDateTimeFormat: memoize( (locale, opts) => new Intl.DateTimeFormat(locale, opts) ), getPluralRules: memoize((locale, opts) => new Intl.PluralRules(locale, opts)), } new IntlMessageFormat('hello {number, number}', 'en', undefined, { formatters, }).format({number: 3}) // prints out `hello, 3` ``` ## Benchmark ``` format_cached_complex_msg x 153,868 ops/sec ±1.13% (85 runs sampled) format_cached_string_msg x 21,661,621 ops/sec ±4.06% (84 runs sampled) new_complex_msg_preparsed x 719,056 ops/sec ±2.83% (78 runs sampled) new_complex_msg x 12,844 ops/sec ±1.97% (85 runs sampled) new_string_msg x 409,770 ops/sec ±2.57% (79 runs sampled) complex msg format x 12,065 ops/sec ±1.66% (81 runs sampled) complex msg w/ formatters format x 11,649 ops/sec ±2.05% (78 runs sampled) complex preparsed msg w/ formatters format x 597,153 ops/sec ±1.46% (90 runs sampled) complex preparsed msg w/ new formatters format x 684,263 ops/sec ±1.37% (89 runs sampled) ``` --- # FormatJS Documentation # Source: https://raw.githubusercontent.com/formatjs/formatjs/main/website/docs/intl.md # Path: website/docs/intl.md --- id: intl title: Core FormatJS Intl --- This library contains core intl API that is used by `react-intl`. ## Installation import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem' ```sh npm i -S @formatjs/intl ``` ```sh yarn add @formatjs/intl ``` ## The `intl` object The core of `@formatjs/intl` is the `intl` object (of type [`IntlShape`](#intlshape)), which is the instance to store a cache of all `Intl.*` APIs, configurations, compiled messages and such. The lifecycle of the `intl` object is typically tied to the `locale` & the list of `messages` that it contains, which means when you switch `locale`, this object should be recreated. :::tip The `intl` object should be reused as much as possible for performance. ::: ## createIntl This allows you to create an `IntlShape` object that contains all `format*` methods. For example: ```tsx import {createIntl, createIntlCache} from '@formatjs/intl' // This is optional but highly recommended // since it prevents memory leak const cache = createIntlCache() const intl = createIntl( { locale: 'fr-FR', messages: {}, }, cache ) // Call imperatively intl.formatNumber(20) ``` ## createIntlCache Creates a cache instance to be used globally across locales. This memoizes previously created `Intl.*` constructors for performance and is only an in-memory cache. ## IntlShape ```ts interface IntlConfig { locale: string timeZone?: string fallbackOnEmptyString?: boolean formats: CustomFormats messages: Record | Record defaultLocale: string defaultRichTextElements?: Record> defaultFormats: CustomFormats onError(err: string): void onWarn(warning: string): void } interface IntlFormatters { formatDate(value: number | Date | string, opts?: FormatDateOptions): string formatTime(value: number | Date | string, opts?: FormatDateOptions): string formatDateToParts( value: number | Date | string, opts?: FormatDateOptions ): Intl.DateTimeFormatPart[] formatTimeToParts( value: number | Date | string, opts?: FormatDateOptions ): Intl.DateTimeFormatPart[] formatRelativeTime( value: number, unit?: FormattableUnit, opts?: FormatRelativeTimeOptions ): string formatNumber(value: number, opts?: FormatNumberOptions): string formatNumberToParts( value: number, opts?: FormatNumberOptions ): Intl.NumberFormatPart[] formatPlural( value: number | string, opts?: FormatPluralOptions ): ReturnType formatMessage( descriptor: MessageDescriptor, values?: Record> ): string formatMessage( descriptor: MessageDescriptor, values?: Record> ): R formatList(values: Iterable, opts?: FormatListOptions): string formatList( values: Iterable, opts?: FormatListOptions ): T | string | Array formatListToParts( values: Iterable, opts?: FormatListOptions ): Part[] formatDisplayName( value: string, opts?: FormatDisplayNameOptions ): string | undefined } type IntlShape = IntlConfig & IntlFormatters ``` The definition above shows what the `intl` object will look like. It's made up of two parts: - **`IntlConfig`:** The intl metadata passed as props into the parent ``. - **`IntlFormatters`:** The imperative formatting API described below. ### locale, formats, and messages The user's current locale and what the app should be rendered in. While `defaultLocale` and `defaultFormats` are for fallbacks or during development and represent the app's default. Notice how there is no `defaultMessages`, that's because each [Message Descriptor](#message-descriptor) provides a `defaultMessage`. ### defaultLocale and defaultFormats Default locale & formats for when a message is not translated (missing from `messages`). `defaultLocale` should be the locale that `defaultMessage`s are declared in so that a sentence is coherent in a single locale. Without `defaultLocale` and/or if it's set incorrectly, you might run into scenario where a sentence is in English but embedded date/time is in Spanish. ### onError Allows the user to provide a custom error handler. By default, error messages are logged using `console.error` if `NODE_ENV` is not set to `production`. ### defaultRichTextElements A map of tag to rich text formatting function. This is meant to provide a centralized way to format common tags such as ``, `

    `... or enforcing certain Design System in the codebase (e.g standardized `` or ` // WORKS // WORKS // WORKS {intl.formatMessage({defaultMessage: // FAILS // FAILS // FAILS // FAILS // FAILS Image description // FAILS ``` This linter reports text literals or string expressions, including string concatenation expressions in the JSX children. It also checks certain JSX attributes that you can customize. #### Example ```js import formatjs from 'eslint-plugin-formatjs' export default [ { plugins: { formatjs, }, rules: { 'formatjs/no-literal-string-in-jsx': [ 2, { // Include or exclude additional prop checks (merged with the default checks) props: { include: [ // picomatch style glob pattern for tag name and prop name. // check `name` prop of `UI.Button` tag. ['UI.Button', 'name'], // check `message` of any component. ['*', 'message'], ], // Exclude will always override include. exclude: [ // do not check `message` of the `Foo` tag. ['Foo', 'message'], // do not check aria-label and aria-description of `Bar` tag. ['Bar', 'aria-{label,description}'], ], }, }, ], }, }, ] ``` The default prop checks are: ```js { include: [ // check aria attributes that the screen reader announces. ['*', 'aria-{label,description,details,errormessage}'], // check placeholder and title attribute of all native DOM elements. ['[a-z]*([a-z0-9])', '(placeholder|title)'], // check alt attribute of the img tag. ['img', 'alt'], ], exclude: [] } ``` ### `no-literal-string-in-object` This prevents untranslated strings in chosen object properties. #### Why - It is easy to forget wrapping literal strings in translation functions, when they are defined in an object field like `{label: "Untranslated label"}`. ```tsx const options = () => [ // FAILS {value: 'chocolate', label: 'Chocolate'}, // WORKS { value: 'strawberry', label: intl.formatMessage({defaultMessage: 'Strawberry'}), }, // WORKS, custom translation function { value: 'mint', label: customTranslateFn('Mint'), }, // FAILS, string concatenation { value: 'coconut', label: 'Coconut' + intl.formatMessage({defaultMessage: 'Ice Cream'}), }, // FAILS, template literal { value: 'mango', label: `Mango ${intl.formatMessage({defaultMessage: 'Ice Cream'})}`, }, // FAILS, conditional rendering { value: 'recommended', label: feelLikeSour ? intl.formatMessage({defaultMessage: 'Lime'}) : 'Vanilla', }, ] const MyComponent = () =>