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.
Installation#
npm i @formatjs/ts-transformer
Usage#
The default message descriptors for the app's default language will be processed from: defineMessages(), defineMessage(), intl.formatMessage and <FormattedMessage>; all of which are named exports of the React Intl package.
Via ts-loader#
module.exports = {
...otherConfigs,
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
getCustomTransformers() {
return {
before: [
transform({
overrideIdFn: '[sha512:contenthash:base64:6]',
}),
],
}
},
},
},
],
},
],
},
}
Via ts-jest in jest.config.js#
Caution
ts-jest@26.4.0 or later// jest.config.js
module.exports = {
// [...]
transform: {
'^.+\\.(ts|tsx)?$': [
'ts-jest',
{
astTransformers: {
before: [
{
path: '@formatjs/ts-transformer/ts-jest-integration',
options: {
// options
overrideIdFn: '[sha512:contenthash:base64:6]',
ast: true,
},
},
],
},
},
],
},
}
Via ts-patch#
{
"compilerOptions": {
"plugins": [
{
"transform": "@formatjs/ts-transformer",
"import": "transform",
"type": "config",
"overrideIdFn": "[sha512:contenthash:base64:6]",
"ast": true
}
]
}
}
Via rollup-plugin-typescript2#
// rollup.config.js
export default {
input: './main.ts',
plugins: [
typescript({
transformers: () => ({
before: [
transform({
overrideIdFn: '[sha512:contenthash:base64:6]',
ast: true,
}),
],
}),
}),
],
}
Options#
overrideIdFn#
A function with the signature (id: string, defaultMessage: string, description: string|object) => string which allows you to override the ID both in the extracted javascript and messages.
Alternatively, overrideIdFn can be a template string, which is used only if the message ID is empty.
removeDefaultMessage#
Remove defaultMessage field in generated js after extraction.
extractSourceLocation#
Whether the metadata about the location of the message in the source file should be extracted. If true, then file, start, and end fields will exist for each extracted message descriptors. Defaults to false.
additionalComponentNames#
Additional component names to extract messages from, e.g: ['FormattedFooBarMessage'].
This option is useful for creating wrapper components that have react-intl functionality built in. For example, if you have a custom button component that internally uses formatMessage:
// Your wrapper component - must accept same props as FormattedMessage
function CustomButton({id, defaultMessage, description, values, onClick}) {
const intl = useIntl()
return (
<button onClick={onClick}>
{intl.formatMessage({id, defaultMessage, description}, values)}
</button>
)
}
// Usage - this will be extracted when CustomButton is in additionalComponentNames
;<CustomButton
defaultMessage="Click me"
description="Button text"
onClick={handleClick}
/>
Configuration with ts-loader:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
getCustomTransformers() {
return {
before: [
transform({
overrideIdFn: '[sha512:contenthash:base64:6]',
additionalComponentNames: ['CustomButton'],
}),
],
}
},
},
},
],
},
],
},
}
NOTE: By default we check for the fact that FormattedMessage is imported from moduleSourceName to make sure variable alias works. This option does not do that so it's less safe - any component with the specified name will be processed regardless of where it's imported from.
For a complete guide on creating wrapper components with auto ID generation, see the Wrapper Components Guide.
additionalFunctionNames#
Additional function names to extract messages from, e.g: ['$formatMessage'].
This option is useful when you want to create custom function wrappers around formatMessage, or use shorter aliases. For example:
// Custom translation hook - wrapper must match formatMessage signature
function useTranslation() {
const intl = useIntl()
const t = (descriptor, values) => intl.formatMessage(descriptor, values)
return {t}
}
// Usage - this will be extracted when 't' is in additionalFunctionNames
function MyComponent() {
const {t} = useTranslation()
return (
<div>{t({defaultMessage: 'Hello world', description: 'Greeting'})}</div>
)
}
Configuration with ts-loader:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
getCustomTransformers() {
return {
before: [
transform({
overrideIdFn: '[sha512:contenthash:base64:6]',
additionalFunctionNames: ['t', '$formatMessage'],
}),
],
}
},
},
},
],
},
],
},
}
For a complete guide on creating function wrappers with auto ID generation, see the Wrapper Components Guide.
pragma#
parse specific additional custom pragma. This allows you to tag certain file with metadata such as project. For example with this file:
// @intl-meta project:my-custom-project
<FormattedMessage defaultMessage="foo" id="bar" />
and with option {pragma: "@intl-meta"}, we'll parse out // @intl-meta project:my-custom-project into {project: 'my-custom-project'} in the result file.
ast#
Pre-parse defaultMessage into AST for faster runtime perf. This flag doesn't do anything when removeDefaultMessage is true.
onMsgExtracted(filePath: string, msgs: MessageDescriptor[])#
Callback that gets triggered whenever a message is encountered.
onMetaExtracted(filePath: string, meta: Record<string, string>)#
Callback that gets triggered whenever a pragme meta is encountered.
preserveWhitespace#
Whether to preserve whitespace and newlines.
flatten#
Whether to hoist selectors & flatten sentences as much as possible. For example:
I have {count, plural, one{a dog} other{many dogs}}
becomes
{count, plural, one{I have a dog} other{I have many dogs}}
The goal is to provide as many full sentences as possible since fragmented sentences are not translator-friendly. This transformation is applied before ID generation when using overrideIdFn, ensuring the ID is based on the flattened message format.
Take a look at compile.ts for example in integration.