eslint-plugin-formatjs
This eslint plugin allows you to enforce certain rules in your ICU message. This is currently under development
Usage
npm install eslint-plugin-formatjs
Then in your eslint config:
{
"plugins": ["formatjs"],
"rules": {
"formatjs/no-offset": "error"
}
}
Currently this uses intl.formatMessage
, defineMessages
, <FormattedMessage>
from react-intl
, or _
from @formatjs/macro
as hooks to verify the message. Therefore, in your code use 1 of the following mechanisms:
import {_} from '@formatjs/macro';
const message = _({
defaultMessage: 'foo',
description: 'bar',
});
import {defineMessages} from 'react-intl';
const messages = defineMessages({
foo: {
defaultMessage: 'foo',
description: 'bar',
},
});
import {FormattedMessage} from 'react-intl';
<FormattedMessage defaultMessage="foo" description="bar" />;
function foo() {
intl.formatMessage({
defaultMessage: 'foo',
});
}
Available Rules
blacklist-elements
This blacklists usage of specific elements in ICU message.
Why
- Certain translation vendors cannot handle things like
selectordinal
Available elements
enum Element {
literal = 'literal',
argument = 'argument',
number = 'number',
date = 'date',
time = 'time',
select = 'select',
selectordinal = 'selectordinal',
plural = 'plural',
}
Example
{
"plugins": ["formatjs"],
"rules": {
"formatjs/blacklist-elements": [2, ["selectordinal"]]
}
}
enforce-description
This enforces description
in the message descriptor.
Why
- Description provides helpful context for translators
import {defineMessages} from 'react-intl';
const messages = defineMessages({
foo: {
defaultMessage: 'foo',
description: 'bar',
},
bar: {
defaultMessage: 'bar',
},
});
enforce-default-message
This enforces defaultMessage
in the message descriptor.
Why
- Can be usefull in case we want to extract messages for translations from source code. This way can make sure people won't forget about defaultMessage
import {defineMessages} from 'react-intl';
const messages = defineMessages({
foo: {
defaultMessage: 'This is default message',
description: 'bar',
},
bar: {
description: 'bar',
},
});
enforce-placeholders
Makes sure all values are passed in if message has placeholders (number/date/time/plural/select/selectordinal). This requires values to be passed in as literal object (not a variable).
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={{placeholder: 'dog'}}
/>
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, {placeholder: 'dog'})
<FormattedMessage
defaultMessage="this is a {placeholder}"
/>
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
})
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={{foo: 1}}
/>
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, {foo: 1})
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={someVar}
/>
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, values)
enforce-plural-rules
Enforce certain plural rules to always be specified/forbidden in a message.
Why
- It is recommended to always specify
other
as fallback in the message. - Some translation vendors only accept certain rules.
Available rules
enum LDML {
zero = 'zero',
one = 'one',
two = 'two',
few = 'few',
many = 'many',
other = 'other',
}
Example
{
"plugins": ["formatjs"],
"rules": {
"formatjs/enforce-plural-rules": [
2,
{
"one": true,
"other": true,
"zero": false
}
]
}
}
no-camel-case
This make sure placeholders are not camel-case.
Why
- This is to prevent case-sensitivity issue in certain translation vendors.
import {defineMessages} from 'react-intl';
const messages = defineMessages({
foo: {
defaultMessage: 'foo {snake_case} {nothing}',
},
bar: {
defaultMessage: 'foo {camelCase}',
},
});
no-emoji
This prevents usage of emoji in message.
Why
- Certain translation vendors cannot handle emojis.
- Cross-platform encoding for emojis are faulty.
import {defineMessages} from 'react-intl';
const messages = defineMessages({
foo: {
defaultMessage: 'Smileys & People',
},
bar: {
defaultMessage: '😃 Smileys & People',
},
});
no-multiple-whitespaces
This prevents usage of multiple consecutive whitespaces in message.
Why
- Consecutive whitespaces are handled differently in different locales.
- Prevents
\
linebreaks in JS string which results in awkward whitespaces.
import {defineMessages} from 'react-intl';
const messages = defineMessages({
foo: {
defaultMessage: 'Smileys & People',
},
bar: {
defaultMessage: 'Smileys & People',
},
baz: {
defaultMessage: 'this message is too long \
so I wanna line break it.',
},
});
no-multiple-plurals
This prevents specifying multiple plurals in your message.
Why
- Nested plurals are hard to translate across languages so some translation vendors don't allow it.
import {defineMessages} from 'react-intl'
const messages = defineMessages({
foo: {
defaultMessage: '{p1, plural, one{one}}',
},
bar: {
defaultMessage: '{p1, plural, one{one}} {p2, plural, one{two}}',
}
bar2: {
defaultMessage: '{p1, plural, one{{p2, plural, one{two}}}}',
}
})
no-offset
This prevents specifying offset in plural rules in your message.
Why
- Offset has complicated logic implication so some translation vendors don't allow it.
import {defineMessages} from 'react-intl';
const messages = defineMessages({
foo: {
defaultMessage: '{var, plural, one{one} other{other}}',
},
bar: {
defaultMessage: '{var, plural, offset:1 one{one} other{other}}',
},
});
supported-datetime-skeleton
Since formatjs only supports a subset of DateTime Skeleton. This rule exists to make sure you don't use a unsupported pattern.