eslint-plugin-formatjs
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Ce plugin ESLint vous permet d'appliquer des règles spécifiques dans vos messages ICU.
Utilisation
- npm
- yarn
npm i -D eslint-plugin-formatjs
yarn add -D eslint-plugin-formatjs
Puis dans votre configuration ESLint :
import formatjs from 'eslint-plugin-formatjs'
export default [
// other configs...
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-offset': 'error',
},
},
]
React
Actuellement, il utilise intl.formatMessage, defineMessage, defineMessages, et <FormattedMessage> de react-intl comme points d'entrée pour vérifier les messages. Par conséquent, dans votre code, utilisez l'un des mécanismes suivants :
import {defineMessages, defineMessage} from 'react-intl'
const messages = defineMessages({
foo: {
defaultMessage: 'foo',
description: 'bar',
},
})
defineMessage({
defaultMessage: 'single message',
})
import {FormattedMessage} from 'react-intl'
;<FormattedMessage defaultMessage="foo" description="bar" />
function foo() {
intl.formatMessage({
defaultMessage: 'foo',
})
}
Vue
Cette fonctionnalité vérifie les appels de fonctions intl.formatMessage et $formatMessage dans vos fichiers JS/TS et vos fichiers SFC .vue. Par exemple :
<template>
<p>
{{
$formatMessage({
defaultMessage: 'today is {now, date}',
})
}}
</p>
</template>
Paramètres partagés
Ces paramètres s'appliquent globalement à toutes les règles formatjs une fois définis. Voir Paramètres partagés pour plus de détails sur leur configuration.
formatjs.additionalFunctionNames
Similaire à babel-plugin-formatjs et @formatjs/ts-transformer, ce paramètre permet de spécifier des noms de fonctions supplémentaires à vérifier au-delà de formatMessage et $formatMessage.
formatjs.additionalComponentNames
Similaire à babel-plugin-formatjs et @formatjs/ts-transformer, ce paramètre permet de spécifier des noms de composants supplémentaires à vérifier au-delà de FormattedMessage.
Configurations partageables
Le plugin fournit les deux configurations partageables suivantes :
-
recommended -
strict
En les utilisant, vous pouvez simplifier votre configuration tout en conservant un ensemble de règles conforme à vos standards de qualité.
Exemple
import formatjs from 'eslint-plugin-formatjs'
export default [
formatjs.configs.recommended,
// Other configs...
]
Règles disponibles
blocklist-elements
Interdit l'utilisation d'éléments spécifiques dans les messages ICU.
Pourquoi
- Certains fournisseurs de traduction ne gèrent pas des éléments comme
selectordinal
Éléments disponibles
enum Element {
// literal text, like `defaultMessage: 'some text'`
literal = 'literal',
// placeholder, like `defaultMessage: '{placeholder} var'`
argument = 'argument',
// number, like `defaultMessage: '{placeholder, number} var'`
number = 'number',
// date, like `defaultMessage: '{placeholder, date} var'`
date = 'date',
// time, like `defaultMessage: '{placeholder, time} var'`
time = 'time',
// select, like `defaultMessage: '{var, select, foo{one} bar{two}} var'`
select = 'select',
// selectordinal, like `defaultMessage: '{var, selectordinal, one{one} other{two}} var'`
selectordinal = 'selectordinal',
// plural, like `defaultMessage: '{var, plural, one{one} other{two}} var'`
plural = 'plural',
}
Exemple
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/blocklist-elements': [2, ['selectordinal']],
},
},
]
enforce-description
Impose la présence d'une description dans le descripteur de message.
Pourquoi
- La description fournit un contexte utile aux traducteurs
import {defineMessages} from 'react-intl'
const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'foo',
description: 'bar',
},
// FAILS
bar: {
defaultMessage: 'bar',
},
})
Options
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-description': ['error', 'literal'],
},
},
]
L'option literal force la description à être un littéral de chaîne plutôt qu'un appel de fonction ou une variable. Ceci est utile pour les outils d'extraction qui nécessitent une description littérale.
enforce-default-message
Impose la présence d'un defaultMessage dans le descripteur de message.
Pourquoi
- Utile pour extraire les messages du code source en vue de leur traduction. Garantit que les développeurs n'oublient pas le defaultMessage.
import {defineMessages} from 'react-intl'
const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'This is default message',
description: 'bar',
},
// FAILS
bar: {
description: 'bar',
},
})
Options
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-default-message': ['error', 'literal'],
},
},
]
L'option literal force le defaultMessage à être un littéral de chaîne plutôt qu'un appel de fonction ou une variable. Ceci est utile pour les outils d'extraction qui nécessitent un defaultMessage littéral.
enforce-placeholders
Vérifie que toutes les valeurs sont fournies si un message contient des espaces réservés (number/date/time/plural/select/selectordinal). Nécessite que les valeurs soient passées sous forme d'objet littéral (non variable).
// WORKS, no error
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={{placeholder: 'dog'}}
/>
// WORKS, no error
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, {placeholder: 'dog'})
// WORKS, error bc no values were provided
<FormattedMessage
defaultMessage="this is a {placeholder}"
/>
// WORKS, error bc no values were provided
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
})
// WORKS, error bc `placeholder` is not passed in
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={{foo: 1}}
/>
// WORKS, error bc `placeholder` is not passed in
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, {foo: 1})
// DOESN'T WORK
<FormattedMessage
defaultMessage="this is a {placeholder}"
values={someVar}
/>
// DOESN'T WORK
intl.formatMessage({
defaultMessage: 'this is a {placeholder}'
}, values)
Options
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-placeholders': [
'error',
{
ignoreList: ['foo'],
},
],
},
},
]
ignoreList: Liste des noms d'espaces réservés à ignorer. Fonctionne avecdefaultRichTextElementsdereact-intlpour éviter les faux positifs dans le formatage des balises globales ambiantes.
enforce-plural-rules
Impose que certaines règles de pluriel soient toujours spécifiées/interdites dans un message.
Pourquoi
-
Il est recommandé de toujours spécifier
othercomme solution de repli dans le message. -
Certains fournisseurs de traduction n'acceptent que certaines règles.
Règles disponibles
enum LDML {
zero = 'zero',
one = 'one',
two = 'two',
few = 'few',
many = 'many',
other = 'other',
}
Exemple
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-plural-rules': [
2,
{
one: true,
other: true,
zero: false,
},
],
},
},
]
no-camel-case
Cette règle garantit que les espaces réservés ne sont pas en camelCase.
Pourquoi
- Pour éviter les problèmes de sensibilité à la casse chez certains fournisseurs de traduction.
import {defineMessages} from 'react-intl'
const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'foo {snake_case} {nothing}',
},
// FAILS
bar: {
defaultMessage: 'foo {camelCase}',
},
})
no-missing-icu-plural-one-placeholders
Les messages de type {thing, plural, one {1 thing} other {# things}} devront être modifiés en {thing, plural, one {# thing} other {# things}}
Pourquoi
- one est une catégorie pour les nombres se comportant comme 1. Dans certaines langues comme l'ukrainien, le russe et le polonais, one s'applique aux nombres terminant par 1 (comme 1, 21, 151) mais excluant ceux finissant par 11 (comme 11, 111, 10311). Plus d'informations
no-emoji
Cette règle interdit l'utilisation d'émojis (ou au-delà d'une version Unicode spécifique) dans les messages
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-emoji': ['error'],
},
},
// OR
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-emoji': ['error', {versionAbove: '12.0'}],
},
},
]
Pourquoi
-
Certains fournisseurs de traduction ne gèrent pas les émojis.
-
L'encodage multiplateforme des émojis présente des failles.
import {defineMessages} from 'react-intl'
const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'Smileys & People',
},
// WORKS with option {versionAbove: '12.0'}
foo_bar: {
defaultMessage: '😃 Smileys & People',
},
// FAILS
bar: {
defaultMessage: '😃 Smileys & People',
},
// FAILS with option {versionAbove: '12.0'}
bar_foo: {
defaultMessage: '🥹 Smileys & People',
},
})
no-literal-string-in-jsx
Cette règle empêche les chaînes non traduites dans le JSX.
Pourquoi
-
Il est facile d'oublier d'encapsuler le texte JSX dans des fonctions ou composants de traduction.
-
Il est facile d'oublier d'encapsuler certains attributs d'accessibilité (ex:
aria-label) dans des fonctions de traduction.
// WORKS
<Button>
<FormattedMessage defaultMessage="Submit" />
</Button>
// WORKS
<Button>
{customTranslateFn("Submit")}
</Button>
// WORKS
<input aria-label={intl.formatMessage({defaultMessage: "Label"})} />
// WORKS
<img
src="/example.png"
alt={intl.formatMessage({defaultMessage: "Image description"})}
/>
// FAILS
<Button>Submit</Button>
// FAILS
<Button>{'Submit'}</Button>
// FAILS
<Button>{`Te` + 's' + t}</Button>
// FAILS
<input aria-label="Untranslated label" />
// FAILS
<img src="/example.png" alt="Image description" />
// FAILS
<input aria-label={`Untranslated label`} />
Ce linter signale les littéraux de texte ou expressions de chaînes, y compris les expressions de concaténation dans les enfants JSX. Il vérifie également certains attributs JSX que vous pouvez personnaliser.
Exemple
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}'],
],
},
},
],
},
},
]
Les vérifications de props par défaut sont :
{
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
Cette règle empêche les chaînes non traduites dans les propriétés d'objet sélectionnées.
Pourquoi
- Il est facile d'oublier d'encapsuler les chaînes littérales dans des fonctions de traduction lorsqu'elles sont définies dans un champ d'objet comme
{label: "Untranslated label"}.
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 = () => <Select options={options()} />
Ce linter signale les littéraux de texte ou expressions de chaînes, y compris les expressions de concaténation dans les propriétés d'objet que vous pouvez personnaliser.
Exemple
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-literal-string-in-object': [
'warn',
{
// The object properties to check for untranslated literal strings, default: ['label']
include: ['label'],
},
],
},
},
]
no-multiple-whitespaces
Cette règle interdit l'utilisation de multiples espaces blancs consécutifs dans les messages.
Pourquoi
-
Les espaces blancs consécutifs sont gérés différemment selon les locales.
-
Empêche les sauts de ligne
\dans les chaînes JS qui entraînent des espaces blancs inappropriés.
import {defineMessages} from 'react-intl'
const messages = defineMessages({
// WORKS
foo: {
defaultMessage: 'Smileys & People',
},
// FAILS
bar: {
defaultMessage: 'Smileys & People',
},
// FAILS
baz: {
defaultMessage:
'this message is too long \
so I wanna line break it.',
},
})
no-multiple-plurals
Cette règle interdit la spécification de pluriels multiples dans votre message.
Pourquoi
- Les pluriels imbriqués sont difficiles à traduire entre les langues, donc certains fournisseurs de traduction ne les autorisent pas.
import {defineMessages} from 'react-intl'
const messages = defineMessages({
// WORKS
foo: {
defaultMessage: '{p1, plural, one{one}}',
},
// FAILS
bar: {
defaultMessage: '{p1, plural, one{one}} {p2, plural, one{two}}',
}
// ALSO FAILS
bar2: {
defaultMessage: '{p1, plural, one{{p2, plural, one{two}}}}',
}
})
no-offset
Cette règle interdit la spécification de décalage dans les règles de pluriel de votre message.
Pourquoi
- Le décalage implique une logique complexe que certains fournisseurs de traduction n'autorisent pas.
import {defineMessages} from 'react-intl'
const messages = defineMessages({
// PASS
foo: {
defaultMessage: '{var, plural, one{one} other{other}}',
},
// FAILS
bar: {
defaultMessage: '{var, plural, offset:1 one{one} other{other}}',
},
})
enforce-id
Cette règle impose la définition d'un ID généré dans le MessageDescriptor.
Pourquoi
Les pipelines peuvent imposer la génération automatique/manuelle d'ID au niveau du linter (autofix pour insérer un ID autogénéré), garantissant ainsi cette fonctionnalité.
import {defineMessages} from 'react-intl';
const messages = defineMessages({
// PASS
foo: {
id: '19shaf'
defaultMessage: '{var, plural, one{one} other{other}}',
},
// FAILS
bar: {
id: 'something',
defaultMessage: '{var, plural, offset:1 one{one} other{other}}',
},
// FAILS
bar: {
defaultMessage: '{var, plural, offset:1 one{one} other{other}}',
},
});
Options
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/enforce-id': [
'error',
{
idInterpolationPattern: '[sha512:contenthash:base64:6]',
},
],
},
},
]
-
idInterpolationPattern: Modèle à vérifier pour l'ID -
idWhitelist: Tableau de chaînes contenant des expressions régulières. Ce tableau permet d'autoriser des ID personnalisés pour les messages. Par exemple '\\.' autorise tout ID contenant un point ;'^payment_.*'autorise tout ID personnalisé ayant le préfixepayment_. Notez que toute barre oblique inversée \ fournie via une chaîne doit être échappée avec une barre oblique inversée supplémentaire.
no-invalid-icu
Cette règle interdit les chaînes syntaxiquement invalides dans defaultMessage.
Pourquoi
Il est facile de passer à côté de chaînes qui semblent correctes au développeur mais qui sont syntaxiquement invalides au format ICU. Par exemple, l'exemple suivant génèrerait une erreur eslint :
formatMessage(
{
defaultMessage: '{count, plural one {#} other {# more}}', //Missing a comma!
},
{
count: 1,
}
)
no-id
Cette règle interdit les ID explicites dans MessageDescriptor.
Pourquoi
Nous préconisons généralement la génération automatique d'ID pour ces raisons. Cette règle garantit qu'aucun ID explicite n'est défini.
no-complex-selectors
Vérifie qu'une phrase n'est pas trop complexe. La complexité est déterminée par le nombre de chaînes produites lorsqu'on tente d'aplatir la phrase en fonction de ses sélecteurs. Par exemple :
I have {count, plural, one{a dog} other{many dogs}}
présente une complexité de 2 car l'aplatissement du sélecteur pluriel génère 2 phrases : I have a dog et I have many dogs.
La limite de complexité par défaut est 20 (en référence à Smartling)
Options
import formatjs from 'eslint-plugin-formatjs'
export default [
{
plugins: {
formatjs,
},
rules: {
'formatjs/no-complex-selectors': [
'error',
{
limit: 3,
},
],
},
},
]
no-useless-message
Cette règle interdit les messages ne nécessitant pas de traduction.
Pourquoi
Les messages comme {test} ne sont pas exploitables par les traducteurs. Le code devrait référencer directement test.
prefer-formatted-message
Privilégiez <FormattedMessage> plutôt que l'impératif intl.formatMessage(...) lorsque applicable.
// Bad
<p>
{intl.formatMessage({defaultMessage: 'hello'})}
</p>
// Good
<p>
<FormattedMessage defaultMessage="hello" />
</p>
Pourquoi
Style de codage cohérent en JSX et réduction de l'encombrement syntaxique.
prefer-pound-in-plural
Utilisez # dans l'argument pluriel pour référencer le compteur plutôt que de répéter l'argument.
// Bad
I have {count} {
count, plural,
one {apple}
other {apples}
}
}
// Good
I have {
count, plural,
one {# apple}
other {# apples}
}
}
// Bad
I have {
count, plural,
one {{count} apple}
other {{count} apples}
}
}
// Good
I have {
count, plural,
one {# apple}
other {# apples}
}
}
// Bad
I won the {ranking}{
count, selectordinal,
one {st}
two {nd}
few {rd}
other {th}
} place.
// Good
I won the {ranking}{
count, selectordinal,
one {#st}
two {#nd}
few {#rd}
other {#th}
} place.
Pourquoi
-
Message plus concis.
-
Garantit que les valeurs numériques sont correctement formatées.