Universal build plugin for FormatJS — supports Vite, Webpack, Rollup, esbuild, Rspack, and Rsbuild with one codebase.
Process string messages for translation from modules that use react-intl, specifically:
- Parse and verify that messages are ICU-compliant w/o any syntax issues.
- Remove
descriptionfrom message descriptor to save bytes since it isn't used at runtime. - Option to remove
defaultMessagefrom message descriptor to save bytes since it isn't used at runtime. - Automatically inject message ID based on specific pattern.
This plugin provides the same transformations as babel-plugin-formatjs and @formatjs/ts-transformer without requiring Babel or TypeScript custom transformers. It uses oxc-parser + magic-string for fast, compiler-agnostic builds.
Installation#
npm i -D @formatjs/unplugin
Requires unplugin 3.4.0 or newer, installed automatically as a dependency.
Usage#
The plugin processes message descriptors from: defineMessages(), defineMessage(), intl.formatMessage, $t, $formatMessage and <FormattedMessage>.
Calls match by function or method name, regardless of receiver. This includes
namespaced declarations and configured additionalFunctionNames. Parentheses
and TypeScript wrappers (as, satisfies, non-null assertions, and angle-bracket
assertions) are transparent around inline descriptors, defineMessages maps,
and individual entries, including nested combinations. Transformations preserve
these wrappers.
Vite#
// vite.config.ts
import {defineConfig} from 'vite'
import formatjs from '@formatjs/unplugin/vite'
export default defineConfig({
plugins: [
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
ast: true,
}),
],
})
Webpack#
// webpack.config.js
const formatjs = require('@formatjs/unplugin/webpack').default
module.exports = {
plugins: [
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
ast: true,
}),
],
}
Rollup#
// rollup.config.js
import formatjs from '@formatjs/unplugin/rollup'
export default {
plugins: [
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
ast: true,
}),
],
}
esbuild#
import esbuild from 'esbuild'
import formatjs from '@formatjs/unplugin/esbuild'
esbuild.build({
plugins: [
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
ast: true,
}),
],
})
Rspack#
// rspack.config.js
const formatjs = require('@formatjs/unplugin/rspack').default
module.exports = {
plugins: [
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
ast: true,
}),
],
}
Rsbuild#
// rsbuild.config.ts
import {defineConfig} from '@rsbuild/core'
import formatjs from '@formatjs/unplugin/rsbuild'
export default defineConfig({
plugins: [
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
ast: true,
}),
],
})
For example, given this component:
<FormattedMessage
description="A message"
defaultMessage="My name is {name}"
values={{
name: userName,
}}
/>
The plugin will produce:
React.createElement(FormattedMessage, {
id: '179jda',
defaultMessage: 'My name is {name}',
values: {
name: userName,
},
})
description
Our plugin also removes description because it's only for translator, not
end-user.
Migrating from babel-plugin-formatjs#
Replace your Babel plugin configuration with the appropriate bundler import from @formatjs/unplugin. The options are the same.
Static message values#
Message fields can reference local const strings, including aliases, templates,
string concatenations, and constant conditionals. OXC performs native scope analysis and static evaluation through
@formatjs/cli-lib. Constants must be initialized before the reference. Lexical scope
and shadowing are respected for JSX and message descriptors.
function Greeting() {
const greeting = 'Hello'
return <FormattedMessage defaultMessage={greeting} />
}
Imported values, mutable bindings, destructured bindings, and runtime expressions remain unresolved. Keep those message fields inline when they need generated IDs or precompiled ASTs.
Native expression evaluation supports macOS arm64, Linux x64/arm64 (glibc and musl), and Windows x64.
Options#
overrideIdFn#
A function with the signature (id: string, defaultMessage: string, description: string, filePath: string) => string which allows you to override the ID both in the extracted javascript and messages.
idInterpolationPattern#
If certain message descriptors don't have id, this pattern will be used to automatically generate IDs for them. Default to [sha512:contenthash:base64:6]. See nodejs crypto createHash for hash algorithms & nodejs buffer docs for digest encodings.
removeDefaultMessage#
Remove defaultMessage field in generated js after extraction.
additionalComponentNames#
Additional component names to extract messages from, e.g: ['FormattedFooBarMessage'].
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
additionalComponentNames: ['CustomButton'],
})
additionalFunctionNames#
Additional function names to extract messages from, e.g: ['$formatMessage'].
formatjs({
idInterpolationPattern: '[sha512:contenthash:base64:6]',
additionalFunctionNames: ['t', '$formatMessage'],
})
ast#
Pre-parse defaultMessage into AST for faster runtime perf. This flag doesn't do anything when removeDefaultMessage is true.
preserveWhitespace#
When set to true, message trimming is disabled.